ruby-wmctrl 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +36 -8
- data/Rakefile +45 -0
- data/bin/rwmctrl +95 -51
- data/ext/wmctrl.c +85 -16
- data/lib/wmctrl.rb +1 -1
- data/lib/wmctrl/version.rb +1 -1
- data/lib/wmctrl/wmctrl.rb +131 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/wmctrl_spec.rb +46 -0
- metadata +33 -14
data/README.md
CHANGED
@@ -7,8 +7,10 @@ Ruby bindings to control windows in EWMH and NetWM compatible X Window manager.
|
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
10
|
-
ruby-wmctrl is a C extended library, which uses glib and x11
|
11
|
-
|
10
|
+
ruby-wmctrl is a C extended library, which uses glib and x11,
|
11
|
+
and is not a program to parse the output of command "wmctrl".
|
12
|
+
|
13
|
+
On ubuntu 11.04, we should install the following packages.
|
12
14
|
|
13
15
|
apt-get install libx11-dev libglib2.0-dev libxmu-dev
|
14
16
|
|
@@ -16,28 +18,49 @@ Then we can install ruby-wmctrl from rubygems.
|
|
16
18
|
|
17
19
|
gem install ruby-wmctrl
|
18
20
|
|
21
|
+
## Remark
|
22
|
+
|
23
|
+
Note that values of positions of windows aredifferent from
|
24
|
+
those of original "wmctrl" and include frame sizes.
|
25
|
+
|
19
26
|
## Usage
|
20
27
|
|
21
|
-
We load 'wmctrl' as below
|
28
|
+
We load 'wmctrl' as below;
|
22
29
|
|
23
30
|
require 'wmctrl'
|
24
31
|
|
25
|
-
|
32
|
+
We get desktops and windows as follows;
|
33
|
+
|
34
|
+
pp WMCtrl.instance.desktops
|
35
|
+
pp WMCtrl.instance.windows
|
36
|
+
|
37
|
+
We can specify search conditions of windows; for example,
|
38
|
+
|
39
|
+
pp WMCtrl.instance.windows(:wm_class => /^emacs/)
|
40
|
+
|
41
|
+
## Usage (Basic method similar to the command "wmctrl")
|
42
|
+
|
43
|
+
ruby-wmctrl has some basic methods derived from wmctrl,
|
44
|
+
but the author think that these methods may be changed in future release.
|
26
45
|
|
27
46
|
### List windows
|
28
47
|
|
29
48
|
require 'wmctrl'
|
30
49
|
require 'pp'
|
31
|
-
wm = WMCtrl.
|
50
|
+
wm = WMCtrl.instance
|
32
51
|
pp wm.list_windows
|
33
52
|
|
53
|
+
If you want to get properties of windows, you pass true as an argument.
|
54
|
+
|
55
|
+
pp wm.list_windows(true)
|
56
|
+
|
34
57
|
### Activate a window
|
35
58
|
|
36
59
|
wm.action_window(window_id, :activate)
|
37
60
|
|
38
|
-
The method '
|
39
|
-
We can get window ID by the method '
|
40
|
-
The second argument of '
|
61
|
+
The method 'action\_window' takes window ID as first argument.
|
62
|
+
We can get window ID by the method 'list\_windows'.
|
63
|
+
The second argument of 'action\_window' is an action that
|
41
64
|
manages the window of window ID.
|
42
65
|
The rest arguments are arguments of the action.
|
43
66
|
|
@@ -66,6 +89,11 @@ width, and height, respectively. That is,
|
|
66
89
|
|
67
90
|
pp wm.list_desktops
|
68
91
|
|
92
|
+
## rwmctrl
|
93
|
+
|
94
|
+
ruby-wmctrl includes the command "rwmctrl", which imitates the command "wmctrl"
|
95
|
+
but does not implement all functionalities of "wmctrl".
|
96
|
+
|
69
97
|
## License
|
70
98
|
|
71
99
|
GPLv2
|
data/Rakefile
CHANGED
@@ -2,3 +2,48 @@ require "bundler/gem_tasks"
|
|
2
2
|
|
3
3
|
require 'yard'
|
4
4
|
YARD::Rake::YardocTask.new
|
5
|
+
|
6
|
+
def each_extconf_directory(&block)
|
7
|
+
Dir.glob("ext/**/extconf.rb").each do |extconf|
|
8
|
+
cd File.dirname(extconf) do
|
9
|
+
yield
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Compile extended library'
|
15
|
+
task 'ext:compile' do |t|
|
16
|
+
each_extconf_directory do
|
17
|
+
sh 'ruby extconf.rb' unless File.exist?('Makefile')
|
18
|
+
sh 'make'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Compile extended library'
|
23
|
+
task 'ext:recompile' do |t|
|
24
|
+
each_extconf_directory do
|
25
|
+
sh 'make distclean' if File.exist?('Makefile')
|
26
|
+
sh 'ruby extconf.rb'
|
27
|
+
sh 'make'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Clean'
|
32
|
+
task 'ext:clean' do |t|
|
33
|
+
each_extconf_directory do
|
34
|
+
sh 'make clean' if File.exist?('Makefile')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Clean completely'
|
39
|
+
task 'ext:distclean' do |t|
|
40
|
+
each_extconf_directory do
|
41
|
+
sh 'make distclean' if File.exist?('Makefile')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'rspec/core'
|
46
|
+
require 'rspec/core/rake_task'
|
47
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
48
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
49
|
+
end
|
data/bin/rwmctrl
CHANGED
@@ -20,7 +20,7 @@ begin
|
|
20
20
|
opt.on('-p', 'Display PID of windows.') do |v|
|
21
21
|
options[:pid] = true
|
22
22
|
end
|
23
|
-
opt.on('-x', 'Display class name.') do |v|
|
23
|
+
opt.on('-x', 'Display class name or interpret <WIN> as the WM_CLASS name.') do |v|
|
24
24
|
options[:class] = true
|
25
25
|
end
|
26
26
|
opt.on('-G', 'Display geometry of windows.') do |v|
|
@@ -43,6 +43,10 @@ begin
|
|
43
43
|
options[:mode] = :close
|
44
44
|
options[:target] = v
|
45
45
|
end
|
46
|
+
opt.on('-a <WIN>', String, 'Switch desktop and make the specified window active.') do |v|
|
47
|
+
options[:mode] = :make_active
|
48
|
+
options[:target] = v
|
49
|
+
end
|
46
50
|
opt.on('-d', 'List all desktops.') do |v|
|
47
51
|
options[:mode] = :desktop
|
48
52
|
end
|
@@ -54,19 +58,62 @@ begin
|
|
54
58
|
options[:change_state] = v
|
55
59
|
end
|
56
60
|
opt.on('-n N', Integer, 'Change the number of desktops.') do |v|
|
57
|
-
options[:mode] = :
|
61
|
+
options[:mode] = :change_number_of_desktops
|
62
|
+
options[:arguments] = [v]
|
63
|
+
end
|
64
|
+
opt.on('-R <WIN>', String, 'Move <WIN> to the current desktop.') do |v|
|
65
|
+
options[:mode] = :move_to_current
|
66
|
+
options[:target] = v
|
67
|
+
end
|
68
|
+
opt.on('-t <DESK>', Integer, 'Move a window specified by -r to the desktop <DESK>.') do |v|
|
69
|
+
options[:mode] = :move_to_desktop
|
58
70
|
options[:desktop_number] = v
|
59
71
|
end
|
72
|
+
opt.on('-g SIZE', String, 'Change the geometry of all desktops. SIZE is the form w,h') do |v|
|
73
|
+
options[:mode] = :change_geometry
|
74
|
+
w, h = v.split(',').map(&:to_i)
|
75
|
+
if w && h
|
76
|
+
options[:arguments] = [w, h]
|
77
|
+
else
|
78
|
+
raise "Invalid argument"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
opt.on('-o SIZE', String, 'Change the viewport. SIZE is the form w,h') do |v|
|
82
|
+
options[:mode] = :change_viewport
|
83
|
+
w, h = v.split(',').map(&:to_i)
|
84
|
+
if w && h
|
85
|
+
options[:arguments] = [w, h]
|
86
|
+
else
|
87
|
+
raise "Invalid argument"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
opt.on('-N STR', String, 'Set long title of specified window.') do |v|
|
91
|
+
options[:mode] = :set_title_long
|
92
|
+
options[:arguments] = [v]
|
93
|
+
end
|
94
|
+
opt.on('-I STR', String, 'Set short title of specified window.') do |v|
|
95
|
+
options[:mode] = :set_title_short
|
96
|
+
options[:arguments] = [v]
|
97
|
+
end
|
98
|
+
opt.on('-T STR', String, 'Set long and short title of specified window.') do |v|
|
99
|
+
options[:mode] = :set_title_both
|
100
|
+
options[:arguments] = [v]
|
101
|
+
end
|
102
|
+
opt.on('-k ARG', String, 'Turn on or off "show the desktop". ARG is on or off.') do |v|
|
103
|
+
options[:mode] = :showing_desktop
|
104
|
+
if v == "on"
|
105
|
+
options[:arguments] = [true]
|
106
|
+
elsif v == "off"
|
107
|
+
options[:arguments] = [false]
|
108
|
+
else
|
109
|
+
raise "Invalid argument"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
opt.on('-s <DESK>', Integer, 'Switch to the desktop <DESK>') do |v|
|
113
|
+
options[:mode] = :switch_desktop
|
114
|
+
options[:arguments] = [v]
|
115
|
+
end
|
60
116
|
# Not implemented options
|
61
|
-
# '-g'
|
62
|
-
# '-I'
|
63
|
-
# '-k'
|
64
|
-
# '-N'
|
65
|
-
# '-o'
|
66
|
-
# '-R'
|
67
|
-
# '-s'
|
68
|
-
# '-t'
|
69
|
-
# '-T'
|
70
117
|
# '-w'
|
71
118
|
|
72
119
|
# '-u'
|
@@ -75,7 +122,7 @@ begin
|
|
75
122
|
options[:use_wid] = true
|
76
123
|
end
|
77
124
|
opt.on('-F', 'Use exact match of window title.') do |v|
|
78
|
-
|
125
|
+
options[:use_exact] = true
|
79
126
|
end
|
80
127
|
opt.on('--ignore-case', 'Ignore case to specfy target window.') do |v|
|
81
128
|
options[:ignore_case] = true
|
@@ -96,32 +143,27 @@ MES
|
|
96
143
|
exit(2)
|
97
144
|
end
|
98
145
|
|
99
|
-
wm = WMCtrl.
|
146
|
+
wm = WMCtrl.instance
|
100
147
|
|
101
|
-
|
148
|
+
# Return first window specified by arg and options
|
149
|
+
def specify_window(arg, options)
|
150
|
+
wm = WMCtrl.instance
|
151
|
+
win = nil
|
102
152
|
if arg == ':ACTIVE:'
|
103
|
-
wm.
|
104
|
-
w[:active] ? w : nil
|
105
|
-
end
|
153
|
+
win = wm.windows(:active => true).first
|
106
154
|
elsif arg == ':SELECT:'
|
107
155
|
raise 'Not implemented.'
|
108
156
|
elsif options[:use_wid]
|
109
|
-
|
110
|
-
wm.list_windows.find do |w|
|
111
|
-
wid == w[:id] ? w : nil
|
112
|
-
end
|
157
|
+
win = wm.windows(:id => Integer(arg)).first
|
113
158
|
else
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
118
|
-
else
|
119
|
-
reg = Regexp.new(arg, options[:ignore_case] ? Regexp::IGNORECASE : 0)
|
120
|
-
wm.list_windows.find do |w|
|
121
|
-
reg =~ w[:title] ? w : nil
|
122
|
-
end
|
123
|
-
end
|
159
|
+
key_name = (options[:class] ? :class : :title)
|
160
|
+
cond_title = (options[:use_exact] ? arg : Regexp.new(arg, options[:ignore_case] ? Regexp::IGNORECASE : 0))
|
161
|
+
win = wm.windows(key_name => cond_title).first
|
124
162
|
end
|
163
|
+
unless win
|
164
|
+
raise "Any window is not specified."
|
165
|
+
end
|
166
|
+
win
|
125
167
|
end
|
126
168
|
|
127
169
|
case options[:mode]
|
@@ -154,37 +196,39 @@ when :list_windows
|
|
154
196
|
puts s
|
155
197
|
end
|
156
198
|
when :move_resize
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
else
|
162
|
-
puts "Invalid arguments."
|
163
|
-
end
|
199
|
+
target_win = specify_window(options[:target], options)
|
200
|
+
args = options[:move_resize].split(',').map(&:to_i)
|
201
|
+
if args.size == 5
|
202
|
+
wm.action_window(target_win[:id], :move_resize, *args)
|
164
203
|
else
|
165
|
-
puts "
|
204
|
+
puts "Invalid arguments."
|
166
205
|
end
|
167
206
|
when :change_state
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
end
|
207
|
+
target_win = specify_window(options[:target], options)
|
208
|
+
args = options[:change_state].split(',')
|
209
|
+
if args.size == 2 || args.size == 3
|
210
|
+
target_win.change_state(*args)
|
211
|
+
else
|
212
|
+
puts "Invalid property string: #{args.inspect}"
|
175
213
|
end
|
176
214
|
when :close
|
177
|
-
|
178
|
-
|
179
|
-
|
215
|
+
specify_window(options[:target], options).close
|
216
|
+
when :make_active
|
217
|
+
specify_window(options[:target], options).activate
|
180
218
|
when :desktop
|
181
219
|
wm.list_desktops.each do |dt|
|
182
220
|
printf("%d %s DG: %dx%d VP: %d,%d WA: %d,%d %dx%d %s\n",
|
183
221
|
dt[:id], dt[:current] ? '*' : '-', dt[:geometry][0], dt[:geometry][1], dt[:viewport][0], dt[:viewport][1],
|
184
222
|
dt[:workarea][0], dt[:workarea][1], dt[:workarea][2], dt[:workarea][3], dt[:title])
|
185
223
|
end
|
186
|
-
when :
|
187
|
-
wm.
|
224
|
+
when :move_to_desktop
|
225
|
+
wm.action_window(specify_window(options[:target], options)[:id], :move_to_desktop, options[:desktop_number])
|
226
|
+
when :move_to_current
|
227
|
+
wm.action_window(specify_window(options[:target], options)[:id], :move_to_current)
|
228
|
+
when :change_number_of_desktops, :change_geometry, :change_viewport, :showing_desktop, :switch_desktop
|
229
|
+
wm.__send__(options[:mode], *options[:arguments])
|
230
|
+
when :set_title_both, :set_title_short, :set_title_long
|
231
|
+
wm.action_window(specify_window(options[:target], options)[:id], options[:mode], *options[:arguments])
|
188
232
|
when :wm_info
|
189
233
|
info = wm.info
|
190
234
|
puts "Name: #{info[:name]}"
|
data/ext/wmctrl.c
CHANGED
@@ -61,7 +61,9 @@ static ID id_select, id_active, id_activate, id_close, id_move_resize,
|
|
61
61
|
|
62
62
|
static void rb_wmctrl_free (void **ptr)
|
63
63
|
{
|
64
|
-
|
64
|
+
Display **disp;
|
65
|
+
disp = (Display **) ptr;
|
66
|
+
XCloseDisplay(*disp);
|
65
67
|
free(ptr);
|
66
68
|
}
|
67
69
|
|
@@ -237,11 +239,12 @@ static gchar *get_window_title (Display *disp, Window win)
|
|
237
239
|
}
|
238
240
|
|
239
241
|
/*
|
240
|
-
|
241
|
-
wm.list_windows(get_state = nil)
|
242
|
+
@overload list_windows(get_state = nil)
|
242
243
|
|
243
244
|
Get list of information of windows.
|
244
245
|
@param get_state [Boolean] If the value is true then we get some properties at the same time
|
246
|
+
|
247
|
+
@return [Hash] An array of hashes of window information.
|
245
248
|
*/
|
246
249
|
static VALUE rb_wmctrl_list_windows (int argc, VALUE *argv, VALUE self) {
|
247
250
|
Display **ptr, *disp;
|
@@ -387,7 +390,11 @@ static VALUE rb_wmctrl_list_windows (int argc, VALUE *argv, VALUE self) {
|
|
387
390
|
return window_ary;
|
388
391
|
}
|
389
392
|
|
390
|
-
/*
|
393
|
+
/*
|
394
|
+
Get list of information of desktops.
|
395
|
+
|
396
|
+
@return [Hash] An array of hashes of desktop information.
|
397
|
+
*/
|
391
398
|
static VALUE rb_wmctrl_list_desktops (VALUE self) {
|
392
399
|
Display **ptr, *disp;
|
393
400
|
unsigned long *num_desktops = NULL;
|
@@ -609,7 +616,15 @@ static VALUE rb_wmctrl_list_desktops (VALUE self) {
|
|
609
616
|
return ret;
|
610
617
|
}
|
611
618
|
|
612
|
-
/*
|
619
|
+
/*
|
620
|
+
@overload switch_desktop(desktop_id)
|
621
|
+
|
622
|
+
Switch desktop.
|
623
|
+
|
624
|
+
@param desktop_id [Integer] ID number of desktop.
|
625
|
+
|
626
|
+
@return [Qtrue]
|
627
|
+
*/
|
613
628
|
static VALUE rb_wmctrl_switch_desktop (VALUE self, VALUE desktop_id) {
|
614
629
|
int target;
|
615
630
|
Display **ptr, *disp;
|
@@ -624,7 +639,11 @@ static VALUE rb_wmctrl_switch_desktop (VALUE self, VALUE desktop_id) {
|
|
624
639
|
return Qtrue;
|
625
640
|
}
|
626
641
|
|
627
|
-
/*
|
642
|
+
/*
|
643
|
+
Get hash of information of window manager.
|
644
|
+
|
645
|
+
@return [Hash]
|
646
|
+
*/
|
628
647
|
static VALUE rb_wmctrl_info (VALUE self) {
|
629
648
|
Display **ptr, *disp;
|
630
649
|
Window *sup_window = NULL;
|
@@ -632,7 +651,7 @@ static VALUE rb_wmctrl_info (VALUE self) {
|
|
632
651
|
gchar *wm_class = NULL;
|
633
652
|
unsigned long *wm_pid = NULL;
|
634
653
|
unsigned long *showing_desktop = NULL;
|
635
|
-
gboolean name_is_utf8
|
654
|
+
gboolean name_is_utf8;
|
636
655
|
VALUE ret = rb_hash_new();
|
637
656
|
|
638
657
|
Data_Get_Struct(self, Display*, ptr);
|
@@ -644,11 +663,13 @@ static VALUE rb_wmctrl_info (VALUE self) {
|
|
644
663
|
XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK", NULL))) {
|
645
664
|
fputs("Cannot get window manager info properties.\n"
|
646
665
|
"(_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)\n", stderr);
|
647
|
-
return EXIT_FAILURE;
|
666
|
+
/* return EXIT_FAILURE; */
|
667
|
+
return Qfalse;
|
648
668
|
}
|
649
669
|
}
|
650
670
|
|
651
671
|
/* WM_NAME */
|
672
|
+
name_is_utf8 = TRUE;
|
652
673
|
if (! (wm_name = get_property(disp, *sup_window,
|
653
674
|
XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL))) {
|
654
675
|
name_is_utf8 = FALSE;
|
@@ -657,8 +678,12 @@ static VALUE rb_wmctrl_info (VALUE self) {
|
|
657
678
|
p_verbose("Cannot get name of the window manager (_NET_WM_NAME).\n");
|
658
679
|
}
|
659
680
|
}
|
681
|
+
if (wm_name) {
|
682
|
+
rb_hash_aset(ret, key_name, (name_is_utf8 ? RB_UTF8_STRING_NEW2(wm_name) : rb_str_new(wm_name, strlen(wm_name))));
|
683
|
+
}
|
660
684
|
|
661
685
|
/* WM_CLASS */
|
686
|
+
name_is_utf8 = TRUE;
|
662
687
|
if (! (wm_class = get_property(disp, *sup_window,
|
663
688
|
XInternAtom(disp, "UTF8_STRING", False), "WM_CLASS", NULL))) {
|
664
689
|
name_is_utf8 = FALSE;
|
@@ -667,6 +692,9 @@ static VALUE rb_wmctrl_info (VALUE self) {
|
|
667
692
|
p_verbose("Cannot get class of the window manager (WM_CLASS).\n");
|
668
693
|
}
|
669
694
|
}
|
695
|
+
if (wm_class) {
|
696
|
+
rb_hash_aset(ret, key_class, (name_is_utf8 ? RB_UTF8_STRING_NEW2(wm_class) : rb_str_new(wm_class, strlen(wm_class))));
|
697
|
+
}
|
670
698
|
|
671
699
|
/* WM_PID */
|
672
700
|
if (! (wm_pid = (unsigned long *)get_property(disp, *sup_window,
|
@@ -680,8 +708,6 @@ static VALUE rb_wmctrl_info (VALUE self) {
|
|
680
708
|
p_verbose("Cannot get the _NET_SHOWING_DESKTOP property.\n");
|
681
709
|
}
|
682
710
|
|
683
|
-
rb_hash_aset(ret, key_name, (wm_name ? RB_UTF8_STRING_NEW2(wm_name) : Qnil));
|
684
|
-
rb_hash_aset(ret, key_class, (wm_class ? RB_UTF8_STRING_NEW2(wm_class) : Qnil));
|
685
711
|
rb_hash_aset(ret, key_pid, (wm_pid ? UINT2NUM(*wm_pid) : Qnil));
|
686
712
|
rb_hash_aset(ret, key_showing_desktop,
|
687
713
|
(showing_desktop ? RB_UTF8_STRING_NEW2(*showing_desktop == 1 ? "ON" : "OFF") : Qnil));
|
@@ -695,7 +721,15 @@ static VALUE rb_wmctrl_info (VALUE self) {
|
|
695
721
|
return ret;
|
696
722
|
}
|
697
723
|
|
698
|
-
/*
|
724
|
+
/*
|
725
|
+
@overload showing_desktop(state)
|
726
|
+
|
727
|
+
Minimize windows to show desktop if the window manager implements "show the desktop" mode.
|
728
|
+
|
729
|
+
@param state [boolean] We set true if we want to turn on showing desktop mode.
|
730
|
+
|
731
|
+
@return [true]
|
732
|
+
*/
|
699
733
|
static VALUE rb_wmctrl_showing_desktop (VALUE self, VALUE state) {
|
700
734
|
Display **ptr, *disp;
|
701
735
|
Data_Get_Struct(self, Display*, ptr);
|
@@ -704,6 +738,16 @@ static VALUE rb_wmctrl_showing_desktop (VALUE self, VALUE state) {
|
|
704
738
|
return Qtrue;
|
705
739
|
}
|
706
740
|
|
741
|
+
/*
|
742
|
+
@overload change_viewport(x, y)
|
743
|
+
|
744
|
+
Change the viewport. A window manager may ignore this request.
|
745
|
+
|
746
|
+
@param x [Integer] Offset of top position
|
747
|
+
@param y [Integer] Offset of left position
|
748
|
+
|
749
|
+
@return [true]
|
750
|
+
*/
|
707
751
|
static VALUE rb_wmctrl_change_viewport (VALUE self, VALUE xnum, VALUE ynum) {
|
708
752
|
long x, y;
|
709
753
|
Display **ptr, *disp;
|
@@ -718,6 +762,16 @@ static VALUE rb_wmctrl_change_viewport (VALUE self, VALUE xnum, VALUE ynum) {
|
|
718
762
|
return Qtrue;
|
719
763
|
}
|
720
764
|
|
765
|
+
/*
|
766
|
+
@overload change_geometry(w, h)
|
767
|
+
|
768
|
+
Change geometry of all desktops. A window manager may ignore this request.
|
769
|
+
|
770
|
+
@param w [Ineteger] Width
|
771
|
+
@param h [Ineteger] Height
|
772
|
+
|
773
|
+
@return [true]
|
774
|
+
*/
|
721
775
|
static VALUE rb_wmctrl_change_geometry (VALUE self, VALUE xnum, VALUE ynum) {
|
722
776
|
long x, y;
|
723
777
|
Display **ptr, *disp;
|
@@ -732,7 +786,15 @@ static VALUE rb_wmctrl_change_geometry (VALUE self, VALUE xnum, VALUE ynum) {
|
|
732
786
|
return Qtrue;
|
733
787
|
}
|
734
788
|
|
735
|
-
/*
|
789
|
+
/*
|
790
|
+
@overload change_number_of_desktops(num)
|
791
|
+
|
792
|
+
Change number of desktops.
|
793
|
+
|
794
|
+
@param num [Integer] Number of desktops.
|
795
|
+
|
796
|
+
@return [true]
|
797
|
+
*/
|
736
798
|
static VALUE rb_wmctrl_change_number_of_desktops (VALUE self, VALUE num) {
|
737
799
|
long n;
|
738
800
|
Display **ptr, *disp;
|
@@ -1109,15 +1171,17 @@ static Window get_target_window (Display *disp, VALUE obj)
|
|
1109
1171
|
}
|
1110
1172
|
|
1111
1173
|
/*
|
1112
|
-
|
1113
|
-
wm.action_window(wid, cmd, *args)
|
1174
|
+
@overload action_window(wid, cmd, *args)
|
1114
1175
|
|
1115
1176
|
Manage windows.
|
1116
1177
|
@param wid Window ID
|
1117
1178
|
@param cmd [Symbol] Symbol of command
|
1118
1179
|
@param args [Array] Arguments for the command
|
1180
|
+
|
1181
|
+
@return [boolean] true if succeeded. Otherwise, false.
|
1119
1182
|
|
1120
1183
|
@example
|
1184
|
+
wm.action_window(wid, :activate)
|
1121
1185
|
wm.action_window(wid, :close)
|
1122
1186
|
wm.action_window(wid, :move_resize, grav, x, y, w, h)
|
1123
1187
|
wm.action_window(wid, :change_state, add, prop1, prop2 = nil)
|
@@ -1169,6 +1233,11 @@ static VALUE rb_wmctrl_action_window(int argc, VALUE *argv, VALUE self) {
|
|
1169
1233
|
}
|
1170
1234
|
}
|
1171
1235
|
|
1236
|
+
/*
|
1237
|
+
Get an array of _NET_SUPPORTED property.
|
1238
|
+
|
1239
|
+
@return [Array] An array of strings.
|
1240
|
+
*/
|
1172
1241
|
static VALUE rb_wmctrl_supported (VALUE self)
|
1173
1242
|
{
|
1174
1243
|
Atom *list;
|
@@ -1207,8 +1276,8 @@ void Init_wmctrl()
|
|
1207
1276
|
rb_define_method(rb_wmctrl_class, "switch_desktop", rb_wmctrl_switch_desktop, 1);
|
1208
1277
|
rb_define_method(rb_wmctrl_class, "info", rb_wmctrl_info, 0);
|
1209
1278
|
rb_define_method(rb_wmctrl_class, "showing_desktop", rb_wmctrl_showing_desktop, 1);
|
1210
|
-
rb_define_method(rb_wmctrl_class, "change_viewport", rb_wmctrl_change_viewport,
|
1211
|
-
rb_define_method(rb_wmctrl_class, "change_geometry", rb_wmctrl_change_geometry,
|
1279
|
+
rb_define_method(rb_wmctrl_class, "change_viewport", rb_wmctrl_change_viewport, 2);
|
1280
|
+
rb_define_method(rb_wmctrl_class, "change_geometry", rb_wmctrl_change_geometry, 2);
|
1212
1281
|
rb_define_method(rb_wmctrl_class, "change_number_of_desktops", rb_wmctrl_change_number_of_desktops, 1);
|
1213
1282
|
rb_define_method(rb_wmctrl_class, "action_window", rb_wmctrl_action_window, -1);
|
1214
1283
|
rb_define_method(rb_wmctrl_class, "supported", rb_wmctrl_supported, 0);
|
data/lib/wmctrl.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require 'wmctrl
|
1
|
+
require 'wmctrl/wmctrl'
|
2
2
|
require 'wmctrl/version'
|
data/lib/wmctrl/version.rb
CHANGED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'wmctrl.so'
|
2
|
+
|
3
|
+
class WMCtrl
|
4
|
+
def self.instance
|
5
|
+
@wmctrl ||= self.new
|
6
|
+
end
|
7
|
+
|
8
|
+
class DataHash
|
9
|
+
def initialize(data_hash)
|
10
|
+
@data = data_hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(*args, &block)
|
14
|
+
@data.__send__(*args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Desktop < DataHash
|
19
|
+
[:id, :current, :title, :geometry, :viewport, :workarea].each do |key|
|
20
|
+
define_method(key) do
|
21
|
+
self[key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Valid keys of the method[] are :id, :current, :title, :geometry, :viewport, and :workarea.
|
26
|
+
end
|
27
|
+
|
28
|
+
class Window < DataHash
|
29
|
+
[:id, :title, :active, :desktop, :client_machine,
|
30
|
+
:pid, :geometry, :state, :exterior_frame, :frame_extents, :strut].each do |key|
|
31
|
+
define_method(key) do
|
32
|
+
self[key]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Because method name "class" is a basic ruby method, we use "wm_class" to get window class.
|
37
|
+
def wm_class
|
38
|
+
self[:class]
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param [Symbol] key Valid keys are :class, :id, :title, :active, :desktop, :client_machine,
|
42
|
+
# :pid, :geometry, :state, :exterior_frame, :frame_extents, and :strut.
|
43
|
+
# :wm_class is an alias of :class.
|
44
|
+
def [](key)
|
45
|
+
if key == :wm_class
|
46
|
+
self.wm_class
|
47
|
+
else
|
48
|
+
super(key)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def sticky?
|
53
|
+
self[:desktop] == -1
|
54
|
+
end
|
55
|
+
|
56
|
+
def active?
|
57
|
+
self[:active]
|
58
|
+
end
|
59
|
+
|
60
|
+
def action(*args)
|
61
|
+
WMCtrl.instance.action_window(self[:id], *args)
|
62
|
+
end
|
63
|
+
private :action
|
64
|
+
|
65
|
+
def close
|
66
|
+
action(:close)
|
67
|
+
end
|
68
|
+
|
69
|
+
def change_state(*args)
|
70
|
+
action(:change_state, *args)
|
71
|
+
end
|
72
|
+
|
73
|
+
def activate
|
74
|
+
action(:activate)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param [Hash] opts Options
|
78
|
+
# @option opts [Integer] :x X coordinate
|
79
|
+
# @option opts [Integer] :y Y coordinate
|
80
|
+
# @option opts [Integer] :width Width of window
|
81
|
+
# @option opts [Integer] :height Height of window
|
82
|
+
# @option opts [Integer,:current] :desktop Desktop number
|
83
|
+
def place(opts = {})
|
84
|
+
if opts[:desktop]
|
85
|
+
if opts[:desktop] == :current
|
86
|
+
action(:move_to_current)
|
87
|
+
elsif (opts[:desktop] != self[:desktop])
|
88
|
+
action(:move_to_desktop, opts[:desktop])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
x = self[:exterior_frame][0]
|
92
|
+
y = self[:exterior_frame][1]
|
93
|
+
width = self[:exterior_frame][2]
|
94
|
+
height = self[:exterior_frame][3]
|
95
|
+
extent_horizontal = self[:frame_extents][0] + self[:frame_extents][1]
|
96
|
+
extent_vertical = self[:frame_extents][2] + self[:frame_extents][3]
|
97
|
+
width = opts[:width] if opts[:width] && (opts[:width] != width)
|
98
|
+
height = opts[:height] if opts[:height] && (opts[:height] != height)
|
99
|
+
x = opts[:x] if opts[:x] && (opts[:x] != x)
|
100
|
+
y = opts[:y] if opts[:y] && (opts[:y] != y)
|
101
|
+
action(:move_resize, 0, x + self[:frame_extents][0], y + self[:frame_extents][1], width - extent_horizontal, height - extent_vertical)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Array] An array of WMCtrl::Desktop
|
106
|
+
def desktops
|
107
|
+
list_desktops.map { |hash| WMCtrl::Desktop.new(hash) }
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param [Array] conditions An array of condition hash. The values of hash are tested by the method ===.
|
111
|
+
# @return [Array] An array of WMCtrl::Window
|
112
|
+
def windows(*conditions)
|
113
|
+
wins = list_windows(true).map { |hash| WMCtrl::Window.new(hash) }
|
114
|
+
unless conditions.empty?
|
115
|
+
wins_selected = []
|
116
|
+
conditions.each do |condition|
|
117
|
+
wins_rest = []
|
118
|
+
wins.each do |win|
|
119
|
+
if condition.all? { |k, v| v === win[k] }
|
120
|
+
wins_selected << win
|
121
|
+
else
|
122
|
+
wins_rest << win
|
123
|
+
end
|
124
|
+
end
|
125
|
+
wins = wins_rest
|
126
|
+
end
|
127
|
+
wins = wins_selected
|
128
|
+
end
|
129
|
+
wins
|
130
|
+
end
|
131
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/wmctrl_spec.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe WMCtrl do
|
4
|
+
before(:all) do
|
5
|
+
@wm = WMCtrl.new
|
6
|
+
end
|
7
|
+
|
8
|
+
let :wm do
|
9
|
+
@wm
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should get an array of hashes." do
|
13
|
+
wins = wm.list_windows
|
14
|
+
wins.should be_an_instance_of Array
|
15
|
+
wins.each do |w|
|
16
|
+
w.should be_an_instance_of Hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should get an array of hashes." do
|
21
|
+
desktops = wm.list_desktops
|
22
|
+
desktops.should be_an_instance_of Array
|
23
|
+
desktops.each do |w|
|
24
|
+
w.should be_an_instance_of Hash
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should get information of window manager." do
|
29
|
+
wm.info.should be_an_instance_of Hash
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should get an array." do
|
33
|
+
sp = wm.supported
|
34
|
+
sp.should be_an_instance_of Array
|
35
|
+
sp.each do |prop|
|
36
|
+
prop.should be_an_instance_of String
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# action_window(wid, cmd, *args)
|
41
|
+
# change_geometry(w, h)
|
42
|
+
# change_number_of_desktops(num)
|
43
|
+
# change_viewport(x, y)
|
44
|
+
# showing_desktop(state)
|
45
|
+
# switch_desktop(desktop_id)
|
46
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-wmctrl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,41 +9,56 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-03-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: yard
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
|
-
- -
|
35
|
+
- - '>='
|
31
36
|
- !ruby/object:Gem::Version
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: pkg-config
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
|
-
- -
|
51
|
+
- - '>='
|
42
52
|
- !ruby/object:Gem::Version
|
43
53
|
version: '0'
|
44
54
|
type: :runtime
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
description: Ruby bindings to control windows in EWMH and NetWM compatible X Window
|
48
63
|
manager, which is created from source code of wmctrl command.
|
49
64
|
email:
|
@@ -65,6 +80,7 @@ files:
|
|
65
80
|
- ext/wmctrl.c
|
66
81
|
- lib/wmctrl.rb
|
67
82
|
- lib/wmctrl/version.rb
|
83
|
+
- lib/wmctrl/wmctrl.rb
|
68
84
|
- ruby-wmctrl.gemspec
|
69
85
|
- sample/activate.rb
|
70
86
|
- sample/change_number_of_desktops.rb
|
@@ -80,6 +96,8 @@ files:
|
|
80
96
|
- sample/showing_desktop.rb
|
81
97
|
- sample/supported.rb
|
82
98
|
- sample/switch_desktop.rb
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
- spec/wmctrl_spec.rb
|
83
101
|
homepage: ''
|
84
102
|
licenses: []
|
85
103
|
post_install_message:
|
@@ -90,19 +108,20 @@ require_paths:
|
|
90
108
|
required_ruby_version: !ruby/object:Gem::Requirement
|
91
109
|
none: false
|
92
110
|
requirements:
|
93
|
-
- -
|
111
|
+
- - '>='
|
94
112
|
- !ruby/object:Gem::Version
|
95
113
|
version: '0'
|
96
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
115
|
none: false
|
98
116
|
requirements:
|
99
|
-
- -
|
117
|
+
- - '>='
|
100
118
|
- !ruby/object:Gem::Version
|
101
119
|
version: '0'
|
102
120
|
requirements: []
|
103
121
|
rubyforge_project: ruby-wmctrl
|
104
|
-
rubygems_version: 1.8.
|
122
|
+
rubygems_version: 1.8.25
|
105
123
|
signing_key:
|
106
124
|
specification_version: 3
|
107
125
|
summary: Ruby bindings to control windows
|
108
126
|
test_files: []
|
127
|
+
has_rdoc:
|