scarpe-wasm 0.1.0
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/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +12 -0
- data/lib/scarpe/wasm/alert.rb +65 -0
- data/lib/scarpe/wasm/app.rb +107 -0
- data/lib/scarpe/wasm/arc.rb +54 -0
- data/lib/scarpe/wasm/background.rb +27 -0
- data/lib/scarpe/wasm/border.rb +24 -0
- data/lib/scarpe/wasm/button.rb +50 -0
- data/lib/scarpe/wasm/check.rb +29 -0
- data/lib/scarpe/wasm/control_interface.rb +147 -0
- data/lib/scarpe/wasm/control_interface_test.rb +234 -0
- data/lib/scarpe/wasm/dimensions.rb +22 -0
- data/lib/scarpe/wasm/document_root.rb +8 -0
- data/lib/scarpe/wasm/edit_box.rb +44 -0
- data/lib/scarpe/wasm/edit_line.rb +43 -0
- data/lib/scarpe/wasm/flow.rb +24 -0
- data/lib/scarpe/wasm/font.rb +36 -0
- data/lib/scarpe/wasm/html.rb +108 -0
- data/lib/scarpe/wasm/image.rb +41 -0
- data/lib/scarpe/wasm/line.rb +35 -0
- data/lib/scarpe/wasm/link.rb +29 -0
- data/lib/scarpe/wasm/list_box.rb +50 -0
- data/lib/scarpe/wasm/para.rb +90 -0
- data/lib/scarpe/wasm/radio.rb +34 -0
- data/lib/scarpe/wasm/shape.rb +68 -0
- data/lib/scarpe/wasm/slot.rb +81 -0
- data/lib/scarpe/wasm/spacing.rb +41 -0
- data/lib/scarpe/wasm/span.rb +66 -0
- data/lib/scarpe/wasm/stack.rb +24 -0
- data/lib/scarpe/wasm/star.rb +61 -0
- data/lib/scarpe/wasm/subscription_item.rb +50 -0
- data/lib/scarpe/wasm/text_widget.rb +30 -0
- data/lib/scarpe/wasm/version.rb +7 -0
- data/lib/scarpe/wasm/video.rb +42 -0
- data/lib/scarpe/wasm/wasm_calls.rb +118 -0
- data/lib/scarpe/wasm/wasm_local_display.rb +94 -0
- data/lib/scarpe/wasm/web_wrangler.rb +679 -0
- data/lib/scarpe/wasm/webview_relay_display.rb +220 -0
- data/lib/scarpe/wasm/widget.rb +228 -0
- data/lib/scarpe/wasm/wv_display_worker.rb +75 -0
- data/lib/scarpe/wasm.rb +46 -0
- data/scarpe-wasm.gemspec +39 -0
- data/sig/scarpe/wasm.rbs +6 -0
- metadata +92 -0
@@ -0,0 +1,220 @@
|
|
1
|
+
# # frozen_string_literal: true
|
2
|
+
|
3
|
+
# require "socket"
|
4
|
+
# require "rbconfig"
|
5
|
+
|
6
|
+
# class Scarpe
|
7
|
+
# # An error occurred which would normally be handled by shutting down the app
|
8
|
+
# class AppShutdownError < Scarpe::Error; end
|
9
|
+
|
10
|
+
# # WVRelayUtil defines the datagram format for the sockets that connect a parent
|
11
|
+
# # Shoes application with a child display server.
|
12
|
+
# #
|
13
|
+
# # The class including this module should also include Shoes::Log so that it can
|
14
|
+
# # be used.
|
15
|
+
# module WVRelayUtil
|
16
|
+
# # Checks whether the internal socket is ready to be read from.
|
17
|
+
# # If timeout is greater than 0, this will block for up to that long.
|
18
|
+
# #
|
19
|
+
# # @param timeout [Float] the longest to wait for more input to read
|
20
|
+
# # @return [Boolean] whether the socket has data ready for reading
|
21
|
+
# def ready_to_read?(timeout = 0.0)
|
22
|
+
# r, _, e = IO.select [@from], [], [@from, @to].uniq, timeout
|
23
|
+
|
24
|
+
# # On timeout, select returns nil instead of arrays.
|
25
|
+
# return if r.nil?
|
26
|
+
|
27
|
+
# unless e.empty?
|
28
|
+
# raise "#{@i_am}: Got error on connection(s) from IO.select! Dying!"
|
29
|
+
# end
|
30
|
+
|
31
|
+
# !r.empty?
|
32
|
+
# end
|
33
|
+
|
34
|
+
# # Send bytes on the internal socket to the opposite side.
|
35
|
+
# #
|
36
|
+
# # @param contents [String] data to send
|
37
|
+
# # @return [void]
|
38
|
+
# def send_datagram(contents)
|
39
|
+
# str_data = JSON.dump contents
|
40
|
+
# dgram_str = (str_data.length.to_s + "a" + str_data).encode(Encoding::BINARY)
|
41
|
+
# to_write = dgram_str.bytesize
|
42
|
+
# written = 0
|
43
|
+
|
44
|
+
# until written == to_write
|
45
|
+
# count = @to.write(dgram_str.byteslice(written..-1))
|
46
|
+
# if count.nil? || count == 0
|
47
|
+
# raise "Something was wrong in send_datagram! Write returned #{count.inspect}!"
|
48
|
+
# end
|
49
|
+
|
50
|
+
# written += count
|
51
|
+
# end
|
52
|
+
|
53
|
+
# nil
|
54
|
+
# end
|
55
|
+
|
56
|
+
# # Read data from the internal socket. Read until a whole datagram
|
57
|
+
# # has been received and then return it.
|
58
|
+
# #
|
59
|
+
# # @return [String] the received datagram
|
60
|
+
# def receive_datagram
|
61
|
+
# @readbuf ||= String.new.encode(Encoding::BINARY)
|
62
|
+
# to_read = nil
|
63
|
+
|
64
|
+
# loop do
|
65
|
+
# # Have we read a packet length already, sitting in @readbuf?
|
66
|
+
# a_idx = @readbuf.index("a")
|
67
|
+
# if a_idx
|
68
|
+
# to_read = @readbuf[0..a_idx].to_i
|
69
|
+
# @readbuf = @readbuf[(a_idx + 1)..-1]
|
70
|
+
# break
|
71
|
+
# end
|
72
|
+
|
73
|
+
# # If not, read more bytes
|
74
|
+
# new_bytes = @from.read(10)
|
75
|
+
# if new_bytes.nil?
|
76
|
+
# # This is perfectly normal, if the connection closed
|
77
|
+
# raise AppShutdownError, "Got an unexpected EOF reading datagram! " +
|
78
|
+
# "Did the #{@i_am == :child ? "parent" : "child"} process die?"
|
79
|
+
# end
|
80
|
+
# @readbuf << new_bytes
|
81
|
+
# end
|
82
|
+
|
83
|
+
# loop do
|
84
|
+
# if @readbuf.bytesize >= to_read
|
85
|
+
# out = @readbuf.byteslice(0, to_read)
|
86
|
+
# @readbuf = @readbuf.byteslice(to_read, -1)
|
87
|
+
# return out
|
88
|
+
# end
|
89
|
+
|
90
|
+
# new_bytes = @from.read(to_read - @readbuf.bytesize)
|
91
|
+
# @readbuf << new_bytes
|
92
|
+
# end
|
93
|
+
# rescue
|
94
|
+
# raise AppShutdownError, "Got exception #{$!.class} when receiving datagram... #{$!.inspect}"
|
95
|
+
# end
|
96
|
+
|
97
|
+
# # Read a datagram from the internal buffer and then dispatch it to the
|
98
|
+
# # appropriate handler.
|
99
|
+
# def respond_to_datagram
|
100
|
+
# message = receive_datagram
|
101
|
+
# m_data = JSON.parse(message)
|
102
|
+
|
103
|
+
# if m_data["type"] == "event"
|
104
|
+
# kwargs_hash = {}
|
105
|
+
# m_data["kwargs"].each { |k, v| kwargs_hash[k.to_sym] = v }
|
106
|
+
# send_shoes_event(
|
107
|
+
# *m_data["args"],
|
108
|
+
# event_name: m_data["kwargs"]["event_name"],
|
109
|
+
# target: m_data["kwargs"]["event_target"],
|
110
|
+
# **kwargs_hash,
|
111
|
+
# )
|
112
|
+
# elsif m_data["type"] == "create"
|
113
|
+
# raise "Parent process should never receive :create datagram!" if @i_am == :parent
|
114
|
+
|
115
|
+
# @wv_display.create_display_widget_for(m_data["class_name"], m_data["id"], m_data["properties"])
|
116
|
+
# elsif m_data["type"] == "destroy"
|
117
|
+
# if @i_am == :parent
|
118
|
+
# @shutdown = true
|
119
|
+
# else
|
120
|
+
# @log.info("Shutting down...")
|
121
|
+
# exit 0
|
122
|
+
# end
|
123
|
+
# else
|
124
|
+
# @log.error("Unrecognized datagram type:event: #{m_data.inspect}!")
|
125
|
+
# end
|
126
|
+
# end
|
127
|
+
|
128
|
+
# # Loop for up to `t` seconds, reading data and waiting.
|
129
|
+
# #
|
130
|
+
# # @param t [Float] the number of seconds to loop for
|
131
|
+
# def event_loop_for(t = 1.5)
|
132
|
+
# t_start = Time.now
|
133
|
+
# delay_time = t
|
134
|
+
|
135
|
+
# while Time.now - t_start < delay_time
|
136
|
+
# if ready_to_read?(0.1)
|
137
|
+
# respond_to_datagram
|
138
|
+
# else
|
139
|
+
# sleep 0.1
|
140
|
+
# end
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
# end
|
144
|
+
|
145
|
+
# # This display service creates a child process and sends events
|
146
|
+
# # back and forth, but creates no widgets of its own. The child
|
147
|
+
# # process will spawn a worker with its own WebviewDisplayService
|
148
|
+
# # where the real Webview exists. By splitting the Webview
|
149
|
+
# # process from the Shoes widgets, it can be easier to return
|
150
|
+
# # control to Webview's event handler promptly. Also, the Ruby
|
151
|
+
# # process could run background threads if it wanted, and
|
152
|
+
# # otherwise behave like a process ***not*** containing Webview.
|
153
|
+
# class WVRelayDisplayService < Shoes::DisplayService
|
154
|
+
# include Shoes::Log
|
155
|
+
# include WVRelayUtil # Needs Shoes::Log
|
156
|
+
|
157
|
+
# attr_accessor :shutdown
|
158
|
+
|
159
|
+
# # Create a Webview Relay Display Service
|
160
|
+
# def initialize
|
161
|
+
# super()
|
162
|
+
# log_init("WV::RelayDisplayService")
|
163
|
+
|
164
|
+
# @event_subs = []
|
165
|
+
# @shutdown = false
|
166
|
+
# @i_am = :parent
|
167
|
+
|
168
|
+
# server = TCPServer.new("127.0.0.1", 0)
|
169
|
+
# port = server.addr[1]
|
170
|
+
|
171
|
+
# @pid = spawn(RbConfig.ruby, File.join(__dir__, "wv_display_worker.rb"), port.to_s)
|
172
|
+
# @from = @to = server.accept
|
173
|
+
|
174
|
+
# # Subscribe to all event notifications and relay them to the worker
|
175
|
+
# @event_subs << bind_shoes_event(event_name: :any, target: :any) do |*args, **kwargs|
|
176
|
+
# unless kwargs[:relayed]
|
177
|
+
# kwargs[:relayed] = true
|
178
|
+
# send_datagram({ type: :event, args:, kwargs: })
|
179
|
+
# end
|
180
|
+
|
181
|
+
# # Forward the run event to the child process before doing this
|
182
|
+
# if event_name == "run"
|
183
|
+
# run_event_loop
|
184
|
+
# end
|
185
|
+
# rescue AppShutdownError
|
186
|
+
# @shutdown = true
|
187
|
+
# @log.info("Attempting to shut down...")
|
188
|
+
# self.destroy
|
189
|
+
# end
|
190
|
+
# end
|
191
|
+
|
192
|
+
# # Run, sending and responding to datagrams continuously.
|
193
|
+
# def run_event_loop
|
194
|
+
# until @shutdown
|
195
|
+
# respond_to_datagram while ready_to_read?
|
196
|
+
# sleep 0.1
|
197
|
+
# end
|
198
|
+
# rescue AppShutdownError
|
199
|
+
# @shutdown = true
|
200
|
+
# @log.info("Attempting to shut down...")
|
201
|
+
# self.destroy
|
202
|
+
# end
|
203
|
+
|
204
|
+
# # This method sends a message to the worker process to create a widget. No actual
|
205
|
+
# # widget is created or registered with the display service.
|
206
|
+
# def create_display_widget_for(widget_class_name, widget_id, properties)
|
207
|
+
# send_datagram({ type: :create, class_name: widget_class_name, id: widget_id, properties: })
|
208
|
+
# # Don't need to return anything. It wouldn't be used anyway.
|
209
|
+
# end
|
210
|
+
|
211
|
+
# # Tell the worker process to quit, and set a flag telling the event loop to shut down.
|
212
|
+
# def destroy
|
213
|
+
# unless @shutdown
|
214
|
+
# send_datagram({ type: :destroy })
|
215
|
+
# end
|
216
|
+
# @shutdown = true
|
217
|
+
# (@events_subs || []).each { |unsub_id| DisplayService.unsub_from_events(unsub_id) }
|
218
|
+
# end
|
219
|
+
# end
|
220
|
+
# end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Scarpe
|
4
|
+
# The WASMWidget parent class helps connect a WASM widget with
|
5
|
+
# its Shoes equivalent, render itself to the WASM DOM, handle
|
6
|
+
# Javascript events and generally keep things working in WASM.
|
7
|
+
class WASMWidget < Shoes::Linkable
|
8
|
+
include Shoes::Log
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Return the corresponding WASM class for a particular Shoes class name
|
12
|
+
def display_class_for(scarpe_class_name)
|
13
|
+
scarpe_class = Shoes.const_get(scarpe_class_name)
|
14
|
+
unless scarpe_class.ancestors.include?(Shoes::Linkable)
|
15
|
+
raise "Scarpe WASM can only get display classes for Shoes " +
|
16
|
+
"linkable widgets, not #{scarpe_class_name.inspect}!"
|
17
|
+
end
|
18
|
+
|
19
|
+
klass = Scarpe.const_get("WASM" + scarpe_class_name.split("::")[-1])
|
20
|
+
if klass.nil?
|
21
|
+
raise "Couldn't find corresponding Scarpe WASM class for #{scarpe_class_name.inspect}!"
|
22
|
+
end
|
23
|
+
|
24
|
+
klass
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# The Shoes ID corresponding to the Shoes widget for this WASM widget
|
29
|
+
attr_reader :shoes_linkable_id
|
30
|
+
|
31
|
+
# The WASMWidget parent of this widget
|
32
|
+
attr_reader :parent
|
33
|
+
|
34
|
+
# An array of WASMWidget children (possibly empty) of this widget
|
35
|
+
attr_reader :children
|
36
|
+
|
37
|
+
# Set instance variables for the display properties of this widget. Bind Shoes
|
38
|
+
# events for changes of parent widget and changes of property values.
|
39
|
+
def initialize(properties)
|
40
|
+
log_init("WASM::Widget")
|
41
|
+
|
42
|
+
# Call method, which looks up the parent
|
43
|
+
@shoes_linkable_id = properties["shoes_linkable_id"] || properties[:shoes_linkable_id]
|
44
|
+
unless @shoes_linkable_id
|
45
|
+
raise "Could not find property shoes_linkable_id in #{properties.inspect}!"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Set the display properties
|
49
|
+
properties.each do |k, v|
|
50
|
+
next if k == "shoes_linkable_id"
|
51
|
+
|
52
|
+
instance_variable_set("@" + k.to_s, v)
|
53
|
+
end
|
54
|
+
|
55
|
+
# The parent field is *almost* simple enough that a typed display property would handle it.
|
56
|
+
bind_shoes_event(event_name: "parent", target: shoes_linkable_id) do |new_parent_id|
|
57
|
+
display_parent = WASMDisplayService.instance.query_display_widget_for(new_parent_id)
|
58
|
+
if @parent != display_parent
|
59
|
+
set_parent(display_parent)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# When Shoes widgets change properties, we get a change notification here
|
64
|
+
bind_shoes_event(event_name: "prop_change", target: shoes_linkable_id) do |prop_changes|
|
65
|
+
prop_changes.each do |k, v|
|
66
|
+
instance_variable_set("@" + k, v)
|
67
|
+
end
|
68
|
+
properties_changed(prop_changes)
|
69
|
+
end
|
70
|
+
|
71
|
+
bind_shoes_event(event_name: "destroy", target: shoes_linkable_id) do
|
72
|
+
destroy_self
|
73
|
+
end
|
74
|
+
|
75
|
+
super(linkable_id: @shoes_linkable_id)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Properties_changed will be called automatically when properties change.
|
79
|
+
# The widget should delete any changes from the Hash that it knows how
|
80
|
+
# to incrementally handle, and pass the rest to super. If any changes
|
81
|
+
# go entirely un-handled, a full redraw will be scheduled.
|
82
|
+
# This exists to be overridden by children watching for changes.
|
83
|
+
#
|
84
|
+
# @param changes [Hash] a Hash of new values for properties that have changed
|
85
|
+
def properties_changed(changes)
|
86
|
+
needs_update! unless changes.empty?
|
87
|
+
end
|
88
|
+
|
89
|
+
# Give this widget a new parent, including managing the appropriate child lists for parent widgets.
|
90
|
+
def set_parent(new_parent)
|
91
|
+
@parent&.remove_child(self)
|
92
|
+
new_parent&.add_child(self)
|
93
|
+
@parent = new_parent
|
94
|
+
end
|
95
|
+
|
96
|
+
# A shorter inspect text for prettier irb output
|
97
|
+
def inspect
|
98
|
+
"#<#{self.class}:#{self.object_id} @shoes_linkable_id=#{@shoes_linkable_id} @parent=#{@parent.inspect} @children=#{@children.inspect}>"
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
# Do not call directly, use set_parent
|
104
|
+
def remove_child(child)
|
105
|
+
@children ||= []
|
106
|
+
unless @children.include?(child)
|
107
|
+
@log.error("remove_child: no such child(#{child.inspect}) for"\
|
108
|
+
" parent(#{parent.inspect})!")
|
109
|
+
end
|
110
|
+
@children.delete(child)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Do not call directly, use set_parent
|
114
|
+
def add_child(child)
|
115
|
+
@children ||= []
|
116
|
+
@children << child
|
117
|
+
|
118
|
+
# If we add a child, we should redraw ourselves
|
119
|
+
needs_update!
|
120
|
+
end
|
121
|
+
|
122
|
+
# Convert an [r, g, b, a] array to an HTML hex color code
|
123
|
+
# Arrays support alpha. HTML hex does not. So premultiply.
|
124
|
+
def rgb_to_hex(color)
|
125
|
+
return color if color.nil?
|
126
|
+
|
127
|
+
r, g, b, a = *color
|
128
|
+
if r.is_a?(Float)
|
129
|
+
a ||= 1.0
|
130
|
+
r_float = r * a
|
131
|
+
g_float = g * a
|
132
|
+
b_float = b * a
|
133
|
+
else
|
134
|
+
a ||= 255
|
135
|
+
a_float = (a / 255.0)
|
136
|
+
r_float = (r.to_f / 255.0) * a_float
|
137
|
+
g_float = (g.to_f / 255.0) * a_float
|
138
|
+
b_float = (b.to_f / 255.0) * a_float
|
139
|
+
end
|
140
|
+
|
141
|
+
r_int = (r_float * 255.0).to_i.clamp(0, 255)
|
142
|
+
g_int = (g_float * 255.0).to_i.clamp(0, 255)
|
143
|
+
b_int = (b_float * 255.0).to_i.clamp(0, 255)
|
144
|
+
|
145
|
+
"#%0.2X%0.2X%0.2X" % [r_int, g_int, b_int]
|
146
|
+
end
|
147
|
+
|
148
|
+
public
|
149
|
+
|
150
|
+
# This gets a mini-WASM for just this element and its children, if any.
|
151
|
+
# It is normally called by the widget itself to do its DOM management.
|
152
|
+
#
|
153
|
+
# @return [Scarpe::WebWrangler::ElementWrangler] a DOM object manager
|
154
|
+
def html_element
|
155
|
+
@elt_wrangler ||= Scarpe::WebWrangler::ElementWrangler.new(html_id)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Return a promise that guarantees all currently-requested changes have completed
|
159
|
+
#
|
160
|
+
# @return [Scarpe::Promise] a promise that will be fulfilled when all pending changes have finished
|
161
|
+
def promise_update
|
162
|
+
html_element.promise_update
|
163
|
+
end
|
164
|
+
|
165
|
+
# Get the object's HTML ID
|
166
|
+
#
|
167
|
+
# @return [String] the HTML ID
|
168
|
+
def html_id
|
169
|
+
object_id.to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
# to_html is intended to get the HTML DOM rendering of this object and its children.
|
173
|
+
# Calling it should be side-effect-free and NOT update the WASM.
|
174
|
+
#
|
175
|
+
# @return [String] the rendered HTML
|
176
|
+
def to_html
|
177
|
+
@children ||= []
|
178
|
+
child_markup = @children.map(&:to_html).join
|
179
|
+
if respond_to?(:element)
|
180
|
+
element { child_markup }
|
181
|
+
else
|
182
|
+
child_markup
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# This binds a Scarpe JS callback, handled via a single dispatch point in the app
|
187
|
+
#
|
188
|
+
# @param event [String] the Scarpe widget event name
|
189
|
+
# @yield the block to call when the event occurs
|
190
|
+
def bind(event, &block)
|
191
|
+
raise("Widget has no linkable_id! #{inspect}") unless linkable_id
|
192
|
+
|
193
|
+
WASMDisplayService.instance.app.bind("#{linkable_id}-#{event}", &block)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Removes the element from both the Ruby Widget tree and the HTML DOM.
|
197
|
+
# Return a promise for when that HTML change will be visible.
|
198
|
+
#
|
199
|
+
# @return [Scarpe::Promise] a promise that is fulfilled when the HTML change is complete
|
200
|
+
def destroy_self
|
201
|
+
@parent&.remove_child(self)
|
202
|
+
html_element.remove
|
203
|
+
end
|
204
|
+
|
205
|
+
# Request a full redraw of all widgets.
|
206
|
+
#
|
207
|
+
# It's really hard to do dirty-tracking here because the redraws are fully asynchronous.
|
208
|
+
# And so we can't easily cancel one "in flight," and we can't easily pick up the latest
|
209
|
+
# changes... And we probably don't want to, because we may be halfway through a batch.
|
210
|
+
#
|
211
|
+
# @return [void]
|
212
|
+
def needs_update!
|
213
|
+
WASMDisplayService.instance.app.request_redraw!
|
214
|
+
end
|
215
|
+
|
216
|
+
# Generate JS code to trigger a specific event name on this widget with the supplies arguments.
|
217
|
+
#
|
218
|
+
# @param handler_function_name [String] the event name - @see #bind
|
219
|
+
# @param args [Array] additional arguments that will be passed to the event in the generated JS
|
220
|
+
# @return [String] the generated JS code
|
221
|
+
def handler_js_code(handler_function_name, *args)
|
222
|
+
raise("Widget has no linkable_id! #{inspect}") unless linkable_id
|
223
|
+
|
224
|
+
js_args = ["'#{linkable_id}-#{handler_function_name}'", *args].join(", ")
|
225
|
+
"scarpeHandler(#{js_args})"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# #!/usr/bin/env ruby
|
2
|
+
# # frozen_string_literal: true
|
3
|
+
|
4
|
+
# require "socket"
|
5
|
+
|
6
|
+
# # Wherever this file is installed, locate Scarpe relative to it. This could be an installed gem or a dev repo.
|
7
|
+
# SCARPE_DIR = File.join(__dir__, "../..")
|
8
|
+
|
9
|
+
# $LOAD_PATH.prepend(SCARPE_DIR)
|
10
|
+
# require "scarpe"
|
11
|
+
# require "scarpe/wv_local"
|
12
|
+
|
13
|
+
# # This script exists to create a WebviewDisplayService that can be operated remotely over a socket.
|
14
|
+
|
15
|
+
# if ARGV.length != 1
|
16
|
+
# $stderr.puts("Usage: wv_display_worker.rb tcp_port_num")
|
17
|
+
# exit(-1)
|
18
|
+
# end
|
19
|
+
|
20
|
+
# # This is the implementation of a freestanding Scarpe Webview display server,
|
21
|
+
# # which connects via sockets and sends events and properties back and forth
|
22
|
+
# # with a display-less Shoes app. The interface is designed to allow fork-based
|
23
|
+
# # usage, where a parent process could create a paired sockets and start the
|
24
|
+
# # child server. It can also be used via TCP sockets or similar, where a single
|
25
|
+
# # socket is both input and output.
|
26
|
+
# class WebviewContainedService < Shoes::Linkable
|
27
|
+
# include Shoes::Log
|
28
|
+
# include Scarpe::WVRelayUtil # Needs Shoes::Log
|
29
|
+
|
30
|
+
# attr_reader :log
|
31
|
+
|
32
|
+
# # Create a new DisplayService.
|
33
|
+
# #
|
34
|
+
# # @param from [Socket] a readable socket to get input from the Shoes process
|
35
|
+
# # @param to [Socket] a writable socket on which to send output to the Shoes process
|
36
|
+
# def initialize(from, to)
|
37
|
+
# super()
|
38
|
+
# log_init("WV::DisplayWorker")
|
39
|
+
|
40
|
+
# @i_am = :child
|
41
|
+
# @event_subs = []
|
42
|
+
# @wv_display = Scarpe::WebviewDisplayService.new
|
43
|
+
|
44
|
+
# @from = from
|
45
|
+
# @to = to
|
46
|
+
|
47
|
+
# @init_done = false
|
48
|
+
|
49
|
+
# # Wait to register our periodic_code until the wrangler exists
|
50
|
+
# @event_subs << bind_shoes_event(event_name: "init") do
|
51
|
+
# @wv_display.wrangler.periodic_code("datagramProcessor", 0.1) do
|
52
|
+
# respond_to_datagram while ready_to_read?(0.0)
|
53
|
+
# end
|
54
|
+
# @init_done = true
|
55
|
+
# end
|
56
|
+
|
57
|
+
# # Subscribe to all event notifications and relay them to the opposite side
|
58
|
+
# @event_subs << bind_shoes_event(event_name: :any, target: :any) do |*args, **kwargs|
|
59
|
+
# unless kwargs[:relayed] || kwargs["relayed"]
|
60
|
+
# kwargs[:relayed] = true
|
61
|
+
# send_datagram({ type: :event, args:, kwargs: })
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
|
65
|
+
# # Run for 2.5 seconds to let the app be created and "run" to get called.
|
66
|
+
# # Once that happens, Webview will take over the event loop.
|
67
|
+
# event_loop_for(2.5)
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
|
71
|
+
# s = TCPSocket.new("localhost", ARGV[0].to_i)
|
72
|
+
|
73
|
+
# SERVICE = WebviewContainedService.new(s, s)
|
74
|
+
|
75
|
+
# SERVICE.log.info("Finished event loop. Exiting!")
|
data/lib/scarpe/wasm.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Scarpe WASM Display Services
|
4
|
+
|
5
|
+
require_relative "wasm/wasm_version"
|
6
|
+
|
7
|
+
require_relative "wasm/wasm_calls"
|
8
|
+
require_relative "wasm/web_wrangler"
|
9
|
+
require_relative "wasm/control_interface"
|
10
|
+
|
11
|
+
require_relative "wasm/widget"
|
12
|
+
require_relative "wasm/wasm_local_display"
|
13
|
+
|
14
|
+
require_relative "wasm/dimensions"
|
15
|
+
require_relative "wasm/html"
|
16
|
+
|
17
|
+
require_relative "wasm/spacing"
|
18
|
+
require_relative "wasm/star"
|
19
|
+
require_relative "wasm/radio"
|
20
|
+
require_relative "wasm/background"
|
21
|
+
require_relative "wasm/border"
|
22
|
+
|
23
|
+
require_relative "wasm/arc"
|
24
|
+
require_relative "wasm/font"
|
25
|
+
|
26
|
+
require_relative "wasm/app"
|
27
|
+
require_relative "wasm/para"
|
28
|
+
require_relative "wasm/slot"
|
29
|
+
require_relative "wasm/stack"
|
30
|
+
require_relative "wasm/flow"
|
31
|
+
require_relative "wasm/document_root"
|
32
|
+
require_relative "wasm/subscription_item"
|
33
|
+
require_relative "wasm/button"
|
34
|
+
require_relative "wasm/image"
|
35
|
+
require_relative "wasm/edit_box"
|
36
|
+
require_relative "wasm/edit_line"
|
37
|
+
require_relative "wasm/list_box"
|
38
|
+
require_relative "wasm/alert"
|
39
|
+
require_relative "wasm/span"
|
40
|
+
require_relative "wasm/shape"
|
41
|
+
|
42
|
+
require_relative "wasm/text_widget"
|
43
|
+
require_relative "wasm/link"
|
44
|
+
require_relative "wasm/line"
|
45
|
+
require_relative "wasm/video"
|
46
|
+
require_relative "wasm/check"
|
data/scarpe-wasm.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/scarpe/wasm/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "scarpe-wasm"
|
7
|
+
spec.version = Scarpe::Wasm::VERSION
|
8
|
+
spec.authors = ["Giovanni Borgh", "Noah Gibbs", "Scarpe Team"]
|
9
|
+
spec.email = ["the.codefolio.guy@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "A WASM display library for Scarpe."
|
12
|
+
#spec.description = "TODO: Write a longer description or delete this line."
|
13
|
+
spec.homepage = "https://github.com/scarpe-team/scarpe-wasm"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 3.2.0"
|
16
|
+
|
17
|
+
#spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
#spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
21
|
+
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
|
37
|
+
# For more information and examples about making a new gem, check out our
|
38
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
39
|
+
end
|