rum 0.0.1-universal-darwin-10 → 0.0.3-universal-darwin-10
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -0
- data/README.md +35 -0
- data/Rakefile +22 -19
- data/bin/rum-client +4 -4
- data/doc/basic.rb +1 -1
- data/doc/doc.html +619 -602
- data/doc/example.rb +1 -1
- data/doc/reference.rb +29 -16
- data/doc/resources/build.rb +14 -12
- data/doc/resources/doc.haml +8 -3
- data/ext/mac/keyboard_hook/EventTap.h +1 -1
- data/ext/mac/keyboard_hook/EventTap.m +9 -9
- data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/project.pbxproj +12 -9
- data/ext/windows/keyboard_hook/keyboard_hook.c +3 -3
- data/ext/windows/system/autohotkey_stuff.c +12 -10
- data/ext/windows/system/clipboard_watcher.c +2 -2
- data/ext/windows/system/extconf.rb +1 -0
- data/ext/windows/system/input_box.c +9 -9
- data/ext/windows/system/system.c +51 -46
- data/lib/rum/barrel.rb +46 -16
- data/lib/rum/barrel/emacs.rb +1 -1
- data/lib/rum/barrel/emacs_client.rb +32 -5
- data/lib/rum/core.rb +24 -22
- data/lib/rum/dsl.rb +15 -16
- data/lib/rum/gui.rb +3 -3
- data/lib/rum/help.rb +10 -8
- data/lib/rum/hotkey_core.rb +35 -25
- data/lib/rum/mac.rb +1 -1
- data/lib/rum/mac/gui.rb +1 -1
- data/lib/rum/mac/gui/growl.rb +3 -3
- data/lib/rum/mac/irb/completion.rb +13 -13
- data/lib/rum/mac/keyboard_hook.rb +8 -5
- 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 +16 -0
- data/lib/rum/mac/layouts.rb +4 -4
- data/lib/rum/mac/system.rb +2 -2
- data/lib/rum/remote.rb +3 -3
- data/lib/rum/server.rb +2 -3
- data/lib/rum/windows.rb +2 -1
- data/lib/rum/windows/app.rb +12 -7
- data/lib/rum/windows/apps.rb +3 -8
- data/lib/rum/windows/gui.rb +8 -8
- data/lib/rum/windows/keyboard.rb +34 -9
- data/lib/rum/windows/layouts.rb +6 -6
- data/lib/rum/windows/system.rb +71 -28
- data/lib/rum/windows/system_foreign_functions.rb +44 -20
- data/rum.gemspec +2 -2
- metadata +32 -53
- data/README +0 -30
@@ -20,8 +20,8 @@ static BOOL observe_messages() {
|
|
20
20
|
static LRESULT APIENTRY
|
21
21
|
clipboard_hook(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) {
|
22
22
|
HWND next_viewer = (HWND)GetWindowLongPtr(hwnd, GWL_USERDATA);
|
23
|
-
switch (umsg)
|
24
|
-
{
|
23
|
+
switch (umsg)
|
24
|
+
{
|
25
25
|
case WM_DRAWCLIPBOARD:
|
26
26
|
/* A stop signal for observe_messages() */
|
27
27
|
PostMessage(hwnd, WM_APP, 0, 0);
|
@@ -28,7 +28,7 @@ static void input_box_populate_window(InputBox *box, HWND blank_window)
|
|
28
28
|
{
|
29
29
|
NONCLIENTMETRICS metrics;
|
30
30
|
HFONT font;
|
31
|
-
|
31
|
+
|
32
32
|
box->main_window = blank_window;
|
33
33
|
|
34
34
|
box->text = CreateWindow(L"Static", L"",
|
@@ -61,7 +61,7 @@ static void input_box_populate_window(InputBox *box, HWND blank_window)
|
|
61
61
|
box->instance, NULL);
|
62
62
|
|
63
63
|
font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
64
|
-
|
64
|
+
|
65
65
|
SendMessage(box->text,WM_SETFONT,(WPARAM)font,FALSE);
|
66
66
|
SendMessage(box->edit_control,WM_SETFONT,(WPARAM)font,FALSE);
|
67
67
|
SendMessage(box->ok,WM_SETFONT,(WPARAM)font,FALSE);
|
@@ -77,7 +77,7 @@ static void input_box_submit(InputBox *box)
|
|
77
77
|
{
|
78
78
|
int input_length = (int)SendMessage(box->edit_control, EM_LINELENGTH, 0, 0);
|
79
79
|
if (input_length){
|
80
|
-
// Provide the max number of chars to be copied
|
80
|
+
// Provide the max number of chars to be copied to the buffer.
|
81
81
|
*((LPWORD)box->result_text) = box->max_chars;
|
82
82
|
// Although undocumented, the copied string is null-terminated.
|
83
83
|
input_length = (WORD)SendMessage(box->edit_control, EM_GETLINE, 0,
|
@@ -89,7 +89,7 @@ static void input_box_submit(InputBox *box)
|
|
89
89
|
LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
90
90
|
{
|
91
91
|
InputBox *box = (InputBox*)GetWindowLong(hwnd, GWL_USERDATA);
|
92
|
-
|
92
|
+
|
93
93
|
switch(msg)
|
94
94
|
{
|
95
95
|
case WM_CREATE:
|
@@ -170,7 +170,7 @@ static void input_box_create(InputBox *box, HINSTANCE instance)
|
|
170
170
|
RECT rect;
|
171
171
|
|
172
172
|
GetWindowRect(GetDesktopWindow(), &rect);
|
173
|
-
|
173
|
+
|
174
174
|
box->instance = instance;
|
175
175
|
box->main_window = CreateWindowEx(WS_EX_TOPMOST,
|
176
176
|
IB_CLASS_NAME, L"",
|
@@ -201,9 +201,9 @@ int input_box(LPCTSTR text, LPCTSTR title,
|
|
201
201
|
{
|
202
202
|
MSG msg;
|
203
203
|
InputBox box;
|
204
|
-
|
204
|
+
|
205
205
|
input_box_create(&box, global_instance);
|
206
|
-
|
206
|
+
|
207
207
|
input_box_show(&box, title, text, result_text, max_chars);
|
208
208
|
|
209
209
|
while(GetMessage(&msg, NULL, 0, 0) > 0)
|
@@ -221,11 +221,11 @@ int input_box(LPCTSTR text, LPCTSTR title,
|
|
221
221
|
} else {
|
222
222
|
TranslateMessage(&msg);
|
223
223
|
}
|
224
|
-
DispatchMessage(&msg);
|
224
|
+
DispatchMessage(&msg);
|
225
225
|
}
|
226
226
|
|
227
227
|
input_box_destroy(&box);
|
228
|
-
|
228
|
+
|
229
229
|
return(box.result);
|
230
230
|
}
|
231
231
|
|
data/ext/windows/system/system.c
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
#include <windows.h>
|
3
3
|
#include <winuser.h>
|
4
4
|
#include <tlhelp32.h>
|
5
|
+
#include <Psapi.h>
|
5
6
|
#include "autohotkey_stuff.h"
|
6
7
|
#include "input_box.h"
|
7
8
|
#include "clipboard_watcher.h"
|
@@ -36,11 +37,23 @@ static BOOL CALLBACK enum_windows_proc(HWND hwnd, LPARAM lParam)
|
|
36
37
|
return TRUE;
|
37
38
|
}
|
38
39
|
|
40
|
+
static BOOL CALLBACK enum_child_windows_proc(HWND hwnd, LPARAM lParam)
|
41
|
+
{
|
42
|
+
rb_yield(LONG2NUM((DWORD)hwnd));
|
43
|
+
return TRUE;
|
44
|
+
}
|
45
|
+
|
39
46
|
static VALUE enum_windows(VALUE self) {
|
40
47
|
EnumWindows(enum_windows_proc, 0);
|
41
48
|
return Qnil;
|
42
49
|
}
|
43
50
|
|
51
|
+
static VALUE enum_child_windows(VALUE self) {
|
52
|
+
HWND window_handle = (HWND)NUM2ULONG(rb_iv_get(self, "@handle"));
|
53
|
+
EnumChildWindows(window_handle, enum_child_windows_proc, 0);
|
54
|
+
return Qnil;
|
55
|
+
}
|
56
|
+
|
44
57
|
static void call_keybd_event(struct KeybdEventParams *event) {
|
45
58
|
#ifdef DEBUG
|
46
59
|
printf("Sending key: vkcode: %d, scancode: %d\n", event.vkcode, event.scancode);
|
@@ -66,6 +79,19 @@ static VALUE f_keybd_event(int argc, VALUE* argv, VALUE self)
|
|
66
79
|
return Qnil;
|
67
80
|
}
|
68
81
|
|
82
|
+
static VALUE f_send_unicode_char_internal(VALUE self, VALUE character)
|
83
|
+
{
|
84
|
+
INPUT inp[2];
|
85
|
+
memset(inp,0,sizeof(INPUT));
|
86
|
+
inp[0].type = INPUT_KEYBOARD;
|
87
|
+
inp[0].ki.dwFlags = KEYEVENTF_UNICODE;
|
88
|
+
inp[0].ki.wScan = *(LPCWSTR)RSTRING_PTR(character);
|
89
|
+
inp[1] = inp[0];
|
90
|
+
inp[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
91
|
+
|
92
|
+
return SendInput(2, inp, sizeof(INPUT)) ? Qtrue : Qfalse;
|
93
|
+
}
|
94
|
+
|
69
95
|
/* Returns 'true' if successful, 'false' if not. */
|
70
96
|
static VALUE ForceWindowToFront(VALUE self)
|
71
97
|
{
|
@@ -80,50 +106,28 @@ static DWORD process_id(VALUE window)
|
|
80
106
|
{
|
81
107
|
VALUE handle = rb_iv_get(window, "@handle");
|
82
108
|
HWND window_handle = (HWND)NUM2ULONG(handle);
|
83
|
-
|
109
|
+
|
84
110
|
DWORD process_id = 0;
|
85
111
|
GetWindowThreadProcessId(window_handle, &process_id);
|
86
112
|
return process_id;
|
87
113
|
}
|
88
114
|
|
89
|
-
static
|
90
|
-
DWORD processID = process_id(window);
|
91
|
-
|
92
|
-
// Take a snapshot of the modules of this process
|
93
|
-
HANDLE moduleSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);
|
94
|
-
|
95
|
-
//Get the first module
|
96
|
-
module->dwSize = sizeof(MODULEENTRY32);
|
97
|
-
Module32First(moduleSnapshot, module);
|
98
|
-
}
|
99
|
-
|
100
|
-
static void get_exe_path(MODULEENTRY32 *module, char *buffer) {
|
101
|
-
strcpy(buffer, module->szExePath);
|
102
|
-
}
|
103
|
-
|
104
|
-
static void get_exe_name(MODULEENTRY32 *module, char *buffer) {
|
105
|
-
strcpy(buffer, module->szModule);
|
106
|
-
buffer[strlen(buffer) - 4] = '\0'; /* Remove .exe suffix */
|
107
|
-
}
|
108
|
-
|
109
|
-
static void access_window_process_data(VALUE window, void fn(MODULEENTRY32 *module, char *buffer), char *buffer) {
|
110
|
-
MODULEENTRY32 module = {0};
|
111
|
-
process_module(window, &module);
|
112
|
-
(*fn)(&module, buffer);
|
113
|
-
}
|
114
|
-
|
115
|
-
static VALUE exe_name(VALUE self)
|
115
|
+
static VALUE exe_path_internal(VALUE self)
|
116
116
|
{
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
117
|
+
DWORD window_process_id = process_id(self);
|
118
|
+
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION,
|
119
|
+
FALSE,
|
120
|
+
window_process_id);
|
121
|
+
#define BUFFER_LENGTH 512
|
122
|
+
WCHAR buffer[BUFFER_LENGTH];
|
123
|
+
DWORD num_chars = GetProcessImageFileNameW(process,
|
124
|
+
buffer,
|
125
|
+
BUFFER_LENGTH);
|
126
|
+
if (num_chars) {
|
127
|
+
return rb_str_new(buffer, sizeof(WCHAR) * num_chars);
|
128
|
+
} else {
|
129
|
+
return Qnil;
|
130
|
+
}
|
127
131
|
}
|
128
132
|
|
129
133
|
static LPRECT get_window_rect(VALUE window, LPRECT rect)
|
@@ -239,35 +243,36 @@ static VALUE input_box_internal(VALUE self, VALUE text, VALUE title,
|
|
239
243
|
void Init_system() {
|
240
244
|
input_box_initialize(GetModuleHandle(NULL));
|
241
245
|
autohotkey_stuff_initialize(GetCurrentThreadId());
|
242
|
-
|
243
|
-
mSystem
|
246
|
+
|
247
|
+
mSystem = rb_define_module_under(rb_define_module("Rum"), "System");
|
244
248
|
mDesktop = rb_define_module_under(mSystem, "Desktop");
|
245
249
|
mScreen = rb_define_module_under(mSystem, "Screen");
|
246
250
|
mClipboard = rb_define_module_under(mSystem, "Clipboard");
|
247
251
|
cWindow = rb_define_class_under(mSystem, "Window", rb_cObject);
|
248
|
-
|
252
|
+
|
249
253
|
rb_define_method(mSystem, "enum_windows", enum_windows, 0);
|
250
254
|
rb_define_method(mSystem, "keybd_event", f_keybd_event, -1);
|
255
|
+
rb_define_method(mSystem, "send_unicode_char_internal", f_send_unicode_char_internal, 1);
|
251
256
|
rb_define_method(mSystem, "get_console_window", get_console_window, 0);
|
252
257
|
rb_define_method(mSystem, "message_box_internal", message_box_internal, 2);
|
253
258
|
rb_define_method(mSystem, "input_box_internal", input_box_internal, 3);
|
254
259
|
|
255
260
|
rb_define_method(cWindow, "show", ForceWindowToFront, 0);
|
256
|
-
rb_define_method(cWindow, "
|
257
|
-
rb_define_method(cWindow, "
|
261
|
+
rb_define_method(cWindow, "enum_child_windows", enum_child_windows, 0);
|
262
|
+
rb_define_method(cWindow, "exe_path_internal", exe_path_internal, 0);
|
258
263
|
rb_define_method(cWindow, "left", left, 0);
|
259
264
|
rb_define_method(cWindow, "right", right, 0);
|
260
265
|
rb_define_method(cWindow, "top", top, 0);
|
261
266
|
rb_define_method(cWindow, "bottom", bottom, 0);
|
262
|
-
|
267
|
+
|
263
268
|
rb_define_module_function(mDesktop, "top", desktop_top, 0);
|
264
269
|
rb_define_module_function(mDesktop, "left", desktop_left, 0);
|
265
270
|
rb_define_module_function(mDesktop, "bottom", desktop_bottom, 0);
|
266
271
|
rb_define_module_function(mDesktop, "right", desktop_right, 0);
|
267
|
-
|
272
|
+
|
268
273
|
rb_define_module_function(mScreen, "width", screen_width, 0);
|
269
274
|
rb_define_module_function(mScreen, "height", screen_height, 0);
|
270
|
-
|
275
|
+
|
271
276
|
rb_define_method(mClipboard, "install_watcher", install_watcher, 0);
|
272
277
|
rb_define_method(mClipboard, "evaluate_watcher", evaluate_watcher, 2);
|
273
278
|
}
|
data/lib/rum/barrel.rb
CHANGED
@@ -13,14 +13,18 @@ module Rum
|
|
13
13
|
end
|
14
14
|
paths
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def self.select dir
|
18
18
|
path = Gui.choose(nil, contents(dir))
|
19
19
|
(File.join(dir, path)) if path
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def self.run path
|
23
|
-
|
23
|
+
if File.extname(path) == '.rb'
|
24
|
+
eval(IO.read(path))
|
25
|
+
else
|
26
|
+
start path
|
27
|
+
end
|
24
28
|
end
|
25
29
|
|
26
30
|
def self.select_and_run dir
|
@@ -36,15 +40,15 @@ module Rum
|
|
36
40
|
path.gsub('\\', '/')
|
37
41
|
end
|
38
42
|
end
|
39
|
-
|
43
|
+
|
40
44
|
module AppDir
|
41
45
|
class << self
|
42
46
|
attr_accessor :base_dir
|
43
|
-
|
47
|
+
|
44
48
|
def get(exe)
|
45
49
|
File.join(@base_dir, exe, '')
|
46
50
|
end
|
47
|
-
|
51
|
+
|
48
52
|
def get_or_create(exe)
|
49
53
|
dir = get(exe)
|
50
54
|
if File.exists? dir
|
@@ -64,7 +68,7 @@ module Rum
|
|
64
68
|
|
65
69
|
def visit
|
66
70
|
dir = current
|
67
|
-
|
71
|
+
Gui.goto dir if dir
|
68
72
|
end
|
69
73
|
|
70
74
|
def select
|
@@ -76,9 +80,10 @@ module Rum
|
|
76
80
|
|
77
81
|
module Commands
|
78
82
|
class Command
|
79
|
-
def initialize(name, proc)
|
83
|
+
def initialize(name, proc, location)
|
80
84
|
@name = name
|
81
85
|
@proc = proc
|
86
|
+
@location = location
|
82
87
|
end
|
83
88
|
|
84
89
|
def to_s
|
@@ -88,8 +93,12 @@ module Rum
|
|
88
93
|
def run
|
89
94
|
@proc.call
|
90
95
|
end
|
96
|
+
|
97
|
+
def visit
|
98
|
+
@location.visit if @location
|
99
|
+
end
|
91
100
|
end
|
92
|
-
|
101
|
+
|
93
102
|
class << self
|
94
103
|
attr_accessor :default_tag
|
95
104
|
attr_accessor :commands
|
@@ -102,15 +111,20 @@ module Rum
|
|
102
111
|
tags << tag if tag
|
103
112
|
tags << default_tag
|
104
113
|
tags.uniq!
|
105
|
-
|
114
|
+
|
115
|
+
location = FileLocation.from_stack_frame(caller.first)
|
116
|
+
|
106
117
|
if args and (hotkey = args[:hotkey])
|
107
118
|
apps = tags.select { |tag| tag.is_a? App }
|
108
|
-
apps.each
|
119
|
+
apps.each do |app|
|
120
|
+
action = hotkey.do(app, &block)
|
121
|
+
action.location = location
|
122
|
+
end
|
109
123
|
end
|
110
|
-
|
111
|
-
cmd = Command.new(name, block)
|
124
|
+
|
125
|
+
cmd = Command.new(name, block, location)
|
112
126
|
tags.each do |tag|
|
113
|
-
commands_for_tag = (commands[tag] ||= {})
|
127
|
+
commands_for_tag = (@commands[tag] ||= {})
|
114
128
|
commands_for_tag[name] = cmd
|
115
129
|
end
|
116
130
|
end
|
@@ -124,10 +138,26 @@ module Rum
|
|
124
138
|
end
|
125
139
|
|
126
140
|
def select(tag=nil)
|
127
|
-
cmd =
|
141
|
+
cmd = select_command(self[tag])
|
128
142
|
cmd.run if cmd
|
129
143
|
end
|
130
144
|
|
145
|
+
TIMER_DURATION = 10
|
146
|
+
def select_command(commands)
|
147
|
+
cmd = Gui.choose(nil, commands)
|
148
|
+
if @visit_timer
|
149
|
+
timer_active = (Time.now-@visit_timer) <= TIMER_DURATION
|
150
|
+
cmd.visit if cmd and timer_active
|
151
|
+
@visit_timer = nil
|
152
|
+
else
|
153
|
+
cmd
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def visit_next_command
|
158
|
+
@visit_timer = Time.now
|
159
|
+
end
|
160
|
+
|
131
161
|
def for_active_window
|
132
162
|
cmds = []
|
133
163
|
exe = active_window.exe_name
|
@@ -136,7 +166,7 @@ module Rum
|
|
136
166
|
if (dir = AppDir.get(exe))
|
137
167
|
cmds.concat Path.contents(dir)
|
138
168
|
end
|
139
|
-
if (chosen =
|
169
|
+
if (chosen = select_command(cmds))
|
140
170
|
case chosen
|
141
171
|
when String
|
142
172
|
Path.run(dir + chosen)
|
data/lib/rum/barrel/emacs.rb
CHANGED
@@ -6,9 +6,9 @@ class << Emacs
|
|
6
6
|
Emacs.client = EmacsClient.new
|
7
7
|
|
8
8
|
def eval(elisp)
|
9
|
+
elisp = "(with-current-buffer (window-buffer) #{elisp})" if @eval_in_user_buffer
|
9
10
|
# MacRuby hack
|
10
11
|
# @client.eval fails
|
11
|
-
elisp = "(with-current-buffer (window-buffer) #{elisp})" if @eval_in_user_buffer
|
12
12
|
Emacs.client.eval(elisp)
|
13
13
|
end
|
14
14
|
|
@@ -4,16 +4,43 @@ require 'socket'
|
|
4
4
|
# emacs_source/lib-src/emacsclient.c
|
5
5
|
|
6
6
|
class EmacsClient
|
7
|
+
|
8
|
+
SERVER_MSG_SIZE = 1024 # defined in server.el on Emacs 24
|
9
|
+
|
7
10
|
def eval(elisp)
|
8
11
|
socket = connect
|
12
|
+
$socket = socket
|
9
13
|
socket.puts "-eval #{quote(elisp)}"
|
10
|
-
|
14
|
+
|
15
|
+
messages = []
|
16
|
+
# On Emacs 24, the first message always has type '-emacs-pid'
|
17
|
+
# On Emacs 23, only one message is returned after sending '-eval'.
|
18
|
+
# It has type '-print' or '-error' and can be arbitrarily long.
|
19
|
+
msg = socket.gets
|
20
|
+
loop do
|
21
|
+
break if handle_message(msg, messages) == :abort
|
22
|
+
msg = socket.recv(SERVER_MSG_SIZE)
|
23
|
+
end
|
11
24
|
socket.close
|
12
|
-
|
25
|
+
unquote(messages.join)
|
13
26
|
end
|
14
27
|
|
15
|
-
def
|
16
|
-
|
28
|
+
def handle_message(msg, messages)
|
29
|
+
return :abort if not msg or msg.empty?
|
30
|
+
|
31
|
+
if (match = msg.match(/(.*?) (.*)/))
|
32
|
+
type, body = match.captures
|
33
|
+
case type
|
34
|
+
when '-print', '-print-nonl'
|
35
|
+
messages << body
|
36
|
+
when '-error'
|
37
|
+
messages << body
|
38
|
+
return :abort
|
39
|
+
end
|
40
|
+
else
|
41
|
+
messages << "Error: Unknown message: #{msg}"
|
42
|
+
return :abort
|
43
|
+
end
|
17
44
|
end
|
18
45
|
|
19
46
|
def quote str
|
@@ -37,7 +64,7 @@ class EmacsClient
|
|
37
64
|
@server_file = File.join(ENV['HOME'], '.emacs.d', 'server', 'server')
|
38
65
|
read_config
|
39
66
|
end
|
40
|
-
|
67
|
+
|
41
68
|
def read_config
|
42
69
|
@server_active = File.exists? @server_file
|
43
70
|
return unless @server_active
|