ruby-wmctrl 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -5
- data/bin/rwmctrl +2 -2
- data/ext/wmctrl.c +76 -24
- data/lib/wmctrl/version.rb +1 -1
- data/lib/wmctrl/wmctrl.rb +49 -5
- data/ruby-wmctrl.gemspec +1 -0
- data/spec/wmctrl_spec.rb +10 -0
- metadata +17 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 207ccda9bcf9eef6653853a9cbf21f4ff720348c2e105ed438862b6dbd1d99c0
|
4
|
+
data.tar.gz: 59318faac41732c72b002e2aab29e00bbb76c711e058f066a8683fad81ac9607
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7839e29f39f10420a359ad6891ce9225ca8d830f9d7ac68dc822cc9519c987cc79ee5097737bb7bac55e13d9017b376c112b09092feee524c48d3828c5429af4
|
7
|
+
data.tar.gz: 9c1fa80e1b3e5656f64f80a1908c7a7187b2e27f5f4c456eb263b4d7b9791b073202297f8711a29e1614da435b44f6918f35dee20b8ef66eafbddb41a029e497
|
data/README.md
CHANGED
@@ -31,12 +31,12 @@ We load 'wmctrl' as below;
|
|
31
31
|
|
32
32
|
We get desktops and windows as follows;
|
33
33
|
|
34
|
-
pp WMCtrl.
|
35
|
-
pp WMCtrl.
|
34
|
+
pp WMCtrl.display.desktops
|
35
|
+
pp WMCtrl.display.windows
|
36
36
|
|
37
37
|
We can specify search conditions of windows; for example,
|
38
38
|
|
39
|
-
pp WMCtrl.
|
39
|
+
pp WMCtrl.display.windows(:wm_class => /^emacs/)
|
40
40
|
|
41
41
|
## Usage (Basic method similar to the command "wmctrl")
|
42
42
|
|
@@ -47,7 +47,7 @@ but the author think that these methods may be changed in future release.
|
|
47
47
|
|
48
48
|
require 'wmctrl'
|
49
49
|
require 'pp'
|
50
|
-
wm = WMCtrl.
|
50
|
+
wm = WMCtrl.display
|
51
51
|
pp wm.list_windows
|
52
52
|
|
53
53
|
If you want to get properties of windows, you pass true as an argument.
|
@@ -111,4 +111,4 @@ Copyright (C) 2003
|
|
111
111
|
|
112
112
|
### ruby-wmctrl
|
113
113
|
|
114
|
-
Takayuki YAMAGUCHI d@ytak.info Copyright (C) 2011
|
114
|
+
Takayuki YAMAGUCHI d@ytak.info Copyright (C) 2011-2019
|
data/bin/rwmctrl
CHANGED
@@ -143,11 +143,11 @@ MES
|
|
143
143
|
exit(2)
|
144
144
|
end
|
145
145
|
|
146
|
-
wm = WMCtrl.
|
146
|
+
wm = WMCtrl.display
|
147
147
|
|
148
148
|
# Return first window specified by arg and options
|
149
149
|
def specify_window(arg, options)
|
150
|
-
wm = WMCtrl.
|
150
|
+
wm = WMCtrl.display
|
151
151
|
win = nil
|
152
152
|
if arg == ':ACTIVE:'
|
153
153
|
win = wm.windows(:active => true).first
|
data/ext/wmctrl.c
CHANGED
@@ -32,7 +32,7 @@ static int client_msg(Display *disp, Window win, const char *msg,
|
|
32
32
|
unsigned long data4);
|
33
33
|
static gchar *get_property (Display *disp, Window win,
|
34
34
|
Atom xa_prop_type, const gchar *prop_name, unsigned long *size);
|
35
|
-
static Window *get_client_list (Display *disp, unsigned long *size);
|
35
|
+
static Window *get_client_list (Display *disp, unsigned long *size, gboolean stacking_order);
|
36
36
|
static gchar *get_window_class (Display *disp, Window win);
|
37
37
|
static gchar *get_window_title (Display *disp, Window win);
|
38
38
|
static VALUE activate_window (Display *disp, Window win, gboolean switch_desktop);
|
@@ -48,7 +48,7 @@ static Window get_target_window (Display *disp, VALUE obj);
|
|
48
48
|
|
49
49
|
|
50
50
|
static VALUE rb_wmctrl_class, key_id, key_title, key_pid, key_geometry,
|
51
|
-
key_active, key_class, key_client_machine, key_desktop,
|
51
|
+
key_display, key_active, key_class, key_client_machine, key_desktop,
|
52
52
|
key_viewport, key_workarea, key_current, key_showing_desktop, key_name,
|
53
53
|
key_state, key_window_type, key_frame_extents, key_strut, key_exterior_frame;
|
54
54
|
|
@@ -60,7 +60,9 @@ static void rb_wmctrl_free (void **ptr)
|
|
60
60
|
{
|
61
61
|
Display **disp;
|
62
62
|
disp = (Display **) ptr;
|
63
|
-
|
63
|
+
if (*disp) {
|
64
|
+
XCloseDisplay(*disp);
|
65
|
+
}
|
64
66
|
free(ptr);
|
65
67
|
}
|
66
68
|
|
@@ -72,14 +74,35 @@ static VALUE rb_wmctrl_alloc(VALUE self)
|
|
72
74
|
|
73
75
|
static VALUE rb_wmctrl_initialize(int argc, VALUE *argv, VALUE self)
|
74
76
|
{
|
77
|
+
char *display_name = NULL;
|
75
78
|
Display **ptr;
|
76
79
|
Data_Get_Struct(self, Display*, ptr);
|
77
|
-
if (
|
78
|
-
|
80
|
+
if (argc > 0) {
|
81
|
+
display_name = StringValuePtr(argv[0]);
|
82
|
+
}
|
83
|
+
if (!(*ptr = XOpenDisplay(display_name))) {
|
84
|
+
if (display_name) {
|
85
|
+
rb_raise(rb_eStandardError, "Cannot open display %s", display_name);
|
86
|
+
} else {
|
87
|
+
rb_raise(rb_eStandardError, "Cannot open display");
|
88
|
+
}
|
79
89
|
}
|
80
90
|
return self;
|
81
91
|
}
|
82
92
|
|
93
|
+
/*
|
94
|
+
Get display name.
|
95
|
+
|
96
|
+
@return [String] A name of display.
|
97
|
+
*/
|
98
|
+
static VALUE rb_wmctrl_get_display_name (VALUE self) {
|
99
|
+
char *display_name;
|
100
|
+
Display **ptr;
|
101
|
+
Data_Get_Struct(self, Display*, ptr);
|
102
|
+
display_name = DisplayString(*ptr);
|
103
|
+
return(RB_UTF8_STRING_NEW2(display_name));
|
104
|
+
}
|
105
|
+
|
83
106
|
static int client_msg(Display *disp, Window win, const char *msg,
|
84
107
|
unsigned long data0, unsigned long data1,
|
85
108
|
unsigned long data2, unsigned long data3,
|
@@ -107,6 +130,25 @@ static int client_msg(Display *disp, Window win, const char *msg,
|
|
107
130
|
return True;
|
108
131
|
}
|
109
132
|
|
133
|
+
static VALUE rb_client_msg(VALUE self, VALUE win_id_obj, VALUE msg_obj, VALUE data0_obj, VALUE data1_obj, VALUE data2_obj, VALUE data3_obj, VALUE data4_obj)
|
134
|
+
{
|
135
|
+
Display **ptr, *disp;
|
136
|
+
Window win_id;
|
137
|
+
char *msg;
|
138
|
+
unsigned long data0, data1, data2, data3, data4;
|
139
|
+
win_id = (Window) NUM2LONG(win_id_obj);
|
140
|
+
Data_Get_Struct(self, Display*, ptr);
|
141
|
+
disp = *ptr;
|
142
|
+
msg = StringValuePtr(msg_obj);
|
143
|
+
data0 = (Window) NUM2ULONG(data0_obj);
|
144
|
+
data1 = (Window) NUM2ULONG(data1_obj);
|
145
|
+
data2 = (Window) NUM2ULONG(data2_obj);
|
146
|
+
data3 = (Window) NUM2ULONG(data3_obj);
|
147
|
+
data4 = (Window) NUM2ULONG(data4_obj);
|
148
|
+
client_msg(disp, win_id, msg, data0, data1, data2, data3, data4);
|
149
|
+
return Qtrue;
|
150
|
+
}
|
151
|
+
|
110
152
|
/* Copy from debian package for 64bit support. */
|
111
153
|
static gchar *get_property (Display *disp, Window win,
|
112
154
|
Atom xa_prop_type, const gchar *prop_name, unsigned long *size)
|
@@ -166,21 +208,23 @@ static gchar *get_property (Display *disp, Window win,
|
|
166
208
|
return ret;
|
167
209
|
}
|
168
210
|
|
169
|
-
static Window *get_client_list (Display *disp, unsigned long *size)
|
211
|
+
static Window *get_client_list (Display *disp, unsigned long *size, gboolean stacking_order)
|
170
212
|
{
|
171
213
|
Window *client_list;
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
"(_NET_CLIENT_LIST or _WIN_CLIENT_LIST)"
|
179
|
-
"\n", stderr);
|
180
|
-
return NULL;
|
214
|
+
if (stacking_order) {
|
215
|
+
client_list = (Window *)get_property(disp, DefaultRootWindow(disp), XA_WINDOW, "_NET_CLIENT_LIST_STACKING", size);
|
216
|
+
} else {
|
217
|
+
client_list = (Window *)get_property(disp, DefaultRootWindow(disp), XA_WINDOW, "_NET_CLIENT_LIST", size);
|
218
|
+
if (client_list == NULL) {
|
219
|
+
client_list = (Window *)get_property(disp, DefaultRootWindow(disp), XA_CARDINAL, "_WIN_CLIENT_LIST", size);
|
181
220
|
}
|
182
221
|
}
|
183
|
-
|
222
|
+
if (client_list == NULL) {
|
223
|
+
fputs("Cannot get client list properties. \n"
|
224
|
+
"(_NET_CLIENT_LIST or _WIN_CLIENT_LIST)"
|
225
|
+
"\n", stderr);
|
226
|
+
return NULL;
|
227
|
+
}
|
184
228
|
return client_list;
|
185
229
|
}
|
186
230
|
|
@@ -235,7 +279,7 @@ static gchar *get_window_title (Display *disp, Window win)
|
|
235
279
|
return title_utf8;
|
236
280
|
}
|
237
281
|
|
238
|
-
static VALUE get_window_hash_data (Window win, Display *disp, Window window_active, int get_state)
|
282
|
+
static VALUE get_window_hash_data (Window win, Display *disp, Window window_active, int get_state, VALUE display_name)
|
239
283
|
{
|
240
284
|
VALUE window_obj = rb_hash_new();
|
241
285
|
gchar *title_utf8 = get_window_title(disp, win); /* UTF8 */
|
@@ -250,6 +294,7 @@ static VALUE get_window_hash_data (Window win, Display *disp, Window window_acti
|
|
250
294
|
rb_hash_aset(window_obj, key_id, INT2NUM(win));
|
251
295
|
rb_hash_aset(window_obj, key_title, (title_utf8 ? RB_UTF8_STRING_NEW2(title_utf8) : Qnil));
|
252
296
|
rb_hash_aset(window_obj, key_class, (class_out ? RB_UTF8_STRING_NEW2(class_out) : Qnil));
|
297
|
+
rb_hash_aset(window_obj, key_display, display_name);
|
253
298
|
|
254
299
|
if (window_active == win) {
|
255
300
|
rb_hash_aset(window_obj, key_active, Qtrue);
|
@@ -378,7 +423,7 @@ static VALUE get_window_hash_data (Window win, Display *disp, Window window_acti
|
|
378
423
|
}
|
379
424
|
|
380
425
|
/*
|
381
|
-
@overload list_windows(get_state = nil)
|
426
|
+
@overload list_windows(get_state = nil, stacking_order = nil)
|
382
427
|
|
383
428
|
Get list of information of windows.
|
384
429
|
@param get_state [Boolean] If the value is true then we get some properties at the same time
|
@@ -391,23 +436,25 @@ static VALUE rb_wmctrl_list_windows (int argc, VALUE *argv, VALUE self) {
|
|
391
436
|
Window window_active;
|
392
437
|
unsigned long client_list_size;
|
393
438
|
unsigned int i;
|
394
|
-
int get_state;
|
395
|
-
VALUE get_state_obj, window_ary;
|
439
|
+
int get_state, stacking_order;
|
440
|
+
VALUE get_state_obj, stacking_order_obj, window_ary, display_name;
|
396
441
|
Data_Get_Struct(self, Display*, ptr);
|
397
442
|
disp = *ptr;
|
398
|
-
rb_scan_args(argc, argv, "
|
443
|
+
rb_scan_args(argc, argv, "02", &get_state_obj, &stacking_order_obj);
|
399
444
|
get_state = RTEST(get_state_obj);
|
445
|
+
stacking_order = RTEST(stacking_order_obj);
|
400
446
|
|
401
|
-
if ((client_list = get_client_list(disp, &client_list_size)) == NULL) {
|
447
|
+
if ((client_list = get_client_list(disp, &client_list_size, stacking_order)) == NULL) {
|
402
448
|
/* return EXIT_FAILURE; */
|
403
449
|
return Qfalse;
|
404
450
|
}
|
405
451
|
|
406
452
|
window_active = get_active_window(disp);
|
407
453
|
window_ary = rb_ary_new2(client_list_size);
|
454
|
+
display_name = rb_wmctrl_get_display_name(self);
|
408
455
|
|
409
456
|
for (i = 0; i < client_list_size / sizeof(Window); i++) {
|
410
|
-
rb_ary_push(window_ary, get_window_hash_data(client_list[i], disp, window_active, get_state));
|
457
|
+
rb_ary_push(window_ary, get_window_hash_data(client_list[i], disp, window_active, get_state, display_name));
|
411
458
|
}
|
412
459
|
g_free(client_list);
|
413
460
|
|
@@ -420,10 +467,12 @@ static VALUE rb_wmctrl_list_windows (int argc, VALUE *argv, VALUE self) {
|
|
420
467
|
static VALUE rb_wmctrl_get_window_data (VALUE self, VALUE win_id_obj) {
|
421
468
|
Display **ptr, *disp;
|
422
469
|
Window win_id;
|
470
|
+
VALUE display_name;
|
423
471
|
win_id = (Window) NUM2LONG(win_id_obj);
|
424
472
|
Data_Get_Struct(self, Display*, ptr);
|
425
473
|
disp = *ptr;
|
426
|
-
|
474
|
+
display_name = rb_wmctrl_get_display_name(self);
|
475
|
+
return get_window_hash_data(win_id, disp, -1, TRUE, display_name);
|
427
476
|
}
|
428
477
|
|
429
478
|
/*
|
@@ -1307,6 +1356,8 @@ void Init_wmctrl()
|
|
1307
1356
|
rb_define_alloc_func(rb_wmctrl_class, rb_wmctrl_alloc);
|
1308
1357
|
rb_define_private_method(rb_wmctrl_class, "initialize", rb_wmctrl_initialize, -1);
|
1309
1358
|
|
1359
|
+
rb_define_method(rb_wmctrl_class, "get_display_name", rb_wmctrl_get_display_name, 0);
|
1360
|
+
rb_define_method(rb_wmctrl_class, "client_msg", rb_client_msg, 7);
|
1310
1361
|
rb_define_method(rb_wmctrl_class, "list_windows", rb_wmctrl_list_windows, -1);
|
1311
1362
|
rb_define_method(rb_wmctrl_class, "get_window_data", rb_wmctrl_get_window_data, 1);
|
1312
1363
|
rb_define_method(rb_wmctrl_class, "list_desktops", rb_wmctrl_list_desktops, 0);
|
@@ -1323,6 +1374,7 @@ void Init_wmctrl()
|
|
1323
1374
|
key_title = ID2SYM(rb_intern("title"));
|
1324
1375
|
key_pid = ID2SYM(rb_intern("pid"));
|
1325
1376
|
key_geometry = ID2SYM(rb_intern("geometry"));
|
1377
|
+
key_display = ID2SYM(rb_intern("display"));
|
1326
1378
|
key_active = ID2SYM(rb_intern("active"));
|
1327
1379
|
key_class = ID2SYM(rb_intern("class"));
|
1328
1380
|
key_client_machine = ID2SYM(rb_intern("client_machine"));
|
data/lib/wmctrl/version.rb
CHANGED
data/lib/wmctrl/wmctrl.rb
CHANGED
@@ -14,8 +14,20 @@ class WMCtrl
|
|
14
14
|
GRAVITY_SOUTH_EAST = 9
|
15
15
|
GRAVITY_STATIC = 10
|
16
16
|
|
17
|
+
@wmctrl = {}
|
18
|
+
|
17
19
|
def self.instance
|
18
|
-
|
20
|
+
STDERR.puts "WMCtrl.instance is deprecated. Please use WMCtrl.display alternatively."
|
21
|
+
self.display
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.display(dpy = nil)
|
25
|
+
dpy ||= ENV["DISPLAY"]
|
26
|
+
unless display = @wmctrl[dpy]
|
27
|
+
display = self.new(dpy)
|
28
|
+
@wmctrl[dpy] = display
|
29
|
+
end
|
30
|
+
display
|
19
31
|
end
|
20
32
|
|
21
33
|
class DataHash
|
@@ -68,7 +80,7 @@ class WMCtrl
|
|
68
80
|
|
69
81
|
class Window < DataHash
|
70
82
|
[:id, :title, :active, :desktop, :client_machine,
|
71
|
-
:pid, :geometry, :state, :exterior_frame, :frame_extents, :strut].each do |key|
|
83
|
+
:pid, :geometry, :state, :exterior_frame, :frame_extents, :strut, :display].each do |key|
|
72
84
|
define_method(key) do
|
73
85
|
self[key]
|
74
86
|
end
|
@@ -131,8 +143,9 @@ class WMCtrl
|
|
131
143
|
end
|
132
144
|
|
133
145
|
def action(*args)
|
134
|
-
WMCtrl.
|
135
|
-
|
146
|
+
wmctrl = WMCtrl.display(self[:display])
|
147
|
+
wmctrl.action_window(self[:id], *args)
|
148
|
+
@data = wmctrl.get_window_data(self[:id])
|
136
149
|
self
|
137
150
|
end
|
138
151
|
private :action
|
@@ -191,16 +204,26 @@ class WMCtrl
|
|
191
204
|
list_desktops.map { |hash| WMCtrl::Desktop.new(hash) }
|
192
205
|
end
|
193
206
|
|
207
|
+
# @overload windows(*conditions, opts = {}, &block)
|
194
208
|
# @param [Array] conditions An array of condition hash.
|
195
209
|
# The keys of the hash are keys of WMCtrl::Window#[].
|
196
210
|
# The values of the hash are tested by the method ===.
|
197
211
|
# Keys of each condition are combined by logical product and
|
198
212
|
# all conditions are combined by logical sum.
|
213
|
+
# @param [Hash] opts Options
|
214
|
+
# @option opts [Integer] :order Order of windows: :mapping or :stacking
|
199
215
|
# @yield [WMCtrl::Window] Test window by block
|
200
216
|
# @return [Array] An array of WMCtrl::Window including windows
|
201
217
|
# matched by the conditions and the block.
|
202
218
|
def windows(*conditions, &block)
|
203
|
-
|
219
|
+
cond_last = conditions.last || {}
|
220
|
+
if cond_last[:order] && (cond_last[:order] == :stacking)
|
221
|
+
conditions.pop
|
222
|
+
wins = list_windows(true, true)
|
223
|
+
else
|
224
|
+
wins = list_windows(true, nil)
|
225
|
+
end
|
226
|
+
wins.map! { |h| WMCtrl::Window.new(h) }
|
204
227
|
unless conditions.empty?
|
205
228
|
wins_selected = []
|
206
229
|
conditions.each do |condition|
|
@@ -221,4 +244,25 @@ class WMCtrl
|
|
221
244
|
end
|
222
245
|
wins
|
223
246
|
end
|
247
|
+
|
248
|
+
WAIT_CHANGE_STACKING_ORDER = 0.01
|
249
|
+
|
250
|
+
# @param [Hash] opts Options
|
251
|
+
# @option opts [Float] :waiting_time Time to wait restack request
|
252
|
+
# @option opts [String] :display Display name
|
253
|
+
def restack(wins_bottom_to_top, opts = {})
|
254
|
+
win_upper = nil
|
255
|
+
waiting_time = opts[:sleep] || WAIT_CHANGE_STACKING_ORDER
|
256
|
+
wins_bottom_to_top.reverse.each do |win|
|
257
|
+
next if win.sticky?
|
258
|
+
if win_upper
|
259
|
+
# 1 means below
|
260
|
+
WMCtrl.display(opts[:display]).client_msg(win.id, "_NET_RESTACK_WINDOW", 2, win_upper.id, 1, 0, 0)
|
261
|
+
# If there is no sleep, change the stacking order fails.
|
262
|
+
# Can we use _NET_WM_SYNC_REQUEST?
|
263
|
+
sleep(waiting_time)
|
264
|
+
end
|
265
|
+
win_upper = win
|
266
|
+
end
|
267
|
+
end
|
224
268
|
end
|
data/ruby-wmctrl.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.extensions = Dir.glob("ext/**/extconf.rb")
|
20
20
|
|
21
21
|
# specify any dependencies here; for example:
|
22
|
+
spec.add_development_dependency "rake"
|
22
23
|
spec.add_development_dependency "rspec"
|
23
24
|
spec.add_development_dependency "yard"
|
24
25
|
spec.add_runtime_dependency "pkg-config"
|
data/spec/wmctrl_spec.rb
CHANGED
@@ -15,6 +15,16 @@ describe WMCtrl do
|
|
15
15
|
wins.each do |w|
|
16
16
|
expect(w).to be_an_instance_of Hash
|
17
17
|
end
|
18
|
+
wins = wm.list_windows(true)
|
19
|
+
expect(wins).to be_an_instance_of Array
|
20
|
+
wins.each do |w|
|
21
|
+
expect(w).to be_an_instance_of Hash
|
22
|
+
end
|
23
|
+
wins = wm.list_windows(true, true)
|
24
|
+
expect(wins).to be_an_instance_of Array
|
25
|
+
wins.each do |w|
|
26
|
+
expect(w).to be_an_instance_of Hash
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
30
|
it "should get an array of hashes." do
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
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.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takayuki YAMAGUCHI
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,8 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
125
|
- !ruby/object:Gem::Version
|
112
126
|
version: '0'
|
113
127
|
requirements: []
|
114
|
-
|
115
|
-
rubygems_version: 2.7.3
|
128
|
+
rubygems_version: 3.0.3
|
116
129
|
signing_key:
|
117
130
|
specification_version: 4
|
118
131
|
summary: Ruby bindings to control windows
|