rum 0.0.1-universal-darwin-10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) 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/CocoaDialog.app/Contents/Info.plist +28 -0
  57. data/lib/rum/mac/gui/CocoaDialog.app/Contents/MacOS/CocoaDialog +0 -0
  58. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Info.plist +28 -0
  59. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/InfoPlist.strings +0 -0
  60. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/classes.nib +51 -0
  61. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/info.nib +16 -0
  62. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Inputbox.nib/keyedobjects.nib +0 -0
  63. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/classes.nib +7 -0
  64. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/info.nib +21 -0
  65. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/info.nib.orig +21 -0
  66. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/objects.nib +0 -0
  67. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/MainMenu.nib/objects.nib.orig +0 -0
  68. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/classes.nib +27 -0
  69. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/info.nib +16 -0
  70. data/lib/rum/mac/gui/CocoaDialog.app/Contents/Resources/Msgbox.nib/keyedobjects.nib +0 -0
  71. data/lib/rum/mac/gui/Growl.framework/Growl +0 -0
  72. data/lib/rum/mac/gui/Growl.framework/Versions/A/Growl +0 -0
  73. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/Growl.h +6 -0
  74. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlApplicationBridge-Carbon.h +780 -0
  75. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +575 -0
  76. data/lib/rum/mac/gui/Growl.framework/Versions/A/Headers/GrowlDefines.h +348 -0
  77. data/lib/rum/mac/gui/Growl.framework/Versions/A/Resources/Info.plist +24 -0
  78. data/lib/rum/mac/gui/growl.rb +54 -0
  79. data/lib/rum/mac/irb/completion.rb +207 -0
  80. data/lib/rum/mac/keyboard_hook.rb +73 -0
  81. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/KeyboardHook +0 -0
  82. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/KeyboardHook +0 -0
  83. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/English.lproj/InfoPlist.strings +0 -0
  84. data/lib/rum/mac/keyboard_hook/KeyboardHook.framework/Versions/A/Resources/Info.plist +22 -0
  85. data/lib/rum/mac/layouts.rb +146 -0
  86. data/lib/rum/mac/system.rb +45 -0
  87. data/lib/rum/remote.rb +48 -0
  88. data/lib/rum/server.rb +92 -0
  89. data/lib/rum/windows.rb +23 -0
  90. data/lib/rum/windows/app.rb +72 -0
  91. data/lib/rum/windows/apps.rb +25 -0
  92. data/lib/rum/windows/gui.rb +116 -0
  93. data/lib/rum/windows/keyboard.rb +80 -0
  94. data/lib/rum/windows/keyboard_hook.rb +20 -0
  95. data/lib/rum/windows/layouts.rb +232 -0
  96. data/lib/rum/windows/system.rb +310 -0
  97. data/lib/rum/windows/system_foreign_functions.rb +129 -0
  98. data/rum.gemspec +14 -0
  99. metadata +166 -0
@@ -0,0 +1,310 @@
1
+ require_relative 'system_foreign_functions'
2
+
3
+ module Rum
4
+ module System
5
+ extend self
6
+
7
+ def send_key_event key, down
8
+ extended, id = key.id.divmod 2**9
9
+ extended = (extended == 1)
10
+ keybd_event id, down, 0, extended
11
+ end
12
+
13
+ def send_keypress key
14
+ send_key_event key, true
15
+ send_key_event key, false
16
+ end
17
+
18
+ def active_window_handles
19
+ Enumerator.new(self, :enum_windows)
20
+ end
21
+
22
+ def active_windows
23
+ Enumerator.new do |yielder|
24
+ enum_windows { |handle| yielder.yield Window.new(handle) }
25
+ end
26
+ end
27
+
28
+ def active_window
29
+ Window.new get_foreground_window
30
+ end
31
+
32
+ def terminal_window
33
+ Window.new get_console_window
34
+ end
35
+
36
+ def spawn_in_terminal(*args)
37
+ if args.delete :close_if_successful
38
+ close_window = '/c'
39
+ # if there's an error, ask for input to keep the shell window alive
40
+ args.concat %w(|| set /p name= Abort)
41
+ else
42
+ close_window = '/k'
43
+ end
44
+ if args.delete(:wait)
45
+ Rum.switch_worker_thread
46
+ wait = '/wait'
47
+ end
48
+ system('start', *wait, 'cmd', close_window, *args)
49
+ end
50
+
51
+ # returns a copy
52
+ def c_string obj
53
+ str = obj.to_s + "\0"
54
+ str.encode(Encoding::UTF_16LE)
55
+ end
56
+
57
+ def message_box message, title=''
58
+ message_box_internal(c_string(message), c_string(title))
59
+ end
60
+
61
+ def input_box text, title, default_text
62
+ buffer_size = 2048
63
+ buffer = default_text.to_s.encode Encoding::UTF_16LE
64
+ buffer.force_encoding Encoding::BINARY
65
+ if buffer.length >= buffer_size
66
+ buffer << "\0\0"
67
+ else
68
+ buffer << "\0" * (buffer_size - buffer.length)
69
+ end
70
+
71
+ length = input_box_internal(c_string(text), c_string(title), buffer)
72
+ if length == 0
73
+ ''
74
+ else
75
+ result = buffer[0, length*2]
76
+ result.force_encoding(Encoding::UTF_16LE).encode(Encoding::UTF_8)
77
+ end
78
+ end
79
+
80
+ def get_selection
81
+ Clipboard.preserve { Clipboard.get_selection }
82
+ end
83
+
84
+ class Window
85
+ include System
86
+
87
+ attr_reader :handle
88
+
89
+ def initialize(handle)
90
+ @handle = handle
91
+ end
92
+
93
+ def == other
94
+ return false unless other.respond_to? :handle
95
+ @handle == other.handle
96
+ end
97
+
98
+ def active?
99
+ self.handle == get_foreground_window
100
+ end
101
+
102
+ def close
103
+ post_message @handle, WM_SYSCOMMAND, SC_CLOSE, 0
104
+ end
105
+
106
+ def hide
107
+ show_window(@handle, SW_HIDE)
108
+ end
109
+
110
+ def zoomed?
111
+ is_zoomed(@handle) == 1 ? true : false
112
+ end
113
+
114
+ def toggle_maximized
115
+ zoomed? ? restore : maximize
116
+ end
117
+
118
+ def maximize
119
+ post_message @handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0
120
+ end
121
+
122
+ def minimize
123
+ post_message @handle, WM_SYSCOMMAND, SC_MINIMIZE, 0
124
+ end
125
+
126
+ def restore
127
+ post_message @handle, WM_SYSCOMMAND, SC_RESTORE, 0
128
+ end
129
+
130
+ def file_dialog?
131
+ class_name == '#32770'
132
+ end
133
+
134
+ def office_file_dialog?
135
+ class_name =~ /^bosa_sdm/
136
+ end
137
+
138
+ def toggle_always_on_top
139
+ window_style = get_window_long(@handle, GWL_EXSTYLE)
140
+ return if window_style == 0
141
+ topmost_or_not = if (window_style & WS_EX_TOPMOST == WS_EX_TOPMOST)
142
+ HWND_NOTOPMOST
143
+ else
144
+ HWND_TOPMOST
145
+ end
146
+ set_window_pos(@handle, topmost_or_not, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)
147
+ end
148
+
149
+ def move(x, y, width, height)
150
+ restore if zoomed?
151
+ move_window(@handle, x, y, width, height, 1)
152
+ end
153
+
154
+ def title(max_length = 1024)
155
+ buffer = "\0" * (max_length * 2)
156
+ length = get_window_text_w(@handle, buffer, buffer.length)
157
+ result = (length == 0 ? '' : buffer[0..(length * 2 - 1)])
158
+ result.force_encoding(Encoding::UTF_16LE)
159
+ result.encode(Encoding::UTF_8, invalid: :replace)
160
+ end
161
+
162
+ def class_name(max_length = 2048)
163
+ buffer = "\0" * max_length
164
+ length = get_class_name(@handle, buffer, buffer.length)
165
+ length == 0 ? '' : buffer[0..length - 1]
166
+ end
167
+
168
+ def text(max_length = 2048)
169
+ buffer = '\0' * max_length
170
+ length = send_with_buffer @handle, WM_GETTEXT, buffer.length, buffer
171
+ length == 0 ? '' : buffer[0..length - 1]
172
+ end
173
+
174
+ def child(id)
175
+ result = case id
176
+ when String
177
+ by_title = find_window_ex @handle, 0, nil, id.gsub('_', '&')
178
+ by_class = find_window_ex @handle, 0, id, nil
179
+ by_title > 0 ? by_title : by_class
180
+ when Fixnum
181
+ get_dlg_item @handle, id
182
+ else
183
+ 0
184
+ end
185
+
186
+ raise "Control '#{id}' not found" if result == 0
187
+ Window.new result
188
+ end
189
+
190
+ def kill_task
191
+ system("taskkill /F /IM #{exe_name}.exe")
192
+ end
193
+
194
+ def report
195
+ "Title: #{title}\nClass: #{class_name}"
196
+ end
197
+
198
+ def self.[] arg, class_name=nil
199
+ if arg.is_a? Hash
200
+ WindowMatcher.new(arg[:title], arg[:class_name])
201
+ else
202
+ WindowMatcher.new(arg, class_name)
203
+ end
204
+ end
205
+ end
206
+
207
+ class WindowMatcher
208
+ include System
209
+ attr_reader :specs
210
+
211
+ def initialize title=nil, class_name=nil
212
+ @specs = { title: title, class_name: class_name }
213
+ raise 'No specifications given.' unless @specs.values.any?
214
+ @specs.each do |spec, value|
215
+ if value.is_a? Regexp
216
+ @specs[spec] = Regexp.new(value.source, Regexp::IGNORECASE)
217
+ end
218
+ end
219
+ end
220
+
221
+ def comparison_method obj
222
+ if obj.is_a? Regexp then :=~ else :== end
223
+ end
224
+
225
+ def matches? window
226
+ @specs.all? do |spec, value|
227
+ not value or value.send(comparison_method(value), window.send(spec))
228
+ end
229
+ end
230
+
231
+ def active?
232
+ matches? active_window
233
+ end
234
+
235
+ def to_matcher
236
+ self
237
+ end
238
+
239
+ def [] spec
240
+ @specs[spec]
241
+ end
242
+
243
+ def find
244
+ if @specs.values.any? { |spec| spec.is_a? Regexp }
245
+ active_windows.detect { |window| matches? window }
246
+ else
247
+ handle = find_window @specs[:class_name], @specs[:title]
248
+ Window.new(handle) unless handle == 0
249
+ end
250
+ end
251
+ end
252
+
253
+ module Clipboard
254
+ extend self
255
+
256
+ def get
257
+ Win32::Clipboard.get
258
+ end
259
+
260
+ def set str
261
+ Win32::Clipboard.set str
262
+ end
263
+
264
+ def preserve
265
+ old_content = get
266
+ yield
267
+ ensure
268
+ set old_content
269
+ end
270
+
271
+ # def copy
272
+ # watcher = install_watcher
273
+ # Keyboard.type '(ctrl c)'
274
+ # evaluate_watcher(watcher, 400) # Timeout: 400 msec
275
+ # end
276
+
277
+ def wait_for_change
278
+ watcher = install_watcher
279
+ begin
280
+ yield
281
+ ensure
282
+ result = evaluate_watcher(watcher, 400)
283
+ end
284
+ result
285
+ end
286
+
287
+ def copy
288
+ wait_for_change { Keyboard.type '(ctrl c)' }
289
+ end
290
+
291
+ def paste
292
+ Keyboard.type '(ctrl v)'
293
+ end
294
+
295
+ def get_selection
296
+ Clipboard.get if Clipboard.copy
297
+ end
298
+
299
+ def append
300
+ clip = Clipboard.get
301
+ if (selection = get_selection)
302
+ clip << "\n" << selection
303
+ end
304
+ ensure
305
+ Clipboard.set clip
306
+ end
307
+ end
308
+ end
309
+ end
310
+
@@ -0,0 +1,129 @@
1
+ require_relative 'system.so'
2
+ # The C-extension defines:
3
+ # Rum::System (module methods)
4
+ # enum_windows
5
+ # keybd_event
6
+ # get_console_window
7
+ # Rum::System::Window (class instance methods)
8
+ # show
9
+ # exe_path
10
+ # ...
11
+ # Rum::System::Desktop (module methods)
12
+ # top
13
+ # right
14
+ # bottom
15
+ # left
16
+ # Rum::System::Screen (module methods)
17
+ # width
18
+ # height
19
+ # Rum::System::Clipboard (module methods)
20
+ # install_watcher
21
+ # evaluate_watcher
22
+
23
+ require 'win32/api'
24
+ require 'win32/clipboard'
25
+
26
+ module Rum
27
+ module System
28
+ def self.snake_case str
29
+ str.gsub(/([a-z])([A-Z0-9])/, '\1_\2').downcase
30
+ end
31
+
32
+ def self.def_api(function_name, parameters, return_value, rename=nil)
33
+ api = Win32::API.new function_name, parameters, return_value, 'user32'
34
+ define_method(rename || snake_case(function_name)) do |*args|
35
+ api.call *args
36
+ end
37
+ end
38
+
39
+ def_api 'FindWindow', 'PP', 'L'
40
+ def_api 'FindWindowEx', 'LLPP', 'L'
41
+ def_api 'SendMessage', 'LLLP', 'L', :send_with_buffer
42
+ def_api 'SendMessage', 'LLLL', 'L'
43
+ def_api 'PostMessage', 'LLLL', 'L'
44
+ def_api 'GetDlgItem', 'LL', 'L'
45
+ def_api 'GetWindowRect', 'LP', 'I'
46
+ def_api 'SetCursorPos', 'LL', 'I'
47
+ def_api 'mouse_event', 'LLLLL', 'V'
48
+ def_api 'IsWindow', 'L', 'L'
49
+ def_api 'IsWindowVisible', 'L', 'L'
50
+ def_api 'SetForegroundWindow', 'L', 'L'
51
+
52
+ def_api 'GetWindowLong', 'LI', 'L'
53
+ def_api 'GetForegroundWindow', 'V', 'L'
54
+ def_api 'GetClassName', 'LPI', 'I'
55
+ def_api 'GetWindowText', 'LPI', 'I'
56
+ def_api 'GetWindowTextW', 'LPI', 'I'
57
+ def_api 'ShowWindow', 'LI', 'I'
58
+ def_api 'SetForegroundWindow', 'L', 'I'
59
+ def_api 'IsIconic', 'L', 'L'
60
+ def_api 'IsZoomed', 'L', 'L'
61
+ def_api 'SetWindowPos', 'LLIIIII', 'I'
62
+ def_api 'MoveWindow', 'LIIIIB', 'L'
63
+
64
+ ShellExecute = Win32::API.new('ShellExecute', 'LPPPPI', 'L', 'shell32')
65
+ def start command, parameters=0
66
+ ShellExecute.call(0, 'open', command, parameters, 0, 1)
67
+ end
68
+
69
+ WM_COMMAND = 0x0111
70
+ WM_SYSCOMMAND = 0x0112
71
+
72
+ SC_CLOSE = 0xF060
73
+ SC_RESTORE = 0xF120;
74
+ SC_MAXIMIZE = 0xF030;
75
+ SC_MINIMIZE = 0xF020;
76
+
77
+ WM_GETTEXT = 0x000D
78
+ EM_GETSEL = 0x00B0
79
+ EM_SETSEL = 0x00B1
80
+ SW_HIDE = 0
81
+ SW_SHOWNORMAL = 1
82
+ SW_NORMAL = 1
83
+ SW_SHOWMINIMIZED = 2
84
+ SW_SHOWMAXIMIZED = 3
85
+ SW_MAXIMIZE = 3
86
+ SW_SHOWNOACTIVATE = 4
87
+ SW_SHOW = 5
88
+ SW_MINIMIZE = 6
89
+ SW_SHOWMINNOACTIVE = 7
90
+ SW_SHOWNA = 8
91
+ SW_RESTORE = 9
92
+ SW_SHOWDEFAULT = 10
93
+ SW_FORCEMINIMIZE = 11
94
+ SW_MAX = 11
95
+
96
+ SWP_NOMOVE = 2
97
+ SWP_NOSIZE = 1
98
+ HWND_TOPMOST = -1
99
+ HWND_NOTOPMOST = -2
100
+ GWL_EXSTYLE = -20
101
+ WS_EX_TOPMOST = 8
102
+ end
103
+ end
104
+
105
+ module Win32
106
+ class Clipboard
107
+ def self.get
108
+ begin
109
+ self.open
110
+ return '' unless IsClipboardFormatAvailable(UNICODETEXT)
111
+ handle = GetClipboardData(UNICODETEXT)
112
+ clip_data = 0.chr * GlobalSize(handle)
113
+ memcpy(clip_data, handle, clip_data.size)
114
+ clip_data.force_encoding(Encoding::UTF_16LE)
115
+ clip_data.encode!(Encoding::UTF_8, invalid: :replace, replace: '', \
116
+ universal_newline: true)
117
+ clip_data[ /^[^\0]*/ ]
118
+ ensure
119
+ self.close
120
+ end
121
+ end
122
+
123
+ def self.set str
124
+ data = str.encode(Encoding::UTF_16LE, crlf_newline: true)
125
+ data.force_encoding Encoding::BINARY
126
+ set_data(data, UNICODETEXT)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'rum'
5
+ spec.version = '0.0.1'
6
+ spec.author = 'nonsequitur'
7
+ spec.license = 'MIT'
8
+ spec.homepage = 'http://nonsequitur.github.com/rum'
9
+ spec.summary = 'A cross-platform Hotkey and Macro utility, running on Windows and Mac OS.'
10
+ spec.required_ruby_version = '>= 1.9.1'
11
+
12
+ spec.executables << 'rum-client'
13
+ spec.extra_rdoc_files = ['README']
14
+ end