rum 0.0.1-universal-darwin-10
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/README +30 -0
- data/Rakefile +134 -0
- data/bin/rum-client +124 -0
- data/doc/basic.rb +10 -0
- data/doc/doc.html +602 -0
- data/doc/example.rb +59 -0
- data/doc/reference.rb +415 -0
- data/doc/resources/bg.png +0 -0
- data/doc/resources/bottom.png +0 -0
- data/doc/resources/build.rb +235 -0
- data/doc/resources/doc.haml +167 -0
- data/doc/resources/emacs-auto-completion.png +0 -0
- data/doc/resources/flash.png +0 -0
- data/doc/resources/highlight.css +94 -0
- data/doc/resources/intro.rb +17 -0
- data/doc/resources/left.png +0 -0
- data/doc/resources/logo.png +0 -0
- data/doc/resources/screen.css +420 -0
- data/doc/resources/screenshot.png +0 -0
- data/doc/resources/top.png +0 -0
- data/ext/mac/keyboard_hook/English.lproj/InfoPlist.strings +0 -0
- data/ext/mac/keyboard_hook/Event.h +17 -0
- data/ext/mac/keyboard_hook/Event.m +18 -0
- data/ext/mac/keyboard_hook/EventTap.h +11 -0
- data/ext/mac/keyboard_hook/EventTap.m +77 -0
- data/ext/mac/keyboard_hook/Info.plist +26 -0
- data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/TemplateIcon.icns +0 -0
- data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/project.pbxproj +323 -0
- data/ext/mac/keyboard_hook/KeyboardHook_Prefix.pch +7 -0
- data/ext/mac/keyboard_hook/version.plist +16 -0
- data/ext/windows/keyboard_hook/extconf.rb +2 -0
- data/ext/windows/keyboard_hook/keyboard_hook.c +126 -0
- data/ext/windows/system/autohotkey_stuff.c +255 -0
- data/ext/windows/system/autohotkey_stuff.h +2 -0
- data/ext/windows/system/clipboard_watcher.c +58 -0
- data/ext/windows/system/clipboard_watcher.h +2 -0
- data/ext/windows/system/extconf.rb +3 -0
- data/ext/windows/system/input_box.c +239 -0
- data/ext/windows/system/input_box.h +4 -0
- data/ext/windows/system/system.c +273 -0
- data/lib/rum.rb +4 -0
- data/lib/rum/apps.rb +4 -0
- data/lib/rum/barrel.rb +157 -0
- data/lib/rum/barrel/emacs.rb +44 -0
- data/lib/rum/barrel/emacs_client.rb +74 -0
- data/lib/rum/core.rb +125 -0
- data/lib/rum/dsl.rb +109 -0
- data/lib/rum/gui.rb +93 -0
- data/lib/rum/help.rb +128 -0
- data/lib/rum/hotkey_core.rb +479 -0
- data/lib/rum/mac.rb +18 -0
- data/lib/rum/mac/app.rb +4 -0
- data/lib/rum/mac/apps.rb +19 -0
- data/lib/rum/mac/gui.rb +26 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Info.plist +28 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/MacOS/CocoaDialog +0 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Info.plist +28 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/InfoPlist.strings +0 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/classes.nib +51 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/info.nib +16 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/keyedobjects.nib +0 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/classes.nib +7 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/info.nib +21 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/info.nib.orig +21 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/objects.nib +0 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/objects.nib.orig +0 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/classes.nib +27 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/info.nib +16 -0
- data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/keyedobjects.nib +0 -0
- data/lib/rum/mac/gui/Growl.framework/Growl +0 -0
- data/lib/rum/mac/gui/Growl.framework/Versions/A/Growl +0 -0
- data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/Growl.h +6 -0
- data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlApplicationBridge-Carbon.h +780 -0
- data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +575 -0
- data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlDefines.h +348 -0
- data/lib/rum/mac/gui/Growl.framework/Versions/A/Resources/Info.plist +24 -0
- data/lib/rum/mac/gui/growl.rb +54 -0
- data/lib/rum/mac/irb/completion.rb +207 -0
- data/lib/rum/mac/keyboard_hook.rb +73 -0
- data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/KeyboardHook +0 -0
- data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/KeyboardHook +0 -0
- data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/English.lproj/InfoPlist.strings +0 -0
- data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/Info.plist +22 -0
- data/lib/rum/mac/layouts.rb +146 -0
- data/lib/rum/mac/system.rb +45 -0
- data/lib/rum/remote.rb +48 -0
- data/lib/rum/server.rb +92 -0
- data/lib/rum/windows.rb +23 -0
- data/lib/rum/windows/app.rb +72 -0
- data/lib/rum/windows/apps.rb +25 -0
- data/lib/rum/windows/gui.rb +116 -0
- data/lib/rum/windows/keyboard.rb +80 -0
- data/lib/rum/windows/keyboard_hook.rb +20 -0
- data/lib/rum/windows/layouts.rb +232 -0
- data/lib/rum/windows/system.rb +310 -0
- data/lib/rum/windows/system_foreign_functions.rb +129 -0
- data/rum.gemspec +14 -0
- metadata +166 -0
@@ -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,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,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
|
+
}
|