rum 0.0.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/CHANGELOG +3 -0
  2. data/README +30 -0
  3. data/Rakefile +134 -0
  4. data/bin/rum-client +124 -0
  5. data/doc/basic.rb +10 -0
  6. data/doc/doc.html +602 -0
  7. data/doc/example.rb +59 -0
  8. data/doc/reference.rb +415 -0
  9. data/doc/resources/bg.png +0 -0
  10. data/doc/resources/bottom.png +0 -0
  11. data/doc/resources/build.rb +235 -0
  12. data/doc/resources/doc.haml +167 -0
  13. data/doc/resources/emacs-auto-completion.png +0 -0
  14. data/doc/resources/flash.png +0 -0
  15. data/doc/resources/highlight.css +94 -0
  16. data/doc/resources/intro.rb +17 -0
  17. data/doc/resources/left.png +0 -0
  18. data/doc/resources/logo.png +0 -0
  19. data/doc/resources/screen.css +420 -0
  20. data/doc/resources/screenshot.png +0 -0
  21. data/doc/resources/top.png +0 -0
  22. data/ext/mac/keyboard_hook/English.lproj/InfoPlist.strings +0 -0
  23. data/ext/mac/keyboard_hook/Event.h +17 -0
  24. data/ext/mac/keyboard_hook/Event.m +18 -0
  25. data/ext/mac/keyboard_hook/EventTap.h +11 -0
  26. data/ext/mac/keyboard_hook/EventTap.m +77 -0
  27. data/ext/mac/keyboard_hook/Info.plist +26 -0
  28. data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/TemplateIcon.icns +0 -0
  29. data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/project.pbxproj +323 -0
  30. data/ext/mac/keyboard_hook/KeyboardHook_Prefix.pch +7 -0
  31. data/ext/mac/keyboard_hook/version.plist +16 -0
  32. data/ext/windows/keyboard_hook/extconf.rb +2 -0
  33. data/ext/windows/keyboard_hook/keyboard_hook.c +126 -0
  34. data/ext/windows/system/autohotkey_stuff.c +255 -0
  35. data/ext/windows/system/autohotkey_stuff.h +2 -0
  36. data/ext/windows/system/clipboard_watcher.c +58 -0
  37. data/ext/windows/system/clipboard_watcher.h +2 -0
  38. data/ext/windows/system/extconf.rb +3 -0
  39. data/ext/windows/system/input_box.c +239 -0
  40. data/ext/windows/system/input_box.h +4 -0
  41. data/ext/windows/system/system.c +273 -0
  42. data/lib/rum.rb +4 -0
  43. data/lib/rum/apps.rb +4 -0
  44. data/lib/rum/barrel.rb +157 -0
  45. data/lib/rum/barrel/emacs.rb +44 -0
  46. data/lib/rum/barrel/emacs_client.rb +74 -0
  47. data/lib/rum/core.rb +125 -0
  48. data/lib/rum/dsl.rb +109 -0
  49. data/lib/rum/gui.rb +93 -0
  50. data/lib/rum/help.rb +128 -0
  51. data/lib/rum/hotkey_core.rb +479 -0
  52. data/lib/rum/mac.rb +18 -0
  53. data/lib/rum/mac/app.rb +4 -0
  54. data/lib/rum/mac/apps.rb +19 -0
  55. data/lib/rum/mac/gui.rb +26 -0
  56. data/lib/rum/mac/gui/growl.rb +54 -0
  57. data/lib/rum/mac/irb/completion.rb +207 -0
  58. data/lib/rum/mac/keyboard_hook.rb +73 -0
  59. data/lib/rum/mac/layouts.rb +146 -0
  60. data/lib/rum/mac/system.rb +45 -0
  61. data/lib/rum/remote.rb +48 -0
  62. data/lib/rum/server.rb +92 -0
  63. data/lib/rum/windows.rb +23 -0
  64. data/lib/rum/windows/app.rb +72 -0
  65. data/lib/rum/windows/apps.rb +25 -0
  66. data/lib/rum/windows/gui.rb +116 -0
  67. data/lib/rum/windows/keyboard.rb +80 -0
  68. data/lib/rum/windows/keyboard_hook.rb +20 -0
  69. data/lib/rum/windows/keyboard_hook.so +0 -0
  70. data/lib/rum/windows/layouts.rb +232 -0
  71. data/lib/rum/windows/system.rb +310 -0
  72. data/lib/rum/windows/system.so +0 -0
  73. data/lib/rum/windows/system_foreign_functions.rb +129 -0
  74. data/rum.gemspec +14 -0
  75. metadata +156 -0
@@ -0,0 +1,7 @@
1
+ //
2
+ // Prefix header for all source files of the 'KeyboardHook' target in the 'KeyboardHook' project.
3
+ //
4
+
5
+ #ifdef __OBJC__
6
+ #import <Cocoa/Cocoa.h>
7
+ #endif
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>BuildVersion</key>
6
+ <string>3</string>
7
+ <key>CFBundleVersion</key>
8
+ <string>1.0</string>
9
+ <key>ProductBuildVersion</key>
10
+ <string>9M2729</string>
11
+ <key>ProjectName</key>
12
+ <string>DevToolsWizardTemplates</string>
13
+ <key>SourceVersion</key>
14
+ <string>11600000</string>
15
+ </dict>
16
+ </plist>
@@ -0,0 +1,2 @@
1
+ require 'mkmf'
2
+ create_makefile("keyboard_hook")
@@ -0,0 +1,126 @@
1
+ #include "ruby.h"
2
+ #include <windows.h>
3
+ #include <stdio.h>
4
+ /* #define DEBUG */
5
+
6
+ VALUE mKeyboardHook;
7
+ VALUE cEvent;
8
+ VALUE ruby_callback_proc;
9
+ int id_call;
10
+ HHOOK keyboard_hook;
11
+ BOOL in_ruby_level = 0;
12
+ DWORD thread=0;
13
+ struct KeyEvent {
14
+ int vkcode;
15
+ int scancode;
16
+ BOOL down;
17
+ BOOL injected;
18
+ };
19
+
20
+ static BOOL pass_key_event_to_ruby(struct KeyEvent *key_event) {
21
+ VALUE event_attributes[] = { INT2NUM(key_event->vkcode),
22
+ INT2NUM(key_event->scancode),
23
+ (key_event->down ? Qtrue : Qfalse) };
24
+ VALUE event = rb_class_new_instance(3, event_attributes, cEvent);
25
+
26
+ return (Qtrue == rb_funcall(ruby_callback_proc, id_call, 1, event));
27
+ }
28
+
29
+ static LRESULT CALLBACK
30
+ callback_function(int Code, WPARAM wParam, LPARAM lParam)
31
+ {
32
+ PKBDLLHOOKSTRUCT kbd = (PKBDLLHOOKSTRUCT)lParam;
33
+ BOOL pass;
34
+ struct KeyEvent key_event;
35
+
36
+ #ifdef DEBUG
37
+ puts("Enter Callback");
38
+ #endif
39
+
40
+ key_event.vkcode = kbd->vkCode;
41
+ key_event.scancode = kbd->scanCode;
42
+ key_event.down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
43
+ key_event.injected = (kbd->flags & 0x10);
44
+
45
+ #ifdef DEBUG
46
+ printf(key_event.down ? "Down" : "Up");
47
+ printf(": Vkcode: %d, Scancode: %d\n", kbd->vkCode, kbd->scanCode);
48
+ #endif
49
+
50
+ if (Code < 0) {
51
+ #ifdef DEBUG
52
+ puts("Skipping event.");
53
+ #endif
54
+ pass=1;
55
+ }
56
+ else if (key_event.injected) {
57
+ #ifdef DEBUG
58
+ puts("Injected. Skipping event.");
59
+ #endif
60
+ pass=1;
61
+ }
62
+ else if (in_ruby_level) {
63
+ #ifdef DEBUG
64
+ puts("Already in Ruby level. Pass event to Ruby.");
65
+ #endif
66
+ pass=pass_key_event_to_ruby(&key_event);
67
+ }
68
+ else {
69
+ #ifdef DEBUG
70
+ puts("Pass event to Ruby.");
71
+ #endif
72
+ in_ruby_level = 1;
73
+ pass=rb_thread_call_with_gvl(pass_key_event_to_ruby, &key_event);
74
+ in_ruby_level = 0;
75
+ }
76
+ #ifdef DEBUG
77
+ puts("Exit Callback\n\n");
78
+ #endif
79
+ if (pass)
80
+ return CallNextHookEx(keyboard_hook, Code, wParam, lParam);
81
+ else
82
+ return 1;
83
+ }
84
+
85
+ static void pump_messages() {
86
+ MSG msg;
87
+ while (GetMessage(&msg, 0, 0, 0) == 1) {
88
+ TranslateMessage(&msg);
89
+ DispatchMessage(&msg);
90
+ }
91
+ /* Missing: Error handling when GetMessage returns -1 */
92
+ }
93
+
94
+ static VALUE start(int argc, VALUE* argv, VALUE self)
95
+ {
96
+ HMODULE module = GetModuleHandle(NULL);
97
+ thread = GetCurrentThreadId();
98
+ rb_scan_args(argc, argv, "0&", &ruby_callback_proc);
99
+
100
+ keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)callback_function,
101
+ module, 0);
102
+ rb_thread_blocking_region(pump_messages, 0, 0, 0);
103
+ UnhookWindowsHookEx(keyboard_hook);
104
+ return self;
105
+ }
106
+
107
+ static VALUE stop(VALUE self)
108
+ {
109
+ if (thread) {
110
+ PostThreadMessage(thread, WM_QUIT, 0, 0);
111
+ thread = 0;
112
+ return Qtrue;
113
+ } else {
114
+ return Qfalse;
115
+ }
116
+ }
117
+
118
+ void Init_keyboard_hook() {
119
+ mKeyboardHook = rb_define_module("KeyboardHook");
120
+ rb_define_module_function(mKeyboardHook, "start", start, -1);
121
+ rb_define_module_function(mKeyboardHook, "stop", stop, 0);
122
+ id_call = rb_intern("call");
123
+ cEvent = rb_const_get(rb_const_get(rb_cObject, rb_intern("KeyboardHook")),
124
+ rb_intern("Event"));
125
+ rb_global_variable(&ruby_callback_proc);
126
+ }
@@ -0,0 +1,255 @@
1
+ #include <windows.h>
2
+
3
+ DWORD g_MainThreadID;
4
+
5
+ void autohotkey_stuff_initialize(DWORD main_thread_id)
6
+ {
7
+ g_MainThreadID = main_thread_id;
8
+ }
9
+
10
+ HWND AttemptSetForeground(HWND aTargetWindow, HWND aForeWindow)
11
+ {
12
+
13
+ BOOL result = SetForegroundWindow(aTargetWindow);
14
+ HWND new_fore_window = GetForegroundWindow();
15
+ Sleep(10);
16
+
17
+ if (new_fore_window == aTargetWindow)
18
+ {
19
+ #ifdef _DEBUG_WINACTIVATE
20
+ if (!result)
21
+ {
22
+ FileAppend(LOGF, "SetForegroundWindow() indicated failure even though it succeeded: ", false);
23
+ FileAppend(LOGF, aTargetTitle);
24
+ }
25
+ #endif
26
+ return aTargetWindow;
27
+ }
28
+ if (new_fore_window != aForeWindow && aTargetWindow == GetWindow(new_fore_window, GW_OWNER))
29
+ // The window we're trying to get to the foreground is the owner of the new foreground window.
30
+ // This is considered to be a success because a window that owns other windows can never be
31
+ // made the foreground window, at least if the windows it owns are visible.
32
+ return new_fore_window;
33
+ // Otherwise, failure:
34
+ #ifdef _DEBUG_WINACTIVATE
35
+ if (result)
36
+ {
37
+ FileAppend(LOGF, "SetForegroundWindow() indicated success even though it failed: ", false);
38
+ FileAppend(LOGF, aTargetTitle);
39
+ }
40
+ #endif
41
+ return NULL;
42
+ }
43
+
44
+
45
+
46
+ HWND SetForegroundWindowEx(HWND aTargetWindow)
47
+ // Caller must have ensured that aTargetWindow is a valid window or NULL, since we
48
+ // don't call IsWindow() here.
49
+ {
50
+ DWORD target_thread;
51
+ HWND orig_foreground_wnd;
52
+ HWND new_foreground_wnd;
53
+ int is_attached_my_to_fore;
54
+ int is_attached_fore_to_target;
55
+ DWORD fore_thread;
56
+ int i;
57
+ int show_mode;
58
+
59
+ if (!aTargetWindow)
60
+ return NULL ; // When called this way (as it is sometimes), do nothing.
61
+
62
+ // v1.0.42.03: Calling IsWindowHung() once here rather than potentially more than once in AttemptSetForeground()
63
+ // solves a crash that is not fully understood, nor is it easily reproduced (it occurs only in release mode,
64
+ // not debug mode). It's likely a bug in the API's IsHungAppWindow(), but that is far from confirmed.
65
+ target_thread = GetWindowThreadProcessId(aTargetWindow, NULL) ;
66
+ /* if (target_thread != g_MainThreadID && IsWindowHung(aTargetWindow)) // Calls to IsWindowHung should probably be avoided if the window belongs to our thread. Relies upon short-circuit boolean order. */
67
+ /* return NULL ; */
68
+
69
+ orig_foreground_wnd = GetForegroundWindow() ;
70
+ // AutoIt3: If there is not any foreground window, then input focus is on the TaskBar.
71
+ // MY: It is definitely possible for GetForegroundWindow() to return NULL, even on XP.
72
+ if (!orig_foreground_wnd)
73
+ orig_foreground_wnd = FindWindow("Shell_TrayWnd", NULL) ;
74
+
75
+ if (aTargetWindow == orig_foreground_wnd) // It's already the active window.
76
+ return aTargetWindow ;
77
+
78
+ if (IsIconic(aTargetWindow))
79
+ show_mode = SW_RESTORE;
80
+ else
81
+ show_mode = SW_SHOW;
82
+ ShowWindow(aTargetWindow, show_mode);
83
+
84
+
85
+
86
+ // if (g_os.IsWin95() || (!g_os.IsWin9x() && !g_os.IsWin2000orLater()))) // Win95 or NT
87
+ // Try a simple approach first for these two OS's, since they don't have
88
+ // any restrictions on focus stealing:
89
+ #ifdef _DEBUG_WINACTIVATE
90
+ #define IF_ATTEMPT_SET_FORE if (new_foreground_wnd = AttemptSetForeground(aTargetWindow, orig_foreground_wnd, win_name))
91
+ #else
92
+ #define IF_ATTEMPT_SET_FORE if (new_foreground_wnd = AttemptSetForeground(aTargetWindow, orig_foreground_wnd))
93
+ #endif
94
+ IF_ATTEMPT_SET_FORE
95
+ return new_foreground_wnd ;
96
+ // Otherwise continue with the more drastic methods below.
97
+
98
+ // MY: The AttachThreadInput method, when used by itself, seems to always
99
+ // work the first time on my XP system, seemingly regardless of whether the
100
+ // "allow focus steal" change has been made via SystemParametersInfo()
101
+ // (but it seems a good idea to keep the SystemParametersInfo() in effect
102
+ // in case Win2k or Win98 needs it, or in case it really does help in rare cases).
103
+ // In many cases, this avoids the two SetForegroundWindow() attempts that
104
+ // would otherwise be needed ; and those two attempts cause some windows
105
+ // to flash in the taskbar, such as Metapad and Excel (less frequently) whenever
106
+ // you quickly activate another window after activating it first (e.g. via hotkeys).
107
+ // So for now, it seems best just to use this method by itself. The
108
+ // "two-alts" case never seems to fire on my system? Maybe it will
109
+ // on Win98 sometimes.
110
+ // Note: In addition to the "taskbar button flashing" annoyance mentioned above
111
+ // any SetForegroundWindow() attempt made prior to the one below will,
112
+ // as a side-effect, sometimes trigger the need for the "two-alts" case
113
+ // below. So that's another reason to just keep it simple and do it this way
114
+ // only.
115
+
116
+ #ifdef _DEBUG_WINACTIVATE
117
+ char buf[1024] ;
118
+ #endif
119
+
120
+ is_attached_my_to_fore = 0;
121
+ is_attached_fore_to_target = 0;
122
+ if (orig_foreground_wnd) // Might be NULL from above.
123
+ {
124
+ // Based on MSDN docs, these calls should always succeed due to the other
125
+ // checks done above (e.g. that none of the HWND's are NULL):
126
+ fore_thread = GetWindowThreadProcessId(orig_foreground_wnd, NULL) ;
127
+
128
+ // MY: Normally, it's suggested that you only need to attach the thread of the
129
+ // foreground window to our thread. However, I've confirmed that doing all three
130
+ // attaches below makes the attempt much more likely to succeed. In fact, it
131
+ // almost always succeeds whereas the one-attach method hardly ever succeeds the first
132
+ // time (resulting in a flashing taskbar button due to having to invoke a second attempt)
133
+ // when one window is quickly activated after another was just activated.
134
+ // AutoIt3: Attach all our input threads, will cause SetForeground to work under 98/Me.
135
+ // MSDN docs: The AttachThreadInput function fails if either of the specified threads
136
+ // does not have a message queue (My: ok here, since any window's thread MUST have a
137
+ // message queue). [It] also fails if a journal record hook is installed. ... Note
138
+ // that key state, which can be ascertained by calls to the GetKeyState or
139
+ // GetKeyboardState function, is reset after a call to AttachThreadInput. You cannot
140
+ // attach a thread to a thread in another desktop. A thread cannot attach to itself.
141
+ // Therefore, idAttachTo cannot equal idAttach. Update: It appears that of the three,
142
+ // this first call does not offer any additional benefit, at least on XP, so not
143
+ // using it for now:
144
+ //if (g_MainThreadID != target_thread) // Don't attempt the call otherwise.
145
+ // AttachThreadInput(g_MainThreadID, target_thread, TRUE) ;
146
+ if (fore_thread && g_MainThreadID != fore_thread)
147
+ is_attached_my_to_fore = AttachThreadInput(g_MainThreadID, fore_thread, TRUE) != 0 ;
148
+ if (fore_thread && target_thread && fore_thread != target_thread) // IsWindowHung(aTargetWindow) was called earlier.
149
+ is_attached_fore_to_target = AttachThreadInput(fore_thread, target_thread, TRUE) != 0 ;
150
+ }
151
+
152
+ // The log showed that it never seemed to need more than two tries. But there's
153
+ // not much harm in trying a few extra times. The number of tries needed might
154
+ // vary depending on how fast the CPU is:
155
+ for (i = 0; i < 5; ++i)
156
+ {
157
+ IF_ATTEMPT_SET_FORE
158
+ {
159
+ #ifdef _DEBUG_WINACTIVATE
160
+ if (i > 0) // More than one attempt was needed.
161
+ {
162
+ snprintf(buf, sizeof(buf), "AttachThreadInput attempt #%d indicated success: %s"
163
+ , i + 1, win_name);
164
+ FileAppend(LOGF, buf);
165
+ }
166
+ #endif
167
+ break;
168
+ }
169
+ }
170
+
171
+ // I decided to avoid the quick minimize + restore method of activation. It's
172
+ // not that much more effective (if at all), and there are some significant
173
+ // disadvantages:
174
+ // - This call will often hang our thread if aTargetWindow is a hung window: ShowWindow(aTargetWindow, SW_MINIMIZE)
175
+ // - Using SW_FORCEMINIMIZE instead of SW_MINIMIZE has at least one (and probably more)
176
+ // side effect: When the window is restored, at least via SW_RESTORE, it is no longer
177
+ // maximized even if it was before the minmize. So don't use it.
178
+ if (!new_foreground_wnd) // Not successful yet.
179
+ {
180
+ // Some apps may be intentionally blocking us by having called the API function
181
+ // LockSetForegroundWindow(), for which MSDN says "The system automatically enables
182
+ // calls to SetForegroundWindow if the user presses the ALT key or takes some action
183
+ // that causes the system itself to change the foreground window (for example,
184
+ // clicking a background window)." Also, it's probably best to avoid doing
185
+ // the 2-alts method except as a last resort, because I think it may mess up
186
+ // the state of menus the user had displayed. And of course if the foreground
187
+ // app has special handling for alt-key events, it might get confused.
188
+ // My original note: "The 2-alts case seems to mess up on rare occasions,
189
+ // perhaps due to menu weirdness triggered by the alt key."
190
+ // AutoIt3: OK, this is not funny - bring out the extreme measures (usually for 2000/XP).
191
+ // Simulate two single ALT keystrokes. UPDATE: This hardly ever succeeds. Usually when
192
+ // it fails, the foreground window is NULL (none). I'm going to try an Win-tab instead,
193
+ // which selects a task bar button. This seems less invasive than doing an alt-tab
194
+ // because not only doesn't it activate some other window first, it also doesn't appear
195
+ // to change the Z-order, which is good because we don't want the alt-tab order
196
+ // that the user sees to be affected by this. UPDATE: Win-tab isn't doing it, so try
197
+ // Alt-tab. Alt-tab doesn't do it either. The window itself (metapad.exe is the only
198
+ // culprit window I've found so far) seems to resist being brought to the foreground,
199
+ // but later, after the hotkey is released, it can be. So perhaps this is being
200
+ // caused by the fact that the user has keys held down (logically or physically?)
201
+ // Releasing those keys with a key-up event might help, so try that sometime:
202
+ /* Remarks from Rum: The following two lines are disabled since KeyEvent
203
+ is not implemented: */
204
+ /* KeyEvent(KEYDOWNANDUP, VK_MENU); */
205
+ /* KeyEvent(KEYDOWNANDUP, VK_MENU); */
206
+ //KeyEvent(KEYDOWN, VK_LWIN) ;
207
+ //KeyEvent(KEYDOWN, VK_TAB) ;
208
+ //KeyEvent(KEYUP, VK_TAB) ;
209
+ //KeyEvent(KEYUP, VK_LWIN) ;
210
+ //KeyEvent(KEYDOWN, VK_MENU) ;
211
+ //KeyEvent(KEYDOWN, VK_TAB) ;
212
+ //KeyEvent(KEYUP, VK_TAB) ;
213
+ //KeyEvent(KEYUP, VK_MENU) ;
214
+ // Also replacing "2-alts" with "alt-tab" below, for now:
215
+
216
+ #ifndef _DEBUG_WINACTIVATE
217
+ new_foreground_wnd = AttemptSetForeground(aTargetWindow, orig_foreground_wnd) ;
218
+ #else // debug mode
219
+ IF_ATTEMPT_SET_FORE
220
+ FileAppend(LOGF, "2-alts ok: ", false) ;
221
+ else
222
+ {
223
+ FileAppend(LOGF, "2-alts (which is the last resort) failed. ", false) ;
224
+ HWND h = GetForegroundWindow() ;
225
+ if (h)
226
+ {
227
+ char fore_name[64] ;
228
+ GetWindowText(h, fore_name, sizeof(fore_name)) ;
229
+ FileAppend(LOGF, "Foreground: ", false) ;
230
+ FileAppend(LOGF, fore_name, false) ;
231
+ }
232
+ FileAppend(LOGF, ". Was trying to activate: ", false) ;
233
+ }
234
+ FileAppend(LOGF, win_name) ;
235
+ #endif
236
+ } // if()
237
+
238
+ // Very important to detach any threads whose inputs were attached above,
239
+ // prior to returning, otherwise the next attempt to attach thread inputs
240
+ // for these particular windows may result in a hung thread or other
241
+ // undesirable effect:
242
+ if (is_attached_my_to_fore)
243
+ AttachThreadInput(g_MainThreadID, fore_thread, FALSE) ;
244
+ if (is_attached_fore_to_target)
245
+ AttachThreadInput(fore_thread, target_thread, FALSE) ;
246
+
247
+ if (new_foreground_wnd) // success.
248
+ {
249
+ BringWindowToTop(aTargetWindow) ;
250
+ return new_foreground_wnd ; // Return this rather than aTargetWindow because it's more appropriate.
251
+ }
252
+ else
253
+ return NULL ;
254
+ }
255
+
@@ -0,0 +1,2 @@
1
+ HWND SetForegroundWindowEx(HWND aTargetWindow);
2
+ void autohotkey_stuff_initialize(DWORD main_thread_id);
@@ -0,0 +1,58 @@
1
+ #include "ruby.h"
2
+ #include <windows.h>
3
+
4
+ static BOOL observe_messages() {
5
+ MSG msg;
6
+ while (GetMessage(&msg, 0, 0, 0) == 1) {
7
+ switch (msg.message)
8
+ {
9
+ case WM_APP:
10
+ return 1;
11
+ case WM_TIMER:
12
+ return 0;
13
+ }
14
+ TranslateMessage(&msg);
15
+ DispatchMessage(&msg);
16
+ }
17
+ return 0;
18
+ }
19
+
20
+ static LRESULT APIENTRY
21
+ clipboard_hook(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) {
22
+ HWND next_viewer = (HWND)GetWindowLongPtr(hwnd, GWL_USERDATA);
23
+ switch (umsg)
24
+ {
25
+ case WM_DRAWCLIPBOARD:
26
+ /* A stop signal for observe_messages() */
27
+ PostMessage(hwnd, WM_APP, 0, 0);
28
+ if (next_viewer != 0)
29
+ PostMessage(next_viewer, umsg, wparam, lparam);
30
+ return 0;
31
+ case WM_CHANGECBCHAIN:
32
+ if ((HWND)wparam == next_viewer)
33
+ SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR)lparam);
34
+ else if (next_viewer != 0)
35
+ PostMessage(next_viewer, umsg, wparam, lparam);
36
+ return 0;
37
+ default:
38
+ return DefWindowProc(hwnd, umsg, wparam, lparam);
39
+ }
40
+ }
41
+
42
+ VALUE install_watcher(VALUE self)
43
+ {
44
+ HWND hwnd = (HWND)CreateWindow("static", "rum-clipboard-watcher",
45
+ 0, 0, 0, 0, 0, 0, 0, 0, 0);
46
+ SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR)SetClipboardViewer(hwnd));
47
+ SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)clipboard_hook);
48
+ return LONG2NUM((ULONG_PTR)hwnd);
49
+ }
50
+
51
+ VALUE evaluate_watcher(VALUE self, VALUE window, VALUE timeout)
52
+ {
53
+ HWND hwnd = (HWND)NUM2ULONG(window);
54
+ UINT timer = SetTimer(hwnd, 1, NUM2INT(timeout), 0);
55
+ BOOL result = rb_thread_blocking_region(observe_messages, 0, 0, 0);
56
+ DestroyWindow(hwnd); /* The timer is destroyed along with the window */
57
+ return(result ? Qtrue : Qfalse);
58
+ }