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.
- checksums.yaml +7 -0
- data/Dockerfile +26 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +91 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/ast/genheader +3 -0
- data/ast/include/hashmap.c +1151 -0
- data/ast/include/hashmap.c.license +20 -0
- data/ast/include/hashmap.h +54 -0
- data/ast/src/core/ast.c +448 -0
- data/ast/src/core/ast.h +259 -0
- data/ast/src/core/common.h +24 -0
- data/ast/src/core/component.c +85 -0
- data/ast/src/core/component.h +35 -0
- data/ast/src/core/hml.c +665 -0
- data/ast/src/core/hml.h +11 -0
- data/ast/src/core/input.c +458 -0
- data/ast/src/core/input.h +118 -0
- data/ast/src/core/style.c +101 -0
- data/ast/src/core/style.h +41 -0
- data/ast/src/core/text.c +784 -0
- data/ast/src/core/text.h +93 -0
- data/ast/src/core/util.c +140 -0
- data/ast/src/core/util.h +48 -0
- data/ast/src/hokusai.c +6 -0
- data/ast/src/hokusai.h +6 -0
- data/ast/test/fixtures/test.ui +13 -0
- data/ast/test/greatest.h +1266 -0
- data/ast/test/hokusai.c +14 -0
- data/ast/test/parser.c +234 -0
- data/ast/test/text.c +116 -0
- data/ext/extconf.rb +27 -0
- data/grammar/Cargo.lock +80 -0
- data/grammar/Cargo.toml +26 -0
- data/grammar/binding.gyp +20 -0
- data/grammar/bindings/node/binding.cc +28 -0
- data/grammar/bindings/node/index.js +19 -0
- data/grammar/bindings/rust/build.rs +40 -0
- data/grammar/bindings/rust/lib.rs +52 -0
- data/grammar/corpus/1_document.txt +131 -0
- data/grammar/corpus/2_selectors.txt +58 -0
- data/grammar/corpus/3_spaces.txt +69 -0
- data/grammar/corpus/4_errors.txt +10 -0
- data/grammar/corpus/5_macros.txt +175 -0
- data/grammar/corpus/6_styles.txt +81 -0
- data/grammar/grammar.js +275 -0
- data/grammar/package-lock.json +34 -0
- data/grammar/package.json +33 -0
- data/grammar/src/grammar.json +1269 -0
- data/grammar/src/node-types.json +474 -0
- data/grammar/src/parser.c +5772 -0
- data/grammar/src/scanner.c +258 -0
- data/grammar/src/tree_sitter/parser.h +230 -0
- data/grammar/src/tree_sitter/scanner.h +12 -0
- data/grammar/test.nml +10 -0
- data/hokusai.gemspec +19 -0
- data/ui/examples/assets/DigitalDisplay.ttf +0 -0
- data/ui/examples/assets/OpenSans-Regular.ttf +0 -0
- data/ui/examples/assets/addy.png +0 -0
- data/ui/examples/assets/baby_sean.png +0 -0
- data/ui/examples/assets/football-troll.png +0 -0
- data/ui/examples/assets/gear.png +0 -0
- data/ui/examples/assets/icecold.ttf +0 -0
- data/ui/examples/assets/science-troll.png +0 -0
- data/ui/examples/buddy.rb +31 -0
- data/ui/examples/clock.rb +58 -0
- data/ui/examples/counter.rb +123 -0
- data/ui/examples/dynamic.rb +147 -0
- data/ui/examples/foobar.rb +236 -0
- data/ui/examples/stock.rb +115 -0
- data/ui/examples/stock_decider/option.rb +74 -0
- data/ui/examples/tic_tac_toe.rb +246 -0
- data/ui/lib/lib_hokusai.rb +425 -0
- data/ui/spec/hokusai/ast_spec.rb +88 -0
- data/ui/spec/hokusai/automation/keys_transcoder_spec.rb +50 -0
- data/ui/spec/hokusai/automation/selector_spec.rb +68 -0
- data/ui/spec/hokusai/block_spec.rb +126 -0
- data/ui/spec/hokusai/directives_spec.rb +327 -0
- data/ui/spec/hokusai/e2e/client_spec.rb +58 -0
- data/ui/spec/hokusai/e2e/meta_spec.rb +42 -0
- data/ui/spec/hokusai/publisher_spec.rb +38 -0
- data/ui/spec/hokusai/slots_spec.rb +150 -0
- data/ui/spec/hokusai/util/piece_table_spec.rb +90 -0
- data/ui/spec/hokusai_spec.rb +0 -0
- data/ui/spec/spec_helper.rb +30 -0
- data/ui/src/hokusai/ast.rb +446 -0
- data/ui/src/hokusai/automation/client.rb +167 -0
- data/ui/src/hokusai/automation/constants.rb +98 -0
- data/ui/src/hokusai/automation/converters/selector_converter.rb +61 -0
- data/ui/src/hokusai/automation/driver.rb +54 -0
- data/ui/src/hokusai/automation/driver_command_queue.rb +50 -0
- data/ui/src/hokusai/automation/driver_commands/base.rb +79 -0
- data/ui/src/hokusai/automation/driver_commands/get_attribute.rb +41 -0
- data/ui/src/hokusai/automation/driver_commands/invoke.rb +33 -0
- data/ui/src/hokusai/automation/driver_commands/locate.rb +48 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_keyboard.rb +94 -0
- data/ui/src/hokusai/automation/driver_commands/trigger_mouse.rb +213 -0
- data/ui/src/hokusai/automation/keys_transcoder.rb +128 -0
- data/ui/src/hokusai/automation/selector.rb +39 -0
- data/ui/src/hokusai/automation/server.rb +114 -0
- data/ui/src/hokusai/automation.rb +3 -0
- data/ui/src/hokusai/backends/raylib/config.rb +47 -0
- data/ui/src/hokusai/backends/raylib/font.rb +113 -0
- data/ui/src/hokusai/backends/raylib/keys.rb +124 -0
- data/ui/src/hokusai/backends/raylib.rb +449 -0
- data/ui/src/hokusai/backends/sdl2/Monaco.ttf +0 -0
- data/ui/src/hokusai/backends/sdl2/color.rb +12 -0
- data/ui/src/hokusai/backends/sdl2/config.rb +31 -0
- data/ui/src/hokusai/backends/sdl2/font.rb +127 -0
- data/ui/src/hokusai/backends/sdl2/keys.rb +119 -0
- data/ui/src/hokusai/backends/sdl2.rb +529 -0
- data/ui/src/hokusai/block.rb +237 -0
- data/ui/src/hokusai/blocks/button.rb +100 -0
- data/ui/src/hokusai/blocks/checkbox.rb +51 -0
- data/ui/src/hokusai/blocks/circle.rb +28 -0
- data/ui/src/hokusai/blocks/clipped.rb +23 -0
- data/ui/src/hokusai/blocks/cursor.rb +49 -0
- data/ui/src/hokusai/blocks/dynamic.rb +37 -0
- data/ui/src/hokusai/blocks/empty.rb +10 -0
- data/ui/src/hokusai/blocks/hblock.rb +35 -0
- data/ui/src/hokusai/blocks/image.rb +18 -0
- data/ui/src/hokusai/blocks/input.rb +200 -0
- data/ui/src/hokusai/blocks/label.rb +39 -0
- data/ui/src/hokusai/blocks/panel.rb +126 -0
- data/ui/src/hokusai/blocks/rect.rb +24 -0
- data/ui/src/hokusai/blocks/scissor_begin.rb +18 -0
- data/ui/src/hokusai/blocks/scissor_end.rb +12 -0
- data/ui/src/hokusai/blocks/scrollbar.rb +103 -0
- data/ui/src/hokusai/blocks/selectable.rb +77 -0
- data/ui/src/hokusai/blocks/svg.rb +20 -0
- data/ui/src/hokusai/blocks/text.rb +214 -0
- data/ui/src/hokusai/blocks/titlebar/osx.rb +145 -0
- data/ui/src/hokusai/blocks/toggle.rb +55 -0
- data/ui/src/hokusai/blocks/vblock.rb +35 -0
- data/ui/src/hokusai/commands/base.rb +22 -0
- data/ui/src/hokusai/commands/circle.rb +47 -0
- data/ui/src/hokusai/commands/image.rb +45 -0
- data/ui/src/hokusai/commands/rect.rb +158 -0
- data/ui/src/hokusai/commands/scissor.rb +22 -0
- data/ui/src/hokusai/commands/text.rb +92 -0
- data/ui/src/hokusai/commands.rb +87 -0
- data/ui/src/hokusai/diff.rb +124 -0
- data/ui/src/hokusai/error.rb +3 -0
- data/ui/src/hokusai/event.rb +54 -0
- data/ui/src/hokusai/events/keyboard.rb +84 -0
- data/ui/src/hokusai/events/mouse.rb +172 -0
- data/ui/src/hokusai/font.rb +280 -0
- data/ui/src/hokusai/meta.rb +152 -0
- data/ui/src/hokusai/mounting/loop_entry.rb +230 -0
- data/ui/src/hokusai/mounting/mount_entry.rb +74 -0
- data/ui/src/hokusai/mounting/update_entry.rb +101 -0
- data/ui/src/hokusai/node.rb +98 -0
- data/ui/src/hokusai/node_mounter.rb +102 -0
- data/ui/src/hokusai/painter.rb +214 -0
- data/ui/src/hokusai/publisher.rb +32 -0
- data/ui/src/hokusai/style.rb +72 -0
- data/ui/src/hokusai/types.rb +266 -0
- data/ui/src/hokusai/util/clamping_iterator.rb +202 -0
- data/ui/src/hokusai/util/piece_table.rb +111 -0
- data/ui/src/hokusai/util/selection.rb +145 -0
- data/ui/src/hokusai.rb +120 -0
- data/ui/vendor/.gitkeep +0 -0
- data/vendor/.gitkeep +0 -0
- data/xmake.lua +192 -0
- 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
|