rum 0.0.1-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) 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/growl.rb +54 -0
  57. data/lib/rum/mac/irb/completion.rb +207 -0
  58. data/lib/rum/mac/keyboard_hook.rb +73 -0
  59. data/lib/rum/mac/layouts.rb +146 -0
  60. data/lib/rum/mac/system.rb +45 -0
  61. data/lib/rum/remote.rb +48 -0
  62. data/lib/rum/server.rb +92 -0
  63. data/lib/rum/windows.rb +23 -0
  64. data/lib/rum/windows/app.rb +72 -0
  65. data/lib/rum/windows/apps.rb +25 -0
  66. data/lib/rum/windows/gui.rb +116 -0
  67. data/lib/rum/windows/keyboard.rb +80 -0
  68. data/lib/rum/windows/keyboard_hook.rb +20 -0
  69. data/lib/rum/windows/keyboard_hook.so +0 -0
  70. data/lib/rum/windows/layouts.rb +232 -0
  71. data/lib/rum/windows/system.rb +310 -0
  72. data/lib/rum/windows/system.so +0 -0
  73. data/lib/rum/windows/system_foreign_functions.rb +129 -0
  74. data/rum.gemspec +14 -0
  75. metadata +156 -0
@@ -0,0 +1,20 @@
1
+ module KeyboardHook
2
+ class Event
3
+ attr_reader :id, :scancode
4
+ def initialize(id, scancode, down)
5
+ @id = id
6
+ @scancode = scancode
7
+ @down = down
8
+ end
9
+
10
+ def down?
11
+ @down
12
+ end
13
+
14
+ def to_s
15
+ "#{@down ? 'Down' : 'Up'}: VKCode #@id, Scancode #@scancode"
16
+ end
17
+ end
18
+ end
19
+
20
+ require_relative 'keyboard_hook.so'
@@ -0,0 +1,232 @@
1
+ # encoding: utf-8
2
+
3
+ module Rum
4
+ module Layouts
5
+ module_function
6
+
7
+ def default_layout
8
+ us
9
+ end
10
+
11
+ def core
12
+ # http://msdn.microsoft.com/en-us/library/dd375731%28VS.85%29.aspx
13
+ core = Layout.new
14
+ # 0-9
15
+ 10.times { |i| core.add i.to_s, 48+i }
16
+ # a-z
17
+ 26.times { |i| core.add (97+i).chr, 65+i }
18
+ # f1-f24
19
+ 1.upto(24) { |i| core.add "f#{i}", 111+i }
20
+
21
+ [['lbutton', 0x01],
22
+ ['rbutton', 0x02],
23
+ ['cancel', 0x03],
24
+ ['mbutton', 0x04],
25
+ ['back', 0x08],
26
+ ['tab', 0x09],
27
+ ['clear', 0x0C],
28
+ ['return', "\n", 0x0D],
29
+ ['shift', 0x10],
30
+ ['control', 0x11],
31
+ ['menu', 0x12],
32
+ ['pause', 0x13],
33
+ ['capital', 0x14],
34
+ ['hangul', 0x15],
35
+ ['junja', 0x17],
36
+ ['final', 0x18],
37
+ ['hanja', 0x19],
38
+ ['kanji', 0x19],
39
+ ['escape', 0x1B],
40
+ ['convert', 0x1C],
41
+ ['nonconvert', 0x1D],
42
+ ['accept', 0x1E],
43
+ ['modechange', 0x1F],
44
+ ['space', ' ', 0x20],
45
+ ['prior', 0x21],
46
+ ['next', 0x22],
47
+ ['end', 0x23],
48
+ ['home', 0x24],
49
+ ['left', 0x25],
50
+ ['up', 0x26],
51
+ ['right', 0x27],
52
+ ['down', 0x28],
53
+ ['select', 0x29],
54
+ ['print', 0x2A],
55
+ ['execute', 0x2B],
56
+ ['snapshot', 0x2C],
57
+ ['insert', 0x2D],
58
+ ['delete', 0x2E],
59
+ ['help', 0x2F],
60
+ ['lwin', 0x5B],
61
+ ['rwin', 0x5C],
62
+ ['apps', 0x5D],
63
+ ['numpad0', 0x60],
64
+ ['numpad1', 0x61],
65
+ ['numpad2', 0x62],
66
+ ['numpad3', 0x63],
67
+ ['numpad4', 0x64],
68
+ ['numpad5', 0x65],
69
+ ['numpad6', 0x66],
70
+ ['numpad7', 0x67],
71
+ ['numpad8', 0x68],
72
+ ['numpad9', 0x69],
73
+ ['multiply', 0x6A],
74
+ ['add', 0x6B],
75
+ ['separator', 0x6C],
76
+ ['subtract', 0x6D],
77
+ ['decimal', 0x6E],
78
+ ['divide', 0x6F],
79
+ ['numlock', 0x90],
80
+ ['scroll', 0x91],
81
+ ['lshift', 0xA0],
82
+ ['rshift', 0xA1],
83
+ ['lcontrol', 0xA2],
84
+ ['rcontrol', 0xA3],
85
+ ['lmenu', 0xA4],
86
+ ['rmenu', 0xA5],
87
+ ['processkey', 0xE5],
88
+ ['attn', 0xF6],
89
+ ['crsel', 0xF7],
90
+ ['exsel', 0xF8],
91
+ ['ereof', 0xF9],
92
+ ['play', 0xFA],
93
+ ['zoom', 0xFB],
94
+ ['noname', 0xFC],
95
+ ['pa1', 0xFD],
96
+ ['oem_clear', 0xFE],
97
+ ['browser_back', 0xA6],
98
+ ['browser_forward', 0xA7],
99
+ ['browser_refresh', 0xA8],
100
+ ['browser_stop', 0xA9],
101
+ ['browser_search', 0xAA],
102
+ ['browser_favorites', 0xAB],
103
+ ['browser_home', 0xAC],
104
+ ['volume_mute', 0xAD],
105
+ ['volume_down', 0xAE],
106
+ ['volume_up', 0xAF],
107
+ ['media_next_track', 0xB0],
108
+ ['media_prev_track', 0xB1],
109
+ ['media_stop', 0xB2],
110
+ ['media_play_pause', 0xB3],
111
+ ['launch_mail', 0xB4],
112
+ ['launch_media_select', 0xB5],
113
+ ['launch_app1', 0xB6],
114
+ ['launch_app2', 0xB7],
115
+ ['oem_plus', 0xBB],
116
+ [',', 'oem_comma', 0xBC],
117
+ ['-', 'oem_minus', 0xBD],
118
+ ['.', 'oem_period', 0xBE],
119
+ ['oem_1', 0xBA],
120
+ ['oem_2', 0xBF],
121
+ ['oem_3', 0xC0],
122
+ ['oem_4', 0xDB],
123
+ ['oem_5', 0xDC],
124
+ ['oem_6', 0xDD],
125
+ ['oem_7', 0xDE],
126
+ ['oem_8', 0xDF],
127
+ ['oem_102', 0xE2],
128
+ ['processkey', 0xE5],
129
+ ['packet', 0xE]].each { |key| core.add *key }
130
+
131
+ core
132
+ end
133
+
134
+ def basic
135
+ basic = core
136
+
137
+ basic.remap 'lcontrol', 'rcontrol', 'control'
138
+ basic.remap 'rmenu', 'lmenu'
139
+ basic.remap 'lshift', 'rshift', 'shift'
140
+ basic.remap 'rwin', 'lwin'
141
+
142
+ basic.alias 'control', 'ctrl'
143
+ basic.alias 'control', 'c'
144
+ basic.rename 'lmenu', 'alt'
145
+ basic.alias 'alt', 'a'
146
+ basic.alias 'shift', 's'
147
+ basic.rename 'lwin', 'win'
148
+ basic.rename 'back', 'backspace'
149
+ basic.rename 'return', 'enter'
150
+ basic.alias 'enter', 'return'
151
+ basic.rename 'capital', 'caps'
152
+ basic.rename 'prior', 'pageup'
153
+ basic.rename 'next', 'pagedown'
154
+ basic.rename 'snapshot', 'print'
155
+
156
+
157
+ %w{shift control alt win}.each { |key| basic.core_modifier key }
158
+
159
+ basic.action_modifier 'alt'
160
+ basic.action_modifier 'win'
161
+
162
+ basic.add :extended, 'alt-extended', basic['alt'].id
163
+ basic.add :extended, 'left-extended', basic['left'].id
164
+ basic.add :extended, 'right-extended', basic['right'].id
165
+
166
+ shift_translations = ('a'..'z').map { |key| [key.upcase, key] }
167
+ shift_translations.each do |from, to|
168
+ basic.translations[from] = "(shift #{to})"
169
+ end
170
+ basic
171
+ end
172
+
173
+ def us
174
+ us = basic
175
+ us.rename 'oem_plus', '='
176
+ us.rename 'oem_1', ';'
177
+ us.rename 'oem_2', '/'
178
+ us.rename 'oem_3', '`'
179
+ us.rename 'oem_4', '['
180
+ us.rename 'oem_5', '\\'
181
+ us.rename 'oem_6', ']'
182
+ us.rename 'oem_7', "'"
183
+ us.rename 'oem_102', '\\'
184
+
185
+ shift_translations = <<END.scan(/(\S) (\S)/)
186
+ ~ ` $ 4 * 8 + = " '
187
+ ! 1 % 5 ( 9 } ] < ,
188
+ @ 2 ^ 6 ) 0 | \ > .
189
+ # 3 & 7 _ - : ; ? /
190
+ END
191
+ shift_translations.each do |from, to|
192
+ us.translations[from] = "(shift #{to})"
193
+ end
194
+ us
195
+ end
196
+
197
+ def german
198
+ german = basic
199
+ german.rename 'oem_plus', '+'
200
+ german.rename 'oem_1', 'ü'
201
+ german.rename 'oem_2', '#'
202
+ german.rename 'oem_3', 'ö'
203
+ german.rename 'oem_4', 'ß'
204
+ german.rename 'oem_5', '^'
205
+ german.rename 'oem_6', '´'
206
+ german.rename 'oem_7', 'ä'
207
+ german.rename 'oem_102', '<'
208
+
209
+ shift_translations = <<END.scan(/(\S) (\S)/)
210
+ ° ^ $ 4 ( 8 ` ´ ; ,
211
+ ! 1 % 5 ) 9 * + : .
212
+ " 2 & 6 = 0 ' # _ -
213
+ § 3 / 7 ? ß > <
214
+ END
215
+ shift_translations.each do |from, to|
216
+ german.translations[from] = "(shift #{to})"
217
+ end
218
+
219
+ altgr_translations = <<END.scan(/(\S) (\S)/)
220
+ ² 2 ] 9 € e
221
+ ³ 3 } 0 ~ +
222
+ { 7 \\ ß | <
223
+ [ 8 @ q µ m
224
+ END
225
+ altgr_translations.each do |from, to|
226
+ german.translations[from] = "(ctrl (alt-extended #{to}))"
227
+ end
228
+
229
+ german
230
+ end
231
+ end
232
+ end
@@ -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
+