hokusai-zero 0.1.1

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.
Files changed (166) hide show
  1. checksums.yaml +7 -0
  2. data/Dockerfile +26 -0
  3. data/Gemfile +15 -0
  4. data/Gemfile.lock +91 -0
  5. data/LICENSE +21 -0
  6. data/README.md +28 -0
  7. data/ast/genheader +3 -0
  8. data/ast/include/hashmap.c +1151 -0
  9. data/ast/include/hashmap.c.license +20 -0
  10. data/ast/include/hashmap.h +54 -0
  11. data/ast/src/core/ast.c +448 -0
  12. data/ast/src/core/ast.h +259 -0
  13. data/ast/src/core/common.h +24 -0
  14. data/ast/src/core/component.c +85 -0
  15. data/ast/src/core/component.h +35 -0
  16. data/ast/src/core/hml.c +665 -0
  17. data/ast/src/core/hml.h +11 -0
  18. data/ast/src/core/input.c +458 -0
  19. data/ast/src/core/input.h +118 -0
  20. data/ast/src/core/style.c +101 -0
  21. data/ast/src/core/style.h +41 -0
  22. data/ast/src/core/text.c +784 -0
  23. data/ast/src/core/text.h +93 -0
  24. data/ast/src/core/util.c +140 -0
  25. data/ast/src/core/util.h +48 -0
  26. data/ast/src/hokusai.c +6 -0
  27. data/ast/src/hokusai.h +6 -0
  28. data/ast/test/fixtures/test.ui +13 -0
  29. data/ast/test/greatest.h +1266 -0
  30. data/ast/test/hokusai.c +14 -0
  31. data/ast/test/parser.c +234 -0
  32. data/ast/test/text.c +116 -0
  33. data/ext/extconf.rb +27 -0
  34. data/grammar/Cargo.lock +80 -0
  35. data/grammar/Cargo.toml +26 -0
  36. data/grammar/binding.gyp +20 -0
  37. data/grammar/bindings/node/binding.cc +28 -0
  38. data/grammar/bindings/node/index.js +19 -0
  39. data/grammar/bindings/rust/build.rs +40 -0
  40. data/grammar/bindings/rust/lib.rs +52 -0
  41. data/grammar/corpus/1_document.txt +131 -0
  42. data/grammar/corpus/2_selectors.txt +58 -0
  43. data/grammar/corpus/3_spaces.txt +69 -0
  44. data/grammar/corpus/4_errors.txt +10 -0
  45. data/grammar/corpus/5_macros.txt +175 -0
  46. data/grammar/corpus/6_styles.txt +81 -0
  47. data/grammar/grammar.js +275 -0
  48. data/grammar/package-lock.json +34 -0
  49. data/grammar/package.json +33 -0
  50. data/grammar/src/grammar.json +1269 -0
  51. data/grammar/src/node-types.json +474 -0
  52. data/grammar/src/parser.c +5772 -0
  53. data/grammar/src/scanner.c +258 -0
  54. data/grammar/src/tree_sitter/parser.h +230 -0
  55. data/grammar/src/tree_sitter/scanner.h +12 -0
  56. data/grammar/test.nml +10 -0
  57. data/hokusai.gemspec +19 -0
  58. data/ui/examples/assets/DigitalDisplay.ttf +0 -0
  59. data/ui/examples/assets/OpenSans-Regular.ttf +0 -0
  60. data/ui/examples/assets/addy.png +0 -0
  61. data/ui/examples/assets/baby_sean.png +0 -0
  62. data/ui/examples/assets/football-troll.png +0 -0
  63. data/ui/examples/assets/gear.png +0 -0
  64. data/ui/examples/assets/icecold.ttf +0 -0
  65. data/ui/examples/assets/science-troll.png +0 -0
  66. data/ui/examples/buddy.rb +31 -0
  67. data/ui/examples/clock.rb +58 -0
  68. data/ui/examples/counter.rb +123 -0
  69. data/ui/examples/dynamic.rb +147 -0
  70. data/ui/examples/foobar.rb +236 -0
  71. data/ui/examples/stock.rb +115 -0
  72. data/ui/examples/stock_decider/option.rb +74 -0
  73. data/ui/examples/tic_tac_toe.rb +246 -0
  74. data/ui/lib/lib_hokusai.rb +425 -0
  75. data/ui/spec/hokusai/ast_spec.rb +88 -0
  76. data/ui/spec/hokusai/automation/keys_transcoder_spec.rb +50 -0
  77. data/ui/spec/hokusai/automation/selector_spec.rb +68 -0
  78. data/ui/spec/hokusai/block_spec.rb +126 -0
  79. data/ui/spec/hokusai/directives_spec.rb +327 -0
  80. data/ui/spec/hokusai/e2e/client_spec.rb +58 -0
  81. data/ui/spec/hokusai/e2e/meta_spec.rb +42 -0
  82. data/ui/spec/hokusai/publisher_spec.rb +38 -0
  83. data/ui/spec/hokusai/slots_spec.rb +150 -0
  84. data/ui/spec/hokusai/util/piece_table_spec.rb +90 -0
  85. data/ui/spec/hokusai_spec.rb +0 -0
  86. data/ui/spec/spec_helper.rb +30 -0
  87. data/ui/src/hokusai/ast.rb +446 -0
  88. data/ui/src/hokusai/automation/client.rb +167 -0
  89. data/ui/src/hokusai/automation/constants.rb +98 -0
  90. data/ui/src/hokusai/automation/converters/selector_converter.rb +61 -0
  91. data/ui/src/hokusai/automation/driver.rb +54 -0
  92. data/ui/src/hokusai/automation/driver_command_queue.rb +50 -0
  93. data/ui/src/hokusai/automation/driver_commands/base.rb +79 -0
  94. data/ui/src/hokusai/automation/driver_commands/get_attribute.rb +41 -0
  95. data/ui/src/hokusai/automation/driver_commands/invoke.rb +33 -0
  96. data/ui/src/hokusai/automation/driver_commands/locate.rb +48 -0
  97. data/ui/src/hokusai/automation/driver_commands/trigger_keyboard.rb +94 -0
  98. data/ui/src/hokusai/automation/driver_commands/trigger_mouse.rb +213 -0
  99. data/ui/src/hokusai/automation/keys_transcoder.rb +128 -0
  100. data/ui/src/hokusai/automation/selector.rb +39 -0
  101. data/ui/src/hokusai/automation/server.rb +114 -0
  102. data/ui/src/hokusai/automation.rb +3 -0
  103. data/ui/src/hokusai/backends/raylib/config.rb +47 -0
  104. data/ui/src/hokusai/backends/raylib/font.rb +113 -0
  105. data/ui/src/hokusai/backends/raylib/keys.rb +124 -0
  106. data/ui/src/hokusai/backends/raylib.rb +449 -0
  107. data/ui/src/hokusai/backends/sdl2/Monaco.ttf +0 -0
  108. data/ui/src/hokusai/backends/sdl2/color.rb +12 -0
  109. data/ui/src/hokusai/backends/sdl2/config.rb +31 -0
  110. data/ui/src/hokusai/backends/sdl2/font.rb +127 -0
  111. data/ui/src/hokusai/backends/sdl2/keys.rb +119 -0
  112. data/ui/src/hokusai/backends/sdl2.rb +529 -0
  113. data/ui/src/hokusai/block.rb +237 -0
  114. data/ui/src/hokusai/blocks/button.rb +100 -0
  115. data/ui/src/hokusai/blocks/checkbox.rb +51 -0
  116. data/ui/src/hokusai/blocks/circle.rb +28 -0
  117. data/ui/src/hokusai/blocks/clipped.rb +23 -0
  118. data/ui/src/hokusai/blocks/cursor.rb +49 -0
  119. data/ui/src/hokusai/blocks/dynamic.rb +37 -0
  120. data/ui/src/hokusai/blocks/empty.rb +10 -0
  121. data/ui/src/hokusai/blocks/hblock.rb +35 -0
  122. data/ui/src/hokusai/blocks/image.rb +18 -0
  123. data/ui/src/hokusai/blocks/input.rb +200 -0
  124. data/ui/src/hokusai/blocks/label.rb +39 -0
  125. data/ui/src/hokusai/blocks/panel.rb +126 -0
  126. data/ui/src/hokusai/blocks/rect.rb +24 -0
  127. data/ui/src/hokusai/blocks/scissor_begin.rb +18 -0
  128. data/ui/src/hokusai/blocks/scissor_end.rb +12 -0
  129. data/ui/src/hokusai/blocks/scrollbar.rb +103 -0
  130. data/ui/src/hokusai/blocks/selectable.rb +77 -0
  131. data/ui/src/hokusai/blocks/svg.rb +20 -0
  132. data/ui/src/hokusai/blocks/text.rb +214 -0
  133. data/ui/src/hokusai/blocks/titlebar/osx.rb +145 -0
  134. data/ui/src/hokusai/blocks/toggle.rb +55 -0
  135. data/ui/src/hokusai/blocks/vblock.rb +35 -0
  136. data/ui/src/hokusai/commands/base.rb +22 -0
  137. data/ui/src/hokusai/commands/circle.rb +47 -0
  138. data/ui/src/hokusai/commands/image.rb +45 -0
  139. data/ui/src/hokusai/commands/rect.rb +158 -0
  140. data/ui/src/hokusai/commands/scissor.rb +22 -0
  141. data/ui/src/hokusai/commands/text.rb +92 -0
  142. data/ui/src/hokusai/commands.rb +87 -0
  143. data/ui/src/hokusai/diff.rb +124 -0
  144. data/ui/src/hokusai/error.rb +3 -0
  145. data/ui/src/hokusai/event.rb +54 -0
  146. data/ui/src/hokusai/events/keyboard.rb +84 -0
  147. data/ui/src/hokusai/events/mouse.rb +172 -0
  148. data/ui/src/hokusai/font.rb +280 -0
  149. data/ui/src/hokusai/meta.rb +152 -0
  150. data/ui/src/hokusai/mounting/loop_entry.rb +230 -0
  151. data/ui/src/hokusai/mounting/mount_entry.rb +74 -0
  152. data/ui/src/hokusai/mounting/update_entry.rb +101 -0
  153. data/ui/src/hokusai/node.rb +98 -0
  154. data/ui/src/hokusai/node_mounter.rb +102 -0
  155. data/ui/src/hokusai/painter.rb +214 -0
  156. data/ui/src/hokusai/publisher.rb +32 -0
  157. data/ui/src/hokusai/style.rb +72 -0
  158. data/ui/src/hokusai/types.rb +266 -0
  159. data/ui/src/hokusai/util/clamping_iterator.rb +202 -0
  160. data/ui/src/hokusai/util/piece_table.rb +111 -0
  161. data/ui/src/hokusai/util/selection.rb +145 -0
  162. data/ui/src/hokusai.rb +120 -0
  163. data/ui/vendor/.gitkeep +0 -0
  164. data/vendor/.gitkeep +0 -0
  165. data/xmake.lua +192 -0
  166. metadata +222 -0
@@ -0,0 +1,446 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hokusai
4
+ # Wrapper for interacting with asts produced
5
+ # by LibHokusai
6
+ class Ast
7
+ def self.registry
8
+ @registry ||= {}
9
+ end
10
+
11
+ def self.get(template)
12
+ registry[template]
13
+ end
14
+
15
+ def self.save(template, ast)
16
+ registry[template] = ast
17
+ end
18
+
19
+ # A node representing a loop
20
+ class Loop
21
+ attr_reader :raw
22
+
23
+ def initialize(raw)
24
+ @raw = raw
25
+ end
26
+
27
+ # The loop variable
28
+ # eg [for="item in list"]
29
+ # the var is `item`
30
+ def var
31
+ @name ||= raw[:name].freeze
32
+ end
33
+
34
+ # The loop method
35
+ # eg [for="item in list"]
36
+ # the method is `list`
37
+ def method
38
+ @list_name ||= raw[:list_name].freeze
39
+ end
40
+ end
41
+
42
+ # A node representing a function
43
+ # call
44
+ class Func
45
+ attr_reader :raw
46
+
47
+ def initialize(raw)
48
+ @raw = raw
49
+ end
50
+
51
+ # Name of the func
52
+ # eg @target="run_this(one,two)"
53
+ # the name is `run_this`
54
+ #
55
+ # @return [String]
56
+ def method
57
+ @method ||= raw[:function].freeze
58
+ end
59
+
60
+ # Args of the func
61
+ # eg @target="run_this(one, two)"
62
+ # the args are [one, two]
63
+ #
64
+ # @return [Array(String)]
65
+ def args
66
+ @strargs ||= raw[:strargs]
67
+ .read_array_of_type(:pointer, :read_pointer, raw[:args_len])
68
+ .map(&:read_string).freeze
69
+ end
70
+ end
71
+
72
+ # A node representing an
73
+ # ast event
74
+ class Event
75
+ attr_reader :raw
76
+
77
+ def initialize(raw)
78
+ @raw = raw
79
+ end
80
+
81
+ # @return [String]
82
+ def name
83
+ @name ||= @raw[:name].freeze
84
+ end
85
+
86
+ # @return [Func?]
87
+ def value
88
+ @call ||= raw[:call]
89
+
90
+ return nil if @call.null?
91
+
92
+ @func ||= Func.new(@call)
93
+ end
94
+ end
95
+
96
+ class Prop
97
+ attr_reader :raw
98
+
99
+ def initialize(raw)
100
+ @raw = raw
101
+ end
102
+
103
+ def computed?
104
+ raw[:computed]
105
+ end
106
+
107
+ def name
108
+ @name ||= raw[:name].freeze
109
+ end
110
+
111
+ def value
112
+ @call ||= raw[:call]
113
+
114
+ return nil if @call.null?
115
+
116
+ @func ||= Func.new(@call)
117
+ end
118
+ end
119
+
120
+ attr_reader :raw
121
+ attr_accessor :updater, :parent, :target
122
+
123
+ def initialize(raw)
124
+ @raw = raw
125
+ @dirty = false
126
+ @children = nil
127
+ @siblings = nil
128
+ @classes = nil
129
+ @else_active = false
130
+ end
131
+
132
+ # @param [String] template the template to parse
133
+ # @param [String] type a name for this template (default root)
134
+ # @return [Hokusai::Ast]
135
+ def self.parse(template, type)
136
+ ast = LibHokusai.parse_template(type, template)
137
+ errored = LibHokusai.hoku_errored_ast(ast)
138
+
139
+ unless errored.null?
140
+ begin
141
+ raise Hokusai::Error.new(errored[:error])
142
+ ensure
143
+ # LibHokusai.hoku_ast_free(errored)
144
+ end
145
+ end
146
+
147
+ new(ast)
148
+ end
149
+
150
+ def style
151
+ @style ||= Style.parse_styles(raw[:styles]) unless raw[:styles].null?
152
+ end
153
+
154
+ def style_list
155
+ @style_list ||= begin
156
+ list = []
157
+
158
+ head = raw[:style_list]
159
+ until head.null?
160
+ list << head[:name]
161
+
162
+ head = head[:next]
163
+ end
164
+
165
+ list
166
+ end
167
+ end
168
+
169
+ def dirty!
170
+ @dirty = true
171
+ end
172
+
173
+ def dirty?
174
+ @dirty
175
+ end
176
+
177
+ def slot?
178
+ type == "slot"
179
+ end
180
+
181
+ def virtual?
182
+ type == "virtual"
183
+ end
184
+
185
+ def loop?
186
+ @loop_condition = !raw[:loop].null? if @loop_condition.nil?
187
+
188
+ @loop_condition
189
+ end
190
+
191
+ def has_if_condition?
192
+ @if_condition = !raw[:cond].null? if @if_condition.nil?
193
+
194
+ @if_condition
195
+ end
196
+
197
+ def has_else_condition?
198
+ @else_condition = !raw[:else_relations].null? if @else_condition.nil?
199
+
200
+ @else_condition
201
+ end
202
+
203
+ def else_condition_active?
204
+ has_else_condition? && @else_active == 1
205
+ end
206
+
207
+ def else_active=(val)
208
+ @else_active = val
209
+ end
210
+
211
+ def else_ast
212
+ return nil unless has_else_condition?
213
+
214
+ Ast.new(raw[:else_relations][:next_child])
215
+ end
216
+
217
+ def loop
218
+ return nil unless loop?
219
+
220
+ @loop ||= Loop.new(raw[:loop])
221
+ end
222
+
223
+ def if
224
+ @cond ||= raw[:cond]
225
+
226
+ return nil if @cond.null?
227
+
228
+ @call ||= @cond[:call]
229
+
230
+ return nil if @call.null?
231
+
232
+ @func ||= Func.new(@call)
233
+ end
234
+
235
+ def type
236
+ @type ||= raw[:type].nil? ? "(null)" : raw[:type]
237
+ end
238
+
239
+ def id
240
+ @id ||= raw[:id].nil? ? "(null)" : raw[:id]
241
+ end
242
+
243
+ def classes
244
+ return @classes unless @classes.nil?
245
+
246
+ @classes = []
247
+ each_class do |child|
248
+ @classes << child
249
+ end
250
+
251
+ @classes
252
+ end
253
+
254
+ def children
255
+ return @children unless @children.nil?
256
+
257
+ @children = []
258
+ each_child do |child|
259
+ @children << child
260
+ end
261
+
262
+ @children
263
+ end
264
+
265
+ def siblings
266
+ return @siblings unless @siblings.nil?
267
+
268
+ @siblings = []
269
+
270
+ ast = raw[:relations][:next_sibling]
271
+ while !ast.null?
272
+ @siblings << Ast.new(ast)
273
+
274
+ ast = ast[:relations][:next_sibling]
275
+ end
276
+
277
+ @siblings
278
+ end
279
+
280
+ def else_children
281
+ children = []
282
+
283
+ ast = raw[:else_relations][:next_child]
284
+ while !ast.null?
285
+ children << ast
286
+
287
+ ast = ast[:relations][:next_sibling]
288
+ end
289
+
290
+ children
291
+ end
292
+
293
+ def props
294
+ return @props unless @props.nil?
295
+
296
+ @props = {}
297
+
298
+ each_prop do |prop|
299
+ @props[prop.name] = prop
300
+ end
301
+
302
+ @props
303
+ end
304
+
305
+ def events
306
+ return @events unless @events.nil?
307
+
308
+ @events = {}
309
+
310
+ each_event do |event|
311
+ @events[event.name] = event
312
+ end
313
+
314
+ @events
315
+ end
316
+
317
+ # Iterate over each child node of this ast
318
+ #
319
+ # @yield [Hokusai::Ast]
320
+ def each_child
321
+ ast = raw[:relations][:next_child]
322
+ i = 0
323
+
324
+ while !ast.null?
325
+ yield Ast.new(ast), i
326
+
327
+ i += 1
328
+ ast = ast[:relations][:next_sibling]
329
+ end
330
+ end
331
+
332
+ # Iterate over all class names for this ast
333
+ #
334
+ # @yield [String] the name of the class
335
+ def each_class
336
+ ast = raw[:class_list]
337
+ i = 0
338
+
339
+ while !ast.null?
340
+ yield(ast[:name], i)
341
+
342
+ i += 1
343
+ ast = ast[:next]
344
+ end
345
+ end
346
+
347
+ # Iterate over all events in this ast
348
+ #
349
+ # @yield [Hokusai::Ast::Event]
350
+ def each_event
351
+ i = FFI::MemoryPointer.new(:size_t)
352
+ i.write(:size_t, 0)
353
+ item = FFI::MemoryPointer.new :pointer
354
+
355
+ while LibHokusai.hashmap_iter(raw[:events], i, item)
356
+ event = item.get_pointer(0)
357
+ event = LibHokusai::HmlAstEvent.new(event)
358
+
359
+ yield Event.new(event)
360
+ end
361
+
362
+ item.free
363
+ i.free
364
+ end
365
+
366
+ # Iterate over all props in this ast
367
+ #
368
+ # @yield [Hokusai::Ast::Prop]
369
+ def each_prop
370
+ i = FFI::MemoryPointer.new :size_t
371
+ i.write(:size_t, 0)
372
+ item = FFI::MemoryPointer.new :pointer
373
+
374
+ while LibHokusai.hashmap_iter(raw[:props], i, item)
375
+ prop = item.get_pointer(0)
376
+ prop = LibHokusai::HmlAstProp.new(prop)
377
+
378
+ yield Prop.new(prop)
379
+ end
380
+
381
+ item.free
382
+ i.free
383
+ end
384
+
385
+ def prop(name)
386
+ props[name]
387
+ end
388
+
389
+ # Search for a prop by name
390
+ #
391
+ # @param [String] name the name of the prop to find
392
+ # @return [Hokusai::Ast::Prop?] the prop if found
393
+ # def prop(name)
394
+ # prop_ptr = FFI::MemoryPointer.new :pointer
395
+ # code = LibHokusai.hoku_ast_prop_init(prop_ptr, name)
396
+ #
397
+ # raise Hokusai::Error.new("Failed to allocate prop") unless code.zero?
398
+ #
399
+ # prop_ptr2 = prop_ptr.get_pointer(0)
400
+ # prop = LibHokusai::HmlAstProp.new(prop_ptr2)
401
+ # response = LibHokusai.hoku_ast_get_prop(raw, prop)
402
+ # return nil if response.null?
403
+ #
404
+ # Prop.new(response)
405
+ # ensure
406
+ # prop_ptr.free
407
+ # end
408
+
409
+ def event(name)
410
+ events[name]
411
+ end
412
+
413
+ # Search for an event by name
414
+ #
415
+ # @param [String] name the name of the prop to find
416
+ # @return [Prop?] the prop if found
417
+ # def event(name)
418
+ # event_ptr = FFI::MemoryPointer.new :pointer
419
+ # code = LibHokusai.hoku_ast_prop_init(event_ptr, name)
420
+ #
421
+ # raise Hokusai::Error.new("Failed to allocate prop") unless code.zero?
422
+ #
423
+ # event_ptr2 = event_ptr.get_pointer(0)
424
+ # event = LibHokusai::HmlAstEvent.new(event_ptr2)
425
+ # response = LibHokusai.hoku_ast_get_event(raw, event)
426
+ # event_ptr.free
427
+ #
428
+ # return nil if response.null?
429
+ #
430
+ # Event.new(response)
431
+ # ensure
432
+ # event_ptr.free
433
+ # end
434
+
435
+ def destroy
436
+ puts "before destroy"
437
+ LibHokusai.hoku_ast_free(raw)
438
+ puts "after destroy"
439
+ end
440
+
441
+ # dumps this ast to STDOUT
442
+ def dump
443
+ LibHokusai.hoku_dump(raw, 0)
444
+ end
445
+ end
446
+ end
@@ -0,0 +1,167 @@
1
+ require_relative "./constants"
2
+ require "json"
3
+ require "uri"
4
+ require "rest-client"
5
+
6
+ module Hokusai
7
+ module Automation
8
+ class Client
9
+ class Block
10
+ attr_reader :uuid, :client
11
+
12
+ def self.locate(selector, client)
13
+ value = client.make_request("commands/locate", { selector: selector })
14
+ new(value, client)
15
+ end
16
+
17
+ def self.locate_all(selector, client)
18
+ value = client.make_request("commands/locate", { selector: selector })
19
+
20
+ value.map do |val|
21
+ new(val, client)
22
+ end
23
+ end
24
+
25
+ def initialize(uuid, client)
26
+ @uuid = uuid
27
+ @client = client
28
+ end
29
+
30
+ [[:left, 0], [:middle, 1], [:right, 2]].each do |(type, button)|
31
+ define_method("#{type}_click") do
32
+ client.make_request("commands/click", {uuid: uuid, button: button})
33
+ end
34
+
35
+ define_method("#{type}_mouseup") do
36
+ client.make_request("commands/mouseup", {uuid: uuid, button: button})
37
+ end
38
+ end
39
+
40
+ def click
41
+ left_click
42
+ end
43
+
44
+ def mouseup
45
+ left_mouseup
46
+ end
47
+
48
+ def prop(name)
49
+ client.make_request("commands/attribute", { uuid: uuid, attribute_name: name})
50
+ end
51
+
52
+ def invoke(name)
53
+ client.make_request("commands/invoke", {uuid: uuid, method: name})
54
+ end
55
+
56
+ def hover
57
+ client.make_request("commands/hover", { uuid: uuid })
58
+ end
59
+
60
+ def drag_to(x, y)
61
+ client.make_request("commands/drag", {uuid: uuid, x: x, y: y})
62
+ end
63
+
64
+ def send_keys(keys)
65
+ case keys
66
+ when String
67
+ encoded = KeysTranscoder.encode(keys.chars)
68
+ else
69
+ encoded = KeysTranscoder.encode(keys)
70
+ end
71
+
72
+ client.make_request("commands/keyboard", {uuid: uuid, keys: encoded})
73
+ end
74
+
75
+ def scroll(to)
76
+ client.make_request("commands/wheel", {id: uuid, scroll_amount: to})
77
+ end
78
+ end
79
+
80
+ attr_reader :host, :port
81
+
82
+ def self.start(host = "localhost", port=3000)
83
+ new URI.parse("http://#{host}:#{port}")
84
+ end
85
+
86
+ def self.start_unix(path)
87
+ new URI.parse("unix://#{path}")
88
+ end
89
+
90
+ def initialize(uri)
91
+ @host = uri.host
92
+ @port = uri.port
93
+ @http = RestClient::Resource.new("#{uri.host}:#{uri.port}")
94
+
95
+ wait_until_ready
96
+ end
97
+
98
+ def block(selector)
99
+ block = Client::Block.locate(selector, self)
100
+ if block_given?
101
+ yield block
102
+ else
103
+ block
104
+ end
105
+ end
106
+
107
+ def blocks(selector)
108
+ Client::Block.locate_all(selector, self)
109
+ end
110
+
111
+ def make_request(command, json)
112
+ begin
113
+ res = @http[command].post(json.to_json, headers)
114
+
115
+ if res.code == 200
116
+ JSON.parse(res.body)["value"]
117
+ else
118
+ raise Automation::Error.new("Got status code #{res.code} - #{res.body}")
119
+ end
120
+ rescue StandardError => ex
121
+ raise Automation::Error.new("Failed to make request: #{ex}")
122
+ end
123
+ end
124
+
125
+ def wait_until_stopped(timeout: 2, interval: 0.2)
126
+ time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
127
+ val = nil
128
+
129
+ while (Process.clock_gettime(Process::CLOCK_MONOTONIC) - time) < timeout
130
+ begin
131
+ res = @http["ready"].post
132
+ if res.code != 200
133
+ break
134
+ else
135
+ sleep interval
136
+ end
137
+ rescue
138
+ sleep interval
139
+ end
140
+ end
141
+ end
142
+
143
+ def wait_until_ready(timeout: 2, interval: 0.2)
144
+ time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
145
+ val = nil
146
+
147
+ while (Process.clock_gettime(Process::CLOCK_MONOTONIC) - time) < timeout
148
+ begin
149
+ @http.start
150
+ res = @http["ready"].post
151
+ if res.code == 200
152
+ break
153
+ else
154
+ sleep interval
155
+ end
156
+ rescue
157
+ sleep interval
158
+ end
159
+ end
160
+ end
161
+
162
+ def headers
163
+ {"Content-Type" => "application/json" }
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,98 @@
1
+ require "logger"
2
+ require_relative "./keys_transcoder"
3
+
4
+ module Hokusai::Automation
5
+ class Error < StandardError; end
6
+
7
+ Log = Logger.new($stdout)
8
+
9
+ case ENV["LOG_LEVEL"]
10
+ when "debug"
11
+ Log.level = Logger::DEBUG
12
+ when "info"
13
+ Log.level = Logger::INFO
14
+ when "warn"
15
+ Log.level = Logger::WARN
16
+ when "error"
17
+ Log.level = Logger::ERROR
18
+ end
19
+
20
+ CODE_TO_HML_KEY = {
21
+ '\0' => :null,
22
+ '\'' => :apostrophe,
23
+ ',' => :comma,
24
+ '-' => :minus,
25
+ '.' => :period,
26
+ '\\' => :slash,
27
+ '0' => :zero,
28
+ '1' => :one,
29
+ '2' => :two,
30
+ '3' => :three,
31
+ "4" => :four,
32
+ '5' => :five,
33
+ '6' => :six,
34
+ '7' => :seven,
35
+ '8' => :eight,
36
+ '9' => :nine,
37
+ ';' => :semicolon,
38
+ '=' => :equal,
39
+ 'A' => :a,
40
+ 'B' => :b,
41
+ 'C' => :c,
42
+ 'D' => :d,
43
+ 'E' => :e,
44
+ 'F' => :f,
45
+ 'G' => :g,
46
+ 'H' => :h,
47
+ 'I' => :i,
48
+ 'J' => :j,
49
+ 'K' => :k,
50
+ 'L' => :l,
51
+ 'M' => :m,
52
+ 'N' => :n,
53
+ 'O' => :o,
54
+ 'P' => :p,
55
+ 'Q' => :q,
56
+ 'R' => :r,
57
+ 'S' => :s,
58
+ 'T' => :t,
59
+ 'U' => :u,
60
+ 'V' => :v,
61
+ 'W' => :w,
62
+ 'X' => :x,
63
+ 'Y' => :y,
64
+ 'Z' => :z,
65
+ 'a' => :a,
66
+ 'b' => :b,
67
+ 'c' => :c,
68
+ 'd' => :d,
69
+ 'e' => :e,
70
+ 'f' => :f,
71
+ 'g' => :g,
72
+ 'h' => :h,
73
+ 'i' => :i,
74
+ 'j' => :j,
75
+ 'k' => :k,
76
+ 'l' => :l,
77
+ 'm' => :m,
78
+ 'n' => :n,
79
+ 'o' => :o,
80
+ 'p' => :p,
81
+ 'q' => :q,
82
+ 'r' => :r,
83
+ 's' => :s,
84
+ 't' => :t,
85
+ 'u' => :u,
86
+ 'v' => :v,
87
+ 'w' => :w,
88
+ 'x' => :x,
89
+ 'y' => :y,
90
+ 'z' => :z,
91
+ '[' => :left_bracket,
92
+ '/' => :backslash,
93
+ ']' => :right_bracket,
94
+ '~' => :grave,
95
+ ' ' => :space,
96
+ '\e' => :escape,
97
+ }.merge(KeysTranscoder::KEYS.to_h { |k, v| [k, k]})
98
+ end