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.
Files changed (51) hide show
  1. data/CHANGELOG +9 -0
  2. data/README.md +35 -0
  3. data/Rakefile +22 -19
  4. data/bin/rum-client +4 -4
  5. data/doc/basic.rb +1 -1
  6. data/doc/doc.html +619 -602
  7. data/doc/example.rb +1 -1
  8. data/doc/reference.rb +29 -16
  9. data/doc/resources/build.rb +14 -12
  10. data/doc/resources/doc.haml +8 -3
  11. data/ext/mac/keyboard_hook/EventTap.h +1 -1
  12. data/ext/mac/keyboard_hook/EventTap.m +9 -9
  13. data/ext/mac/keyboard_hook/KeyboardHook.xcodeproj/project.pbxproj +12 -9
  14. data/ext/windows/keyboard_hook/keyboard_hook.c +3 -3
  15. data/ext/windows/system/autohotkey_stuff.c +12 -10
  16. data/ext/windows/system/clipboard_watcher.c +2 -2
  17. data/ext/windows/system/extconf.rb +1 -0
  18. data/ext/windows/system/input_box.c +9 -9
  19. data/ext/windows/system/system.c +51 -46
  20. data/lib/rum/barrel.rb +46 -16
  21. data/lib/rum/barrel/emacs.rb +1 -1
  22. data/lib/rum/barrel/emacs_client.rb +32 -5
  23. data/lib/rum/core.rb +24 -22
  24. data/lib/rum/dsl.rb +15 -16
  25. data/lib/rum/gui.rb +3 -3
  26. data/lib/rum/help.rb +10 -8
  27. data/lib/rum/hotkey_core.rb +35 -25
  28. data/lib/rum/mac.rb +1 -1
  29. data/lib/rum/mac/gui.rb +1 -1
  30. data/lib/rum/mac/gui/growl.rb +3 -3
  31. data/lib/rum/mac/irb/completion.rb +13 -13
  32. data/lib/rum/mac/keyboard_hook.rb +8 -5
  33. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/KeyboardHook +0 -0
  34. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/KeyboardHook +0 -0
  35. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/English.lproj/InfoPlist.strings +0 -0
  36. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/Info.plist +16 -0
  37. data/lib/rum/mac/layouts.rb +4 -4
  38. data/lib/rum/mac/system.rb +2 -2
  39. data/lib/rum/remote.rb +3 -3
  40. data/lib/rum/server.rb +2 -3
  41. data/lib/rum/windows.rb +2 -1
  42. data/lib/rum/windows/app.rb +12 -7
  43. data/lib/rum/windows/apps.rb +3 -8
  44. data/lib/rum/windows/gui.rb +8 -8
  45. data/lib/rum/windows/keyboard.rb +34 -9
  46. data/lib/rum/windows/layouts.rb +6 -6
  47. data/lib/rum/windows/system.rb +71 -28
  48. data/lib/rum/windows/system_foreign_functions.rb +44 -20
  49. data/rum.gemspec +2 -2
  50. metadata +32 -53
  51. 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);
@@ -1,3 +1,4 @@
1
1
  require 'mkmf'
2
2
  $LIBS << ' gdi32.lib' # needed for input_box.c
3
+ $LIBS << ' Psapi.lib' # for GetProcessImageFileName
3
4
  create_makefile("system")
@@ -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 as a word in the buffer.
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
 
@@ -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 void process_module(VALUE window, MODULEENTRY32 *module) {
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
- char buffer[MAX_MODULE_NAME32 + 1];
118
- access_window_process_data(self, &get_exe_name, buffer);
119
- return rb_str_new2(buffer);
120
- }
121
-
122
- static VALUE exe_path(VALUE self)
123
- {
124
- char buffer[MAX_PATH];
125
- access_window_process_data(self, &get_exe_path, buffer);
126
- return rb_str_new2(buffer);
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 = rb_define_module_under(rb_define_module("Rum"), "System");
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, "exe_path", exe_path, 0);
257
- rb_define_method(cWindow, "exe_name", exe_name, 0);
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
  }
@@ -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
- start path
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
- Dopus.go dir if dir
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 { |app| hotkey.do(app, &block) }
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 = Gui.choose(nil, self[tag])
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 = Gui.choose(nil, cmds))
169
+ if (chosen = select_command(cmds))
140
170
  case chosen
141
171
  when String
142
172
  Path.run(dir + chosen)
@@ -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
- result = unquote(socket.read)
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
- format(result)
25
+ unquote(messages.join)
13
26
  end
14
27
 
15
- def format(str)
16
- str[/.*? (.*)/, 1]
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