ruby-wmctrl 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|