movewin 1.3 → 1.4

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.
@@ -47,6 +47,7 @@ void Init_movewin_ext();
47
47
  VALUE MW_is_authorized(VALUE module); /* MoveWin.authorized? */
48
48
  VALUE MW_display_size(VALUE module); /* MoveWin.display_size */
49
49
  VALUE MW_windows(VALUE module); /* MoveWin.windows */
50
+ VALUE MW_Window_id(VALUE self); /* MoveWin::Window.id */
50
51
  VALUE MW_Window_app_name(VALUE self); /* MoveWin::Window.app_name */
51
52
  VALUE MW_Window_title(VALUE self); /* MoveWin::Window.title */
52
53
  VALUE MW_Window_position(VALUE self); /* MoveWin::Window.position */
@@ -91,6 +92,7 @@ void Init_movewin_ext() {
91
92
 
92
93
  /* Define class MoveWin::Window and its methods */
93
94
  MW_WindowClass = rb_define_class_under(MW_Module, "Window", rb_cObject);
95
+ rb_define_method(MW_WindowClass, "id", MW_Window_id, 0);
94
96
  rb_define_method(MW_WindowClass, "app_name", MW_Window_app_name, 0);
95
97
  rb_define_method(MW_WindowClass, "title", MW_Window_title, 0);
96
98
  rb_define_method(MW_WindowClass, "position", MW_Window_position, 0);
@@ -127,6 +129,19 @@ VALUE MW_windows(VALUE module) {
127
129
  return retval;
128
130
  }
129
131
 
132
+ /* Return window ID a MoveWin::Window as an integer (nil for unknown) */
133
+ VALUE MW_Window_id(VALUE self) {
134
+ void *mwWindow;
135
+ int windowId;
136
+
137
+ Data_Get_Struct(self, MW_Window, mwWindow);
138
+ windowId = CFDictionaryGetInt(
139
+ ((MW_Window *)mwWindow)->cgWindow, kCGWindowNumber
140
+ );
141
+
142
+ return INT2NUM(windowId);
143
+ }
144
+
130
145
  /* Return application name (owner) of a MoveWin::Window as a Ruby string */
131
146
  VALUE MW_Window_app_name(VALUE self) {
132
147
  void *mwWindow;
@@ -244,28 +259,20 @@ VALUE MW_Window_to_string(VALUE self) {
244
259
 
245
260
  /* Given window CFDictionaryRef and Ruby array, push MW_Window to array */
246
261
  void StoreWindows(CFDictionaryRef cgWindow, void *rb_ary_ptr) {
247
- int i;
248
262
  AXUIElementRef axWindow;
249
263
  MW_Window *mwWindow;
250
264
  VALUE wrappedMwWindow;
251
- VALUE mwWindows;
252
-
253
- mwWindows = (VALUE)rb_ary_ptr;
254
-
255
- i = 0;
256
- while(1) {
257
- axWindow = AXWindowFromCGWindow(cgWindow, i);
258
- if(!axWindow) break;
259
- mwWindow = (MW_Window *)malloc(sizeof(MW_Window));
260
- mwWindow->cgWindow =
261
- CFDictionaryCreateCopy(kCFAllocatorDefault, cgWindow);
262
- mwWindow->axWindow = axWindow;
263
- wrappedMwWindow = Data_Wrap_Struct(
264
- MW_WindowClass, NULL, MW_Window_destroy, (void *)mwWindow
265
- );
266
- rb_ary_push(mwWindows, wrappedMwWindow);
267
- i++;
268
- }
265
+ VALUE mwWindows = (VALUE)rb_ary_ptr;
266
+
267
+ axWindow = AXWindowFromCGWindow(cgWindow);
268
+ mwWindow = (MW_Window *)malloc(sizeof(MW_Window));
269
+ mwWindow->cgWindow =
270
+ CFDictionaryCreateCopy(kCFAllocatorDefault, cgWindow);
271
+ mwWindow->axWindow = axWindow;
272
+ wrappedMwWindow = Data_Wrap_Struct(
273
+ MW_WindowClass, NULL, MW_Window_destroy, (void *)mwWindow
274
+ );
275
+ rb_ary_push(mwWindows, wrappedMwWindow);
269
276
  }
270
277
 
271
278
  /* Free up MW_Window resources, for Ruby finalizer in Data_Wrap_Struct() */
@@ -37,6 +37,12 @@
37
37
  #include <fnmatch.h>
38
38
  #include "winutils.h"
39
39
 
40
+ /* Undocumented accessibility API to get window ID:
41
+ * http://stackoverflow.com/a/10134254
42
+ * https://github.com/jmgao/metamove/blob/master/src/window.mm
43
+ */
44
+ extern AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID *out);
45
+
40
46
  /* Search windows for match (NULL for all), run function (NULL for none) */
41
47
  int EnumerateWindows(
42
48
  char *pattern,
@@ -165,17 +171,22 @@ bool isAuthorized() {
165
171
  #endif
166
172
  }
167
173
 
174
+ /* Silence warning that address of _AXUIElementGetWindow is always true */
175
+ #pragma GCC diagnostic ignored "-Waddress"
176
+
168
177
  /* Given window dictionary from CGWindowList, return accessibility object */
169
- AXUIElementRef AXWindowFromCGWindow(CFDictionaryRef window, int minIdx) {
178
+ AXUIElementRef AXWindowFromCGWindow(CFDictionaryRef window) {
179
+ CGWindowID targetWindowId, actualWindowId;
170
180
  CFStringRef targetWindowName, actualWindowTitle;
171
181
  CGPoint targetPosition, actualPosition;
172
182
  CGSize targetSize, actualSize;
173
183
  pid_t pid;
174
184
  AXUIElementRef app, appWindow, foundAppWindow;
185
+ int i;
175
186
  CFArrayRef appWindowList;
176
- int matchIdx, i;
177
187
 
178
- /* Save the window name, position, and size we are looking for */
188
+ /* Save the window ID, name, position, and size we are looking for */
189
+ targetWindowId = CFDictionaryGetInt(window, kCGWindowNumber);
179
190
  targetWindowName = CFDictionaryGetValue(window, kCGWindowName);
180
191
  targetPosition = CGWindowGetPosition(window);
181
192
  targetSize = CGWindowGetSize(window);
@@ -187,37 +198,45 @@ AXUIElementRef AXWindowFromCGWindow(CFDictionaryRef window, int minIdx) {
187
198
  app, kAXWindowsAttribute, (CFTypeRef *)&appWindowList
188
199
  );
189
200
 
190
- /* Search application windows for first matching title, position, size:
191
- * http://stackoverflow.com/questions/6178860/getting-window-number-through-osx-accessibility-api
192
- */
193
- matchIdx = 0;
201
+ /* Search application windows to find a match */
194
202
  foundAppWindow = NULL;
195
203
  for(i = 0; i < CFArrayGetCount(appWindowList); i++) {
196
204
  appWindow = CFArrayGetValueAtIndex(appWindowList, i);
197
205
 
198
- /* Window name must match */
199
- AXUIElementCopyAttributeValue(
200
- appWindow, kAXTitleAttribute, (CFTypeRef *)&actualWindowTitle
201
- );
202
- if( !actualWindowTitle ||
203
- CFStringCompare(targetWindowName, actualWindowTitle, 0) != 0)
204
- {
205
- continue;
206
- }
206
+ /* If possible, extract the window ID and match window by ID */
207
+ if(_AXUIElementGetWindow) {
208
+ _AXUIElementGetWindow(appWindow, &actualWindowId);
209
+ if(actualWindowId == targetWindowId) {
210
+ foundAppWindow = appWindow;
211
+ break;
212
+ } else {
213
+ continue;
214
+ }
215
+
216
+ /* Otherwise, search for first matching title, position, size:
217
+ * http://stackoverflow.com/questions/6178860/getting-window-number-through-osx-accessibility-api
218
+ */
219
+ } else {
207
220
 
208
- /* Position and size must match */
209
- actualPosition = AXWindowGetPosition(appWindow);
210
- if(!CGPointEqualToPoint(targetPosition, actualPosition)) continue;
211
- actualSize = AXWindowGetSize(appWindow);
212
- if(!CGSizeEqualToSize(targetSize, actualSize)) continue;
221
+ /* Window name must match */
222
+ AXUIElementCopyAttributeValue(
223
+ appWindow, kAXTitleAttribute, (CFTypeRef *)&actualWindowTitle
224
+ );
225
+ if( !actualWindowTitle ||
226
+ CFStringCompare(targetWindowName, actualWindowTitle, 0) != 0)
227
+ {
228
+ continue;
229
+ }
230
+
231
+ /* Position and size must match */
232
+ actualPosition = AXWindowGetPosition(appWindow);
233
+ if(!CGPointEqualToPoint(targetPosition, actualPosition)) continue;
234
+ actualSize = AXWindowGetSize(appWindow);
235
+ if(!CGSizeEqualToSize(targetSize, actualSize)) continue;
213
236
 
214
- /* Multiple windows may match, caller chooses which match to return */
215
- if(matchIdx >= minIdx) {
216
237
  /* Found the first matching window, save it and break */
217
238
  foundAppWindow = appWindow;
218
239
  break;
219
- } else {
220
- matchIdx++;
221
240
  }
222
241
  }
223
242
  CFRelease(app);
@@ -64,7 +64,7 @@ CGSize CGWindowGetSize(CFDictionaryRef window);
64
64
  bool isAuthorized();
65
65
 
66
66
  /* Given window dictionary from CGWindowList, return accessibility object */
67
- AXUIElementRef AXWindowFromCGWindow(CFDictionaryRef window, int minIdx);
67
+ AXUIElementRef AXWindowFromCGWindow(CFDictionaryRef window);
68
68
 
69
69
  /* Get a value from an accessibility object */
70
70
  void AXWindowGetValue(
data/lib/movewin.rb CHANGED
@@ -36,7 +36,7 @@
36
36
  require 'movewin/movewin_ext'
37
37
 
38
38
  module MoveWin
39
- VERSION = '1.3'
39
+ VERSION = '1.4'
40
40
 
41
41
  # Individual accessors for display size components
42
42
  def self.display_width; MoveWin.display_size[0]; end
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: movewin
3
3
  version: !ruby/object:Gem::Version
4
- version: "1.3"
4
+ hash: 7
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 4
9
+ version: "1.4"
5
10
  platform: ruby
6
11
  authors:
7
12
  - Andrew Ho
@@ -9,7 +14,7 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2014-09-08 00:00:00 Z
17
+ date: 2014-10-30 00:00:00 Z
13
18
  dependencies: []
14
19
 
15
20
  description: List and move OS X windows from Ruby via the OS X accessibility APIs.
@@ -29,28 +34,35 @@ files:
29
34
  homepage: https://github.com/andrewgho/movewin-ruby
30
35
  licenses:
31
36
  - BSD-3-Clause
32
- metadata: {}
33
-
34
37
  post_install_message:
35
38
  rdoc_options: []
36
39
 
37
40
  require_paths:
38
41
  - lib
39
42
  required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
40
44
  requirements:
41
- - &id001
42
- - ">="
45
+ - - ">="
43
46
  - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
44
50
  version: "0"
45
51
  required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
46
53
  requirements:
47
- - *id001
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
48
60
  requirements: []
49
61
 
50
62
  rubyforge_project:
51
- rubygems_version: 2.0.5
63
+ rubygems_version: 1.8.7
52
64
  signing_key:
53
- specification_version: 4
65
+ specification_version: 3
54
66
  summary: List and move OS X windows from Ruby
55
67
  test_files: []
56
68
 
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 186d90a92e2cba238cb35a3dd4c0ca998ae39c32
4
- data.tar.gz: dbbebd61d13940f5e749697f81c0b121b9a57e5f
5
- SHA512:
6
- metadata.gz: 07394cd3f53cb282b2a606a46fc9802c5e773422f69a15b722cdf41aeea9e780ebb9d427e4ec95a1b22cf5feeb6cd582f6419a6ff39a8cf53ffbbc8868747ecd
7
- data.tar.gz: 0cd496ec1c9c9d462db9c483d7908f04fc5a4313fe55cfa6dbd7cfe705e660345c40e2de79261c8cf81963d98551439af189828ddcf905f379279ef468742175