rum 0.0.1-x86-mingw32 → 0.0.3-x86-mingw32
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.
- 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/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/keyboard_hook.so +0 -0
- data/lib/rum/windows/layouts.rb +6 -6
- data/lib/rum/windows/system.rb +71 -28
- data/lib/rum/windows/system.so +0 -0
- data/lib/rum/windows/system_foreign_functions.rb +44 -20
- data/rum.gemspec +2 -2
- metadata +19 -21
- 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
|