reflexion 0.3.10 → 0.3.11

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a97dc146f447fdbb146f25134371e56e3aa589769d9972c922c52ec12476c9ac
4
- data.tar.gz: bb35dfed3abf245004e67c1b7e044ee99c940d5e56a8af8a4ef712a8bf18d03a
3
+ metadata.gz: 9adc2b7a59858b027b34cddb25f70e0732c9253d7a5001191aa325dd388af3ef
4
+ data.tar.gz: 670815ce0dc0e7272f56eb3f278bd2f9990002f7860aad6300ed8bb6b6ca156b
5
5
  SHA512:
6
- metadata.gz: 4244aa39f3a5aab1ccca7bf747ae2aa5e6b919c787cba215057c20a2b5f27d4712e4be08e02c63642583859761eceaa085db785ba2e56bbb74bfb05973269c57
7
- data.tar.gz: a76ee0ccf4fc1c348b0e08bb24a6090de1e04ec20cfdc4e82aa77a9c137691840dbfe01c652c56a8a5650b640a560d55beed0f40af2988f89a52139b765ae19b
6
+ metadata.gz: ceed45697adeec1a3886e9561c2d6c5864da12083c04d28c8b25dfe2687162af712941549c56f394176c63cf4415ffba269c0e29f90d9135519c98349c6315fc
7
+ data.tar.gz: 5a22ee9b666211707365a31e8c3dd6191ac18b6088f6b5b06baad33b1c1663ec7edd29b5e8693ba5ccc2a1589fc836ec3ca46fd19d1099825d20e760e51c7e6a
@@ -119,7 +119,7 @@ VALUE get_key(VALUE self)
119
119
  CASE(LBRACKET): SYMBOL1(lbracket);
120
120
  CASE(RBRACKET): SYMBOL1(rbracket);
121
121
 
122
- #if defined(OSX) || defined(IOS) || defined(WIN32)
122
+ #if defined(OSX) || defined(IOS) || defined(WIN32) || defined(LINUX)
123
123
  CASE(ENTER): SYMBOL1(enter);
124
124
  #else
125
125
  CASE(ENTER): SYMBOL1(enter);
@@ -141,12 +141,12 @@ VALUE get_key(VALUE self)
141
141
  CASE(PAGEUP): SYMBOL1(pageup);
142
142
  CASE(PAGEDOWN): SYMBOL1(pagedown);
143
143
 
144
- #if !defined(OSX) && !defined(IOS)
144
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
145
145
  CASE(SHIFT):
146
146
  #endif
147
147
  CASE(LSHIFT):
148
148
  CASE(RSHIFT): SYMBOL1(shift);
149
- #if !defined(OSX) && !defined(IOS)
149
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
150
150
  CASE(CONTROL):
151
151
  #endif
152
152
  CASE(LCONTROL):
@@ -47,11 +47,7 @@ void Init_reflex_image_view ();
47
47
 
48
48
 
49
49
  extern "C" void
50
- #ifdef COCOAPODS
51
- Init_reflex_native ()
52
- #else
53
- Init_native ()
54
- #endif
50
+ Init_reflex_ext ()
55
51
  {
56
52
  RUCY_TRY
57
53
 
data/CLAUDE.md ADDED
@@ -0,0 +1,30 @@
1
+ # Reflex
2
+
3
+ GUI toolkit for Ruby. Event-driven architecture centered on Window and View.
4
+
5
+ ## Gem Name
6
+
7
+ The published gem name is **`reflexion`** (not `reflex`).
8
+
9
+ ## External Libraries
10
+
11
+ - Box2D v2.4.1 — Physics engine
12
+ - RtMidi 6.0.0 — MIDI device support
13
+
14
+ ## Platform-Specific Code
15
+
16
+ Under `src/`:
17
+ - `src/osx/` — macOS
18
+ - `src/ios/` — iOS
19
+ - `src/win32/` — Windows
20
+ - `src/sdl/` — Linux (SDL2)
21
+
22
+ ## Event System
23
+
24
+ 19+ event types (KeyEvent, PointerEvent, DrawEvent, MidiEvent, etc.).
25
+ View hook methods: `on_draw`, `on_update`, `on_key`, `on_pointer`, etc.
26
+
27
+ ## Testing
28
+
29
+ - `test_reflex_init.rb` must run alone (`TESTS_ALONE`)
30
+ - CI runs on macOS only (requires GUI)
data/ChangeLog.md CHANGED
@@ -1,6 +1,21 @@
1
1
  # reflex ChangeLog
2
2
 
3
3
 
4
+ ## [v0.3.11] - 2026-04-09
5
+
6
+ - Add minimal support for SDL2
7
+ - Add 'apt' for install_packages()
8
+ - Check is_blocked() before dispatching wheel event
9
+ - Change C-Extension name from 'native.so' to 'reflex_ext.so'
10
+ - Captured key and pointer events cannot prevent the event from being sent to the window
11
+ - Update dependencies
12
+
13
+ - Fix coordinate transformation for wheel event
14
+ - Fix touch continuity by using UITouch address as system ID (UP event coords may shift on iOS)
15
+ - Fix USB gamepad not detected on macOS 11+
16
+ - Fix inverted Y-axis stick button events in HID gamepad
17
+
18
+
4
19
  ## [v0.3.10] - 2025-07-06
5
20
 
6
21
  - Add deepwiki badge
data/Rakefile CHANGED
@@ -16,6 +16,8 @@ EXTENSIONS = [Xot, Rucy, Rays, Reflex]
16
16
  GEMNAME = 'reflexion'
17
17
  TESTS_ALONE = ['test/test_reflex_init.rb']
18
18
 
19
+ install_packages apt: %w[libsdl2-dev]
20
+
19
21
  use_external_library 'https://github.com/erincatto/box2d',
20
22
  tag: 'v2.4.1',
21
23
  incdirs: %w[include src],
@@ -29,6 +31,7 @@ use_external_library 'https://github.com/thestk/rtmidi',
29
31
  case
30
32
  when osx? then '__MACOSX_CORE__'
31
33
  when win32? then '__WINDOWS_MM__'
34
+ when linux? then '__LINUX_ALSA__'
32
35
  end
33
36
  }.call,
34
37
  &proc {sh %( mkdir rtmidi && cp RtMidi.* rtmidi )}
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.10
1
+ 0.3.11
@@ -15,13 +15,13 @@ Xot::ExtConf.new Xot, Rucy, Rays, Reflex do
15
15
  headers << 'ruby.h'
16
16
  libs.unshift 'gdi32', 'winmm', 'opengl32', 'glew32', 'xinput' if win32?
17
17
  frameworks << 'Cocoa' << 'GameController' if osx?
18
- $LDFLAGS << ' -Wl,--out-implib=native.dll.a' if mingw? || cygwin?
18
+ $LDFLAGS << ' -Wl,--out-implib=reflex_ext.dll.a' if mingw? || cygwin?
19
19
 
20
- unless osx?
20
+ unless osx? || linux?
21
21
  lib_dirs << Rays::Extension.ext_dir
22
- libs << 'rays/native'
22
+ libs << 'rays_ext'
23
23
  end
24
24
  end
25
25
 
26
- create_makefile 'reflex/native'
26
+ create_makefile 'reflex_ext'
27
27
  end
@@ -123,7 +123,7 @@ RUCY_DEF0(get_key)
123
123
  CASE(LBRACKET): SYMBOL1(lbracket);
124
124
  CASE(RBRACKET): SYMBOL1(rbracket);
125
125
 
126
- #if defined(OSX) || defined(IOS) || defined(WIN32)
126
+ #if defined(OSX) || defined(IOS) || defined(WIN32) || defined(LINUX)
127
127
  CASE(ENTER): SYMBOL1(enter);
128
128
  #else
129
129
  CASE(ENTER): SYMBOL1(enter);
@@ -145,12 +145,12 @@ RUCY_DEF0(get_key)
145
145
  CASE(PAGEUP): SYMBOL1(pageup);
146
146
  CASE(PAGEDOWN): SYMBOL1(pagedown);
147
147
 
148
- #if !defined(OSX) && !defined(IOS)
148
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
149
149
  CASE(SHIFT):
150
150
  #endif
151
151
  CASE(LSHIFT):
152
152
  CASE(RSHIFT): SYMBOL1(shift);
153
- #if !defined(OSX) && !defined(IOS)
153
+ #if !defined(OSX) && !defined(IOS) && !defined(LINUX)
154
154
  CASE(CONTROL):
155
155
  #endif
156
156
  CASE(LCONTROL):
@@ -47,11 +47,7 @@ void Init_reflex_image_view ();
47
47
 
48
48
 
49
49
  extern "C" void
50
- #ifdef COCOAPODS
51
- Init_reflex_native ()
52
- #else
53
- Init_native ()
54
- #endif
50
+ Init_reflex_ext ()
55
51
  {
56
52
  RUCY_TRY
57
53
 
@@ -21,7 +21,7 @@ namespace Reflex
21
21
 
22
22
  public:
23
23
 
24
- typedef int ID;
24
+ typedef uintptr_t ID;
25
25
 
26
26
  enum Type
27
27
  {
data/lib/reflex/ext.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  require 'rays/ext'
2
- require 'reflex/native'
2
+ require 'reflex_ext'
data/reflex.gemspec CHANGED
@@ -25,9 +25,9 @@ Gem::Specification.new do |s|
25
25
  s.platform = Gem::Platform::RUBY
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
- s.add_dependency 'xot', '~> 0.3.9', '>= 0.3.9'
29
- s.add_dependency 'rucy', '~> 0.3.9', '>= 0.3.9'
30
- s.add_dependency 'rays', '~> 0.3.9', '>= 0.3.9'
28
+ s.add_dependency 'xot', '~> 0.3.10'
29
+ s.add_dependency 'rucy', '~> 0.3.10'
30
+ s.add_dependency 'rays', '~> 0.3.10'
31
31
 
32
32
  s.files = `git ls-files`.split $/
33
33
  s.executables = s.files.grep(%r{^bin/}) {|f| File.basename f}
data/src/event.cpp CHANGED
@@ -1,6 +1,7 @@
1
1
  #include "event.h"
2
2
 
3
3
 
4
+ #include <string.h>
4
5
  #include <algorithm>
5
6
  #include "reflex/device.h"
6
7
  #include "reflex/timer.h"
data/src/gamepad.cpp CHANGED
@@ -1,6 +1,7 @@
1
1
  #include "gamepad.h"
2
2
 
3
3
 
4
+ #include <string.h>
4
5
  #include <algorithm>
5
6
  #include "reflex/exception.h"
6
7
  #include "application.h"
@@ -87,6 +88,21 @@ namespace Reflex
87
88
  return it->get();
88
89
  }
89
90
 
91
+ Gamepad*
92
+ Gamepad_find_by_name (const char* name)
93
+ {
94
+ if (!name)
95
+ return NULL;
96
+
97
+ auto it = std::find_if(
98
+ gamepads.begin(), gamepads.end(),
99
+ [&](auto& gamepad) {return strcmp(gamepad->name(), name) == 0;});
100
+ if (it == gamepads.end())
101
+ return NULL;
102
+
103
+ return it->get();
104
+ }
105
+
90
106
  float
91
107
  Gamepad_get_button_press_threshold ()
92
108
  {
data/src/gamepad.h CHANGED
@@ -58,6 +58,8 @@ namespace Reflex
58
58
 
59
59
  Gamepad* Gamepad_find (void* handle);
60
60
 
61
+ Gamepad* Gamepad_find_by_name (const char* name);
62
+
61
63
  float Gamepad_get_button_press_threshold ();
62
64
 
63
65
 
data/src/ios/event.mm CHANGED
@@ -92,6 +92,7 @@ namespace Reflex
92
92
  (uint) touch.tapCount,
93
93
  action == Pointer::MOVE,
94
94
  touch.timestamp);
95
+ Pointer_set_system_id(&pointer, (Pointer::ID) touch);
95
96
 
96
97
  if (pointer.action() != Pointer::DOWN)
97
98
  {
data/src/midi.cpp CHANGED
@@ -316,6 +316,7 @@ namespace Reflex
316
316
  if (!manager)
317
317
  invalid_state_error(__FILE__, __LINE__);
318
318
 
319
+ midis.clear();
319
320
  manager.reset();
320
321
  }
321
322
 
@@ -149,7 +149,7 @@ namespace Reflex
149
149
  handle_gamepad_events (Gamepad* gamepad, GCController* controller)
150
150
  {
151
151
  GCExtendedGamepad* g = controller.extendedGamepad;
152
- if (!gamepad) return;
152
+ if (!g) return;
153
153
 
154
154
  static const Gamepad::Index L = Gamepad::INDEX_LEFT, R = Gamepad::INDEX_RIGHT;
155
155
 
@@ -9,6 +9,7 @@
9
9
  #import <IOKit/hid/IOHIDDevice.h>
10
10
  #import <GameController/GameController.h>
11
11
  #include <xot/util.h>
12
+ #include "reflex/application.h"
12
13
  #include "reflex/exception.h"
13
14
  #include "reflex/debug.h"
14
15
  #include "event.h"
@@ -247,7 +248,7 @@ namespace Reflex
247
248
  }
248
249
 
249
250
  static void
250
- handle_disconnect_event (void* context, IOReturn result, void* sender)
251
+ unregister_device (void* context, IOReturn result, void* sender)
251
252
  {
252
253
  IOHIDDeviceRef device = (IOHIDDeviceRef) context;
253
254
 
@@ -255,6 +256,62 @@ namespace Reflex
255
256
  unregister_to_device_map(device);
256
257
  }
257
258
 
259
+ static void
260
+ register_device (Application* app, IOHIDDeviceRef device)
261
+ {
262
+ IOHIDDeviceRegisterRemovalCallback(device, unregister_device, device);
263
+ register_to_device_map(device);
264
+ add_gamepad(app, device);
265
+ }
266
+
267
+ struct PendingDevice
268
+ {
269
+
270
+ Application::Ref app;
271
+
272
+ IOHIDDeviceRef device;
273
+
274
+ String name;
275
+
276
+ PendingDevice (Application* app, IOHIDDeviceRef device, const char* name)
277
+ : app(app), device(device), name(name)
278
+ {
279
+ CFRetain(device);
280
+ }
281
+
282
+ ~PendingDevice ()
283
+ {
284
+ CFRelease(device);
285
+ }
286
+
287
+ };// PendingDevice
288
+
289
+ static std::vector<std::shared_ptr<PendingDevice>> pending_devices;
290
+
291
+ static void
292
+ register_pending_devices ()
293
+ {
294
+ for (auto& pending : pending_devices)
295
+ {
296
+ if (!Gamepad_find_by_name(pending->name.c_str()))
297
+ register_device(pending->app, pending->device);
298
+ }
299
+ pending_devices.clear();
300
+ }
301
+
302
+ static void
303
+ queue_device (Application* app, IOHIDDeviceRef device, const char* name)
304
+ {
305
+ pending_devices.push_back(std::make_shared<PendingDevice>(app, device, name));
306
+ if (pending_devices.size() == 1)
307
+ {
308
+ dispatch_after(
309
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t) (0.5 * NSEC_PER_SEC)),
310
+ dispatch_get_main_queue(),
311
+ ^{register_pending_devices();});
312
+ }
313
+ }
314
+
258
315
  static void
259
316
  handle_connect_event (
260
317
  void* context, IOReturn result, void* sender, IOHIDDeviceRef device)
@@ -267,13 +324,13 @@ namespace Reflex
267
324
  if (@available(macOS 11.0, *))
268
325
  {
269
326
  if ([GCController supportsHIDDevice: device])
270
- return;
327
+ {
328
+ String name = get_string_property(device, CFSTR(kIOHIDProductKey));
329
+ return queue_device(app, device, name);
330
+ }
271
331
  }
272
332
 
273
- IOHIDDeviceRegisterRemovalCallback(device, handle_disconnect_event, device);
274
-
275
- register_to_device_map(device);
276
- add_gamepad(app, device);
333
+ register_device(app, device);
277
334
  }
278
335
 
279
336
  enum
@@ -411,9 +468,9 @@ namespace Reflex
411
468
  switch (usage)
412
469
  {
413
470
  case kHIDUsage_GD_X: HANDLE_DPAD(LSTICK, LEFT, RIGHT, sticks[0].x, centered); break;
414
- case kHIDUsage_GD_Y: HANDLE_DPAD(LSTICK, UP, DOWN, sticks[0].y, -centered); break;
471
+ case kHIDUsage_GD_Y: HANDLE_DPAD(LSTICK, DOWN, UP, sticks[0].y, -centered); break;
415
472
  case kHIDUsage_GD_Rx: HANDLE_DPAD(RSTICK, LEFT, RIGHT, sticks[1].x, centered); break;
416
- case kHIDUsage_GD_Ry: HANDLE_DPAD(RSTICK, UP, DOWN, sticks[1].y, -centered); break;
473
+ case kHIDUsage_GD_Ry: HANDLE_DPAD(RSTICK, DOWN, UP, sticks[1].y, -centered); break;
417
474
  case kHIDUsage_GD_Z: HANDLE_TRIGGER(LTRIGGER, triggers[0], linear); break;
418
475
  case kHIDUsage_GD_Rz: HANDLE_TRIGGER(RTRIGGER, triggers[1], linear); break;
419
476
  }
@@ -423,9 +480,9 @@ namespace Reflex
423
480
  switch (usage)
424
481
  {
425
482
  case kHIDUsage_GD_X: HANDLE_DPAD(LSTICK, LEFT, RIGHT, sticks[0].x, centered); break;
426
- case kHIDUsage_GD_Y: HANDLE_DPAD(LSTICK, UP, DOWN, sticks[0].y, -centered); break;
483
+ case kHIDUsage_GD_Y: HANDLE_DPAD(LSTICK, DOWN, UP, sticks[0].y, -centered); break;
427
484
  case kHIDUsage_GD_Z: HANDLE_DPAD(RSTICK, LEFT, RIGHT, sticks[1].x, centered); break;
428
- case kHIDUsage_GD_Rz: HANDLE_DPAD(RSTICK, UP, DOWN, sticks[1].y, -centered); break;
485
+ case kHIDUsage_GD_Rz: HANDLE_DPAD(RSTICK, DOWN, UP, sticks[1].y, -centered); break;
429
486
  case kHIDUsage_GD_Rx: HANDLE_TRIGGER(LTRIGGER, triggers[0], linear); break;
430
487
  case kHIDUsage_GD_Ry: HANDLE_TRIGGER(RTRIGGER, triggers[1], linear); break;
431
488
  }
@@ -435,7 +492,7 @@ namespace Reflex
435
492
  switch (usage)
436
493
  {
437
494
  case kHIDUsage_GD_X: HANDLE_DPAD(LSTICK, LEFT, RIGHT, sticks[0].x, centered); break;
438
- case kHIDUsage_GD_Y: HANDLE_DPAD(LSTICK, UP, DOWN, sticks[0].y, -centered); break;
495
+ case kHIDUsage_GD_Y: HANDLE_DPAD(LSTICK, DOWN, UP, sticks[0].y, -centered); break;
439
496
  }
440
497
  break;
441
498
  }
@@ -555,6 +612,8 @@ namespace Reflex
555
612
  if (!manager)
556
613
  invalid_state_error(__FILE__, __LINE__);
557
614
 
615
+ pending_devices.clear();
616
+
558
617
  IOHIDManagerUnscheduleFromRunLoop(
559
618
  manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
560
619
  IOHIDManagerClose(manager, kIOHIDOptionsTypeNone);
data/src/queue.h CHANGED
@@ -58,7 +58,7 @@ namespace Reflex
58
58
 
59
59
  std::queue<T> queue;
60
60
 
61
- std::mutex mutex;
61
+ mutable std::mutex mutex;
62
62
 
63
63
  std::condition_variable condvar;
64
64
 
@@ -0,0 +1,124 @@
1
+ #include "../application.h"
2
+
3
+
4
+ #include <SDL.h>
5
+ #include <xot/time.h>
6
+ #include "reflex/exception.h"
7
+ #include "reflex/debug.h"
8
+ #include "window.h"
9
+
10
+
11
+ namespace Reflex
12
+ {
13
+
14
+
15
+ struct ApplicationData : public Application::Data
16
+ {
17
+
18
+ bool quit = false;
19
+
20
+ };// ApplicationData
21
+
22
+
23
+ static ApplicationData*
24
+ get_data (Application* app)
25
+ {
26
+ return (ApplicationData*) app->self.get();
27
+ }
28
+
29
+ static const ApplicationData*
30
+ get_data (const Application* app)
31
+ {
32
+ return get_data(const_cast<Application*>(app));
33
+ }
34
+
35
+ Application::Data*
36
+ Application_create_data ()
37
+ {
38
+ return new ApplicationData();
39
+ }
40
+
41
+
42
+ static bool
43
+ dispatch_window_event (const SDL_Event& event)
44
+ {
45
+ SDL_Window* native = SDL_GetWindowFromID(event.key.windowID);
46
+ if (!native) return false;
47
+
48
+ Window* win = Window_from(native);
49
+ if (!win) return false;
50
+
51
+ return Window_dispatch_event(win, event);
52
+ }
53
+
54
+ static bool
55
+ dispatch_events ()
56
+ {
57
+ SDL_Event event;
58
+ while (SDL_PollEvent(&event))
59
+ {
60
+ if (dispatch_window_event(event))
61
+ continue;
62
+
63
+ switch (event.type)
64
+ {
65
+ case SDL_QUIT: return false;
66
+ }
67
+ }
68
+
69
+ return true;
70
+ }
71
+
72
+ static void
73
+ update_all_windows (Application* app)
74
+ {
75
+ for (auto it = app->window_begin(), end = app->window_end(); it != end; ++it)
76
+ Window_update(it->get());
77
+ }
78
+
79
+ void
80
+ Application::start ()
81
+ {
82
+ Event e;
83
+ Application_call_start(this, &e);
84
+
85
+ ApplicationData* self = get_data(this);
86
+
87
+ double prev = Xot::time();
88
+ while (!self->quit)
89
+ {
90
+ if (!dispatch_events()) break;
91
+
92
+ static const double INTERVAL = 1.0 / 60.0;
93
+ static const double SLEEPABLE = INTERVAL * 0.9;
94
+
95
+ double now = Xot::time();
96
+ double dt = now - prev;
97
+ if (dt < INTERVAL)
98
+ {
99
+ if (dt < SLEEPABLE) SDL_Delay(1);
100
+ continue;
101
+ }
102
+
103
+ update_all_windows(this);
104
+ prev = now;
105
+ }
106
+ }
107
+
108
+ void
109
+ Application::quit ()
110
+ {
111
+ Event e;
112
+ Application_call_quit(this, &e);
113
+ if (e.is_blocked()) return;
114
+
115
+ get_data(this)->quit = true;
116
+ }
117
+
118
+ void
119
+ Application::on_about (Event* e)
120
+ {
121
+ }
122
+
123
+
124
+ }// Reflex
@@ -0,0 +1,18 @@
1
+ #include "reflex/device.h"
2
+
3
+
4
+ #include "reflex/exception.h"
5
+
6
+
7
+ namespace Reflex
8
+ {
9
+
10
+
11
+ void
12
+ vibrate ()
13
+ {
14
+ not_implemented_error(__FILE__, __LINE__);
15
+ }
16
+
17
+
18
+ }// Reflex
@@ -0,0 +1,27 @@
1
+ #include "../gamepad.h"
2
+
3
+
4
+ #include "reflex/exception.h"
5
+
6
+
7
+ namespace Reflex
8
+ {
9
+
10
+
11
+ void
12
+ Gamepad_init (Application* app)
13
+ {
14
+ }
15
+
16
+ void
17
+ Gamepad_fin (Application* app)
18
+ {
19
+ }
20
+
21
+ void
22
+ Gamepad_poll ()
23
+ {
24
+ }
25
+
26
+
27
+ }// Reflex