rbgl 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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/LICENSE +21 -0
  4. data/README.md +123 -0
  5. data/Rakefile +12 -0
  6. data/examples/array_test.rb +99 -0
  7. data/examples/chemical_heartbeat.rb +166 -0
  8. data/examples/color_test.rb +61 -0
  9. data/examples/cube_spinning.rb +87 -0
  10. data/examples/dark_transit.rb +166 -0
  11. data/examples/fractured_orb.rb +428 -0
  12. data/examples/fractured_orb_rb.rb +598 -0
  13. data/examples/gradient.rb +84 -0
  14. data/examples/hexagonal_flow.rb +333 -0
  15. data/examples/multi_return_test.rb +98 -0
  16. data/examples/plasma.rb +78 -0
  17. data/examples/sphere_raymarch.rb +126 -0
  18. data/examples/teapot.rb +362 -0
  19. data/examples/teapot_mcu.rb +344 -0
  20. data/examples/triangle_basic.rb +36 -0
  21. data/examples/triangle_window.rb +62 -0
  22. data/examples/window_test.rb +36 -0
  23. data/lib/rbgl/engine/buffer.rb +160 -0
  24. data/lib/rbgl/engine/context.rb +157 -0
  25. data/lib/rbgl/engine/framebuffer.rb +115 -0
  26. data/lib/rbgl/engine/pipeline.rb +35 -0
  27. data/lib/rbgl/engine/rasterizer.rb +213 -0
  28. data/lib/rbgl/engine/shader.rb +324 -0
  29. data/lib/rbgl/engine/texture.rb +125 -0
  30. data/lib/rbgl/engine.rb +15 -0
  31. data/lib/rbgl/gui/backend.rb +76 -0
  32. data/lib/rbgl/gui/cocoa/backend.rb +121 -0
  33. data/lib/rbgl/gui/event.rb +34 -0
  34. data/lib/rbgl/gui/file_backend.rb +91 -0
  35. data/lib/rbgl/gui/wayland/backend.rb +126 -0
  36. data/lib/rbgl/gui/wayland/connection.rb +331 -0
  37. data/lib/rbgl/gui/window.rb +148 -0
  38. data/lib/rbgl/gui/x11/backend.rb +156 -0
  39. data/lib/rbgl/gui/x11/connection.rb +344 -0
  40. data/lib/rbgl/gui.rb +16 -0
  41. data/lib/rbgl/version.rb +5 -0
  42. data/lib/rbgl.rb +6 -0
  43. metadata +114 -0
@@ -0,0 +1,344 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+
5
+ module RBGL
6
+ module GUI
7
+ module X11
8
+ class Connection
9
+ attr_reader :default_screen, :resource_id_base, :resource_id_mask
10
+ attr_reader :root, :root_depth, :root_visual, :white_pixel, :black_pixel
11
+
12
+ def initialize(display_name)
13
+ host, display_num, _screen_num = parse_display_name(display_name)
14
+ @socket = connect(host, display_num)
15
+ @next_seq = 1
16
+ @resource_id_counter = 0
17
+ @pending_events = []
18
+
19
+ handshake
20
+ end
21
+
22
+ def generate_id
23
+ id = @resource_id_base | @resource_id_counter
24
+ @resource_id_counter += 1
25
+ id
26
+ end
27
+
28
+ def flush
29
+ @socket.flush
30
+ end
31
+
32
+ def pending
33
+ ready = IO.select([@socket], nil, nil, 0)
34
+ ready ? 1 : 0
35
+ end
36
+
37
+ def create_window(depth:, wid:, parent:, x:, y:, width:, height:,
38
+ border_width:, window_class:, visual:, value_mask:, values:)
39
+ class_val = case window_class
40
+ when :input_output then 1
41
+ when :input_only then 2
42
+ else 0
43
+ end
44
+
45
+ mask = 0
46
+ value_list = []
47
+
48
+ if value_mask.include?(:back_pixel)
49
+ mask |= 0x0002
50
+ value_list << values[:back_pixel]
51
+ end
52
+
53
+ if value_mask.include?(:event_mask)
54
+ mask |= 0x0800
55
+ event_mask = 0
56
+ values[:event_mask].each do |ev|
57
+ event_mask |= case ev
58
+ when :exposure then 0x8000
59
+ when :key_press then 0x0001
60
+ when :key_release then 0x0002
61
+ when :button_press then 0x0004
62
+ when :button_release then 0x0008
63
+ when :pointer_motion then 0x0040
64
+ when :structure_notify then 0x020000
65
+ else 0
66
+ end
67
+ end
68
+ value_list << event_mask
69
+ end
70
+
71
+ request = [
72
+ depth,
73
+ wid,
74
+ parent,
75
+ x, y,
76
+ width, height,
77
+ border_width,
78
+ class_val,
79
+ visual,
80
+ mask
81
+ ].pack("CVVSSSSSVV") + value_list.pack("V*")
82
+
83
+ send_request(1, request)
84
+ end
85
+
86
+ def map_window(wid)
87
+ send_request(8, [wid].pack("V"))
88
+ end
89
+
90
+ def destroy_window(wid)
91
+ send_request(4, [wid].pack("V"))
92
+ end
93
+
94
+ def create_gc(gc_id, drawable, values = {})
95
+ mask = 0
96
+ value_list = []
97
+
98
+ if values[:foreground]
99
+ mask |= 0x0004
100
+ value_list << values[:foreground]
101
+ end
102
+
103
+ if values[:background]
104
+ mask |= 0x0008
105
+ value_list << values[:background]
106
+ end
107
+
108
+ request = [gc_id, drawable, mask].pack("VVV") + value_list.pack("V*")
109
+ send_request(55, request)
110
+ end
111
+
112
+ def put_image(format:, drawable:, gc:, width:, height:, dst_x:, dst_y:, depth:, data:)
113
+ format_byte = case format
114
+ when :bitmap then 0
115
+ when :xy_pixmap then 1
116
+ when :z_pixmap then 2
117
+ else 2
118
+ end
119
+
120
+ left_pad = 0
121
+
122
+ header = [
123
+ drawable,
124
+ gc,
125
+ width, height,
126
+ dst_x, dst_y,
127
+ left_pad,
128
+ depth
129
+ ].pack("VVvvvvCC") + "\x00\x00"
130
+
131
+ send_request_with_data(72, format_byte, header, data)
132
+ end
133
+
134
+ def change_property(window, property, type, data, mode: :replace)
135
+ mode_val = case mode
136
+ when :replace then 0
137
+ when :prepend then 1
138
+ when :append then 2
139
+ else 0
140
+ end
141
+
142
+ property_atom = get_atom(property)
143
+ type_atom = get_atom(type)
144
+
145
+ format = 8
146
+ data_bytes = data.to_s
147
+
148
+ request = [
149
+ window,
150
+ property_atom,
151
+ type_atom,
152
+ format,
153
+ data_bytes.bytesize
154
+ ].pack("VVVCV") + "\x00\x00\x00" + pad_to_4(data_bytes)
155
+
156
+ send_request(18, request, mode_val)
157
+ end
158
+
159
+ def intern_atom(name, only_if_exists: false)
160
+ request = [
161
+ name.bytesize,
162
+ 0
163
+ ].pack("vv") + pad_to_4(name)
164
+
165
+ send_request(16, request, only_if_exists ? 1 : 0)
166
+ flush
167
+
168
+ reply = read_reply
169
+ return 0 unless reply
170
+
171
+ reply[8, 4].unpack1("V")
172
+ end
173
+
174
+ def next_event
175
+ return @pending_events.shift unless @pending_events.empty?
176
+
177
+ read_events
178
+ @pending_events.shift
179
+ end
180
+
181
+ def read_events
182
+ while pending > 0
183
+ event = read_event
184
+ @pending_events << event if event
185
+ end
186
+ end
187
+
188
+ private
189
+
190
+ def parse_display_name(name)
191
+ if name =~ /^(?:(.+):)?(\d+)(?:\.(\d+))?$/
192
+ [::Regexp.last_match(1), ::Regexp.last_match(2).to_i, (::Regexp.last_match(3) || 0).to_i]
193
+ else
194
+ raise "Invalid display name: #{name}"
195
+ end
196
+ end
197
+
198
+ def connect(host, display_num)
199
+ if host.nil? || host.empty? || host == "unix"
200
+ socket_path = "/tmp/.X11-unix/X#{display_num}"
201
+ UNIXSocket.new(socket_path)
202
+ else
203
+ TCPSocket.new(host, 6000 + display_num)
204
+ end
205
+ end
206
+
207
+ def handshake
208
+ init_request = [
209
+ 0x6C,
210
+ 0,
211
+ 11, 0,
212
+ 0, 0,
213
+ 0, 0
214
+ ].pack("CCvvvvvv")
215
+
216
+ @socket.write(init_request)
217
+ @socket.flush
218
+
219
+ header = @socket.read(8)
220
+ status = header.unpack1("C")
221
+
222
+ raise "X11 connection failed" unless status == 1
223
+
224
+ additional_length = header[6, 2].unpack1("v")
225
+ data = @socket.read(additional_length * 4)
226
+
227
+ parse_server_info(data)
228
+ end
229
+
230
+ def parse_server_info(data)
231
+ @resource_id_base = data[4, 4].unpack1("V")
232
+ @resource_id_mask = data[8, 4].unpack1("V")
233
+
234
+ vendor_length = data[16, 2].unpack1("v")
235
+ num_screens = data[20, 1].unpack1("C")
236
+ num_formats = data[21, 1].unpack1("C")
237
+
238
+ offset = 32 + pad_length(vendor_length) + num_formats * 8
239
+
240
+ if num_screens > 0
241
+ @root = data[offset, 4].unpack1("V")
242
+ @root_depth = data[offset + 38, 1].unpack1("C")
243
+ @root_visual = data[offset + 32, 4].unpack1("V")
244
+ @white_pixel = data[offset + 8, 4].unpack1("V")
245
+ @black_pixel = data[offset + 12, 4].unpack1("V")
246
+ end
247
+ end
248
+
249
+ def send_request(opcode, data, extra = 0)
250
+ length = (4 + data.bytesize + 3) / 4
251
+ header = [opcode, extra, length].pack("CCv")
252
+
253
+ padding_size = length * 4 - 4 - data.bytesize
254
+ padded_data = data + ("\x00" * padding_size)
255
+
256
+ @socket.write(header + padded_data)
257
+ @next_seq += 1
258
+ end
259
+
260
+ def send_request_with_data(opcode, extra, header_data, bulk_data)
261
+ total_data = header_data + bulk_data
262
+ length = (4 + total_data.bytesize + 3) / 4
263
+
264
+ header = [opcode, extra, length].pack("CCv")
265
+ padding_size = length * 4 - 4 - total_data.bytesize
266
+ padded = total_data + ("\x00" * padding_size)
267
+
268
+ @socket.write(header + padded)
269
+ @next_seq += 1
270
+ end
271
+
272
+ def read_reply
273
+ header = @socket.read(32)
274
+ return nil unless header && header.bytesize == 32
275
+
276
+ additional = header[4, 4].unpack1("V")
277
+ if additional > 0
278
+ header + @socket.read(additional * 4)
279
+ else
280
+ header
281
+ end
282
+ end
283
+
284
+ def read_event
285
+ data = @socket.read(32)
286
+ return nil unless data && data.bytesize == 32
287
+
288
+ event_type = data.unpack1("C") & 0x7F
289
+
290
+ case event_type
291
+ when 2
292
+ keycode = data[1, 1].unpack1("C")
293
+ { type: :key_press, keycode: keycode }
294
+ when 3
295
+ keycode = data[1, 1].unpack1("C")
296
+ { type: :key_release, keycode: keycode }
297
+ when 4
298
+ x, y = data[24, 4].unpack("ss")
299
+ button = data[1, 1].unpack1("C")
300
+ { type: :button_press, x: x, y: y, button: button }
301
+ when 5
302
+ x, y = data[24, 4].unpack("ss")
303
+ button = data[1, 1].unpack1("C")
304
+ { type: :button_release, x: x, y: y, button: button }
305
+ when 6
306
+ x, y = data[24, 4].unpack("ss")
307
+ { type: :motion_notify, x: x, y: y }
308
+ when 12
309
+ { type: :exposure }
310
+ when 22
311
+ width, height = data[20, 4].unpack("vv")
312
+ { type: :configure_notify, width: width, height: height }
313
+ when 33
314
+ window = data[4, 4].unpack1("V")
315
+ atom = data[8, 4].unpack1("V")
316
+ { type: :client_message, window: window, data: atom }
317
+ else
318
+ { type: :unknown, code: event_type }
319
+ end
320
+ end
321
+
322
+ def get_atom(name)
323
+ case name
324
+ when :wm_name then 39
325
+ when :string then 31
326
+ when :wm_protocols then intern_atom("WM_PROTOCOLS")
327
+ when :wm_delete_window then intern_atom("WM_DELETE_WINDOW")
328
+ else
329
+ name.is_a?(Integer) ? name : intern_atom(name.to_s)
330
+ end
331
+ end
332
+
333
+ def pad_to_4(str)
334
+ padding = (4 - str.bytesize % 4) % 4
335
+ str + ("\x00" * padding)
336
+ end
337
+
338
+ def pad_length(len)
339
+ ((len + 3) / 4) * 4
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
data/lib/rbgl/gui.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gui/event"
4
+ require_relative "gui/backend"
5
+ require_relative "gui/file_backend"
6
+ require_relative "gui/window"
7
+
8
+ module RBGL
9
+ module GUI
10
+ VERSION = "0.1.0"
11
+
12
+ def self.load_tk_backend
13
+ require_relative "gui/tk_backend"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBGL
4
+ VERSION = "0.1.0"
5
+ end
data/lib/rbgl.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "larb"
4
+ require_relative "rbgl/version"
5
+ require_relative "rbgl/engine"
6
+ require_relative "rbgl/gui"
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbgl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: larb
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rlsl
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: A pure Ruby graphics library with software rendering engine and cross-platform
41
+ GUI support (X11, Wayland, Cocoa).
42
+ email:
43
+ - t.yudai92@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - examples/array_test.rb
53
+ - examples/chemical_heartbeat.rb
54
+ - examples/color_test.rb
55
+ - examples/cube_spinning.rb
56
+ - examples/dark_transit.rb
57
+ - examples/fractured_orb.rb
58
+ - examples/fractured_orb_rb.rb
59
+ - examples/gradient.rb
60
+ - examples/hexagonal_flow.rb
61
+ - examples/multi_return_test.rb
62
+ - examples/plasma.rb
63
+ - examples/sphere_raymarch.rb
64
+ - examples/teapot.rb
65
+ - examples/teapot_mcu.rb
66
+ - examples/triangle_basic.rb
67
+ - examples/triangle_window.rb
68
+ - examples/window_test.rb
69
+ - lib/rbgl.rb
70
+ - lib/rbgl/engine.rb
71
+ - lib/rbgl/engine/buffer.rb
72
+ - lib/rbgl/engine/context.rb
73
+ - lib/rbgl/engine/framebuffer.rb
74
+ - lib/rbgl/engine/pipeline.rb
75
+ - lib/rbgl/engine/rasterizer.rb
76
+ - lib/rbgl/engine/shader.rb
77
+ - lib/rbgl/engine/texture.rb
78
+ - lib/rbgl/gui.rb
79
+ - lib/rbgl/gui/backend.rb
80
+ - lib/rbgl/gui/cocoa/backend.rb
81
+ - lib/rbgl/gui/event.rb
82
+ - lib/rbgl/gui/file_backend.rb
83
+ - lib/rbgl/gui/wayland/backend.rb
84
+ - lib/rbgl/gui/wayland/connection.rb
85
+ - lib/rbgl/gui/window.rb
86
+ - lib/rbgl/gui/x11/backend.rb
87
+ - lib/rbgl/gui/x11/connection.rb
88
+ - lib/rbgl/version.rb
89
+ homepage: https://github.com/ydah/rbgl
90
+ licenses:
91
+ - MIT
92
+ metadata:
93
+ homepage_uri: https://github.com/ydah/rbgl
94
+ source_code_uri: https://github.com/ydah/rbgl
95
+ changelog_uri: https://github.com/ydah/rbgl/blob/main/CHANGELOG.md
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.1.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 4.0.3
111
+ specification_version: 4
112
+ summary: RuBy Graphics Library - Pure Ruby software rendering with cross-platform
113
+ GUI
114
+ test_files: []