reflexion 0.3.4 → 0.3.6

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/reflex/application.cpp +16 -0
  3. data/.doc/ext/reflex/device.cpp +46 -3
  4. data/.doc/ext/reflex/device_event.cpp +62 -0
  5. data/.doc/ext/reflex/key_event.cpp +50 -0
  6. data/.doc/ext/reflex/native.cpp +6 -4
  7. data/.doc/ext/reflex/reflex.cpp +24 -0
  8. data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
  9. data/CONTRIBUTING.md +7 -0
  10. data/ChangeLog.md +20 -0
  11. data/README.md +46 -2
  12. data/VERSION +1 -1
  13. data/ext/reflex/application.cpp +18 -0
  14. data/ext/reflex/device.cpp +48 -3
  15. data/ext/reflex/device_event.cpp +65 -0
  16. data/ext/reflex/extconf.rb +3 -3
  17. data/ext/reflex/key_event.cpp +50 -0
  18. data/ext/reflex/native.cpp +6 -4
  19. data/ext/reflex/reflex.cpp +24 -0
  20. data/include/reflex/application.h +4 -0
  21. data/include/reflex/defs.h +60 -0
  22. data/include/reflex/device.h +22 -0
  23. data/include/reflex/event.h +25 -0
  24. data/include/reflex/gamepad.h +175 -0
  25. data/include/reflex/ruby/application.h +18 -0
  26. data/include/reflex/ruby/device.h +40 -0
  27. data/include/reflex/ruby/event.h +11 -0
  28. data/reflex.gemspec +3 -3
  29. data/src/application.cpp +67 -0
  30. data/src/application.h +9 -0
  31. data/src/device.cpp +24 -0
  32. data/src/event.cpp +38 -0
  33. data/src/gamepad.cpp +176 -0
  34. data/src/gamepad.h +74 -0
  35. data/src/ios/app_delegate.mm +2 -2
  36. data/src/ios/application.mm +0 -25
  37. data/src/ios/event.mm +2 -3
  38. data/src/ios/gamepad.mm +313 -0
  39. data/src/osx/app_delegate.mm +2 -2
  40. data/src/osx/application.mm +0 -25
  41. data/src/osx/event.h +3 -0
  42. data/src/osx/event.mm +11 -3
  43. data/src/osx/gamepad.mm +40 -0
  44. data/src/osx/gamepad_gc.mm +299 -0
  45. data/src/osx/gamepad_hid.mm +567 -0
  46. data/src/view.cpp +52 -18
  47. data/src/win32/application.cpp +5 -26
  48. data/src/win32/event.cpp +8 -7
  49. data/src/win32/event.h +5 -0
  50. data/src/win32/gamepad.cpp +110 -0
  51. data/src/win32/gamepad.h +20 -0
  52. data/src/win32/window.cpp +9 -1
  53. data/src/window.cpp +15 -0
  54. data/src/window.h +9 -0
  55. metadata +30 -14
@@ -0,0 +1,567 @@
1
+ // -*- objc -*-
2
+ #include "../gamepad.h"
3
+
4
+
5
+ #include <memory>
6
+ #include <vector>
7
+ #include <map>
8
+ #import <IOKit/hid/IOHIDManager.h>
9
+ #import <IOKit/hid/IOHIDDevice.h>
10
+ #import <GameController/GameController.h>
11
+ #include <xot/util.h>
12
+ #include "reflex/exception.h"
13
+ #include "reflex/debug.h"
14
+ #include "event.h"
15
+ #include "window.h"
16
+
17
+
18
+ namespace Reflex
19
+ {
20
+
21
+
22
+ static int
23
+ get_int_property (IOHIDDeviceRef device, CFStringRef key)
24
+ {
25
+ CFNumberRef ref = (CFNumberRef) IOHIDDeviceGetProperty(device, key);
26
+ if (!ref) return 0;
27
+
28
+ int value = 0;
29
+ CFNumberGetValue(ref, kCFNumberIntType, &value);
30
+ return value;
31
+ }
32
+
33
+ static String
34
+ get_string_property (IOHIDDeviceRef device, CFStringRef key)
35
+ {
36
+ return Xot::to_s((CFStringRef) IOHIDDeviceGetProperty(device, key));
37
+ }
38
+
39
+
40
+ struct HIDGamepadData : Gamepad::Data
41
+ {
42
+
43
+ typedef Gamepad::Data Super;
44
+
45
+ enum Mapping {RSTICK_UNKNOWN, RSTICK_RxRy, RSTICK_ZRz};
46
+
47
+ IOHIDDeviceRef device;
48
+
49
+ Mapping mapping = RSTICK_UNKNOWN;
50
+
51
+ CFIndex prev_hatswitch = 8;// neutral
52
+
53
+ mutable String name_cache;
54
+
55
+ HIDGamepadData (IOHIDDeviceRef device)
56
+ : device(device)
57
+ {
58
+ if (!device)
59
+ argument_error(__FILE__, __LINE__);
60
+
61
+ CFRetain(device);
62
+ prev.reset(new Gamepad());
63
+ }
64
+
65
+ ~HIDGamepadData ()
66
+ {
67
+ CFRelease(device);
68
+ }
69
+
70
+ const char* name () const override
71
+ {
72
+ if (name_cache.empty())
73
+ {
74
+ String& name = name_cache;
75
+ name = get_string_property(device, CFSTR(kIOHIDManufacturerKey));
76
+ String product = get_string_property(device, CFSTR(kIOHIDProductKey));
77
+ String serial = get_string_property(device, CFSTR(kIOHIDSerialNumberKey));
78
+
79
+ if (!product.empty())
80
+ {
81
+ if (!name.empty() && name[name.size() - 1] != ' ') name_cache += " ";
82
+ name += product;
83
+ }
84
+
85
+ if (!serial.empty())
86
+ {
87
+ if (!name.empty() && name[name.size() - 1] != ' ')
88
+ {
89
+ name += " ";
90
+ serial = "[" + serial + "]";
91
+ }
92
+ name += serial;
93
+ }
94
+
95
+ if (name.empty()) name = "Unknown";
96
+ }
97
+ return name_cache;
98
+ }
99
+
100
+ bool is_valid () const override
101
+ {
102
+ return Super::is_valid() && device;
103
+ }
104
+
105
+ bool has_handle (void* handle) const override
106
+ {
107
+ return handle == device;
108
+ }
109
+
110
+ };// HIDGamepadData
111
+
112
+
113
+ static Gamepad*
114
+ Gamepad_create (IOHIDDeviceRef device)
115
+ {
116
+ Gamepad* g = Gamepad_create();
117
+ g->self.reset(new HIDGamepadData(device));
118
+ return g;
119
+ }
120
+
121
+ static HIDGamepadData*
122
+ get_data (Gamepad* gamepad)
123
+ {
124
+ return (HIDGamepadData*) gamepad->self.get();
125
+ }
126
+
127
+ static std::map<IOHIDElementRef, IOHIDDeviceRef> element2device;
128
+
129
+ static void
130
+ each_element (IOHIDDeviceRef device, std::function<bool(IOHIDElementRef)> fun)
131
+ {
132
+ std::shared_ptr<const __CFArray> elements(
133
+ IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone),
134
+ Xot::safe_cfrelease);
135
+ if (!elements) return;
136
+
137
+ CFIndex count = CFArrayGetCount(elements.get());
138
+ for (CFIndex i = 0; i < count; ++i)
139
+ {
140
+ IOHIDElementRef element =
141
+ (IOHIDElementRef) CFArrayGetValueAtIndex(elements.get(), i);
142
+ if (!element) continue;
143
+
144
+ if (!fun(element)) break;
145
+ }
146
+ }
147
+
148
+ static void
149
+ register_to_device_map (IOHIDDeviceRef device)
150
+ {
151
+ if (@available(macOS 11.0, *))
152
+ return;
153
+
154
+ each_element(device, [&](IOHIDElementRef element)
155
+ {
156
+ element2device[element] = device;
157
+ return true;
158
+ });
159
+ }
160
+
161
+ static void
162
+ unregister_to_device_map (IOHIDDeviceRef device)
163
+ {
164
+ if (@available(macOS 11.0, *))
165
+ return;
166
+
167
+ std::erase_if(
168
+ element2device,
169
+ [&](const auto& e) {return e.second == device;});
170
+ }
171
+
172
+ static IOHIDDeviceRef
173
+ get_device (IOHIDElementRef element)
174
+ {
175
+ if (@available(macOS 11.0, *))
176
+ return IOHIDElementGetDevice(element);
177
+ else
178
+ {
179
+ auto it = element2device.find(element);
180
+ if (it == element2device.end())
181
+ return NULL;
182
+
183
+ return it->second;
184
+ }
185
+ }
186
+
187
+ static float
188
+ get_current_value (IOHIDDeviceRef device, uint32_t usage)
189
+ {
190
+ float value = 0;
191
+ each_element(device, [&](IOHIDElementRef element)
192
+ {
193
+ if (IOHIDElementGetUsage(element) != usage)
194
+ return true;
195
+
196
+ IOHIDValueRef valref = NULL;
197
+ IOReturn result = IOHIDDeviceGetValue(device, element, &valref);
198
+ if (result != kIOReturnSuccess || !valref)
199
+ return true;
200
+
201
+ CFIndex val = IOHIDValueGetIntegerValue(valref);
202
+ CFIndex min = IOHIDElementGetLogicalMin(element);
203
+ CFIndex max = IOHIDElementGetLogicalMax(element);
204
+
205
+ value = (val - min) / (float) (max - min);
206
+ return false;
207
+ });
208
+
209
+ return value;
210
+ }
211
+
212
+ static HIDGamepadData::Mapping
213
+ get_mapping (IOHIDDeviceRef device)
214
+ {
215
+ float Z = get_current_value(device, kHIDUsage_GD_Z);
216
+ float Rx = get_current_value(device, kHIDUsage_GD_Rx);
217
+ if (0.4 < Z && Z < 0.6) return HIDGamepadData::RSTICK_ZRz;
218
+ if (0.4 < Rx && Rx < 0.6) return HIDGamepadData::RSTICK_RxRy;
219
+ return HIDGamepadData::RSTICK_UNKNOWN;
220
+ }
221
+
222
+ static void
223
+ add_gamepad (Application* app, IOHIDDeviceRef device)
224
+ {
225
+ Gamepad* gamepad = Gamepad_create(device);
226
+ get_data(gamepad)->mapping = get_mapping(device);
227
+ Gamepad_add(app, gamepad);
228
+ }
229
+
230
+ static void
231
+ remove_gamepad (Application* app, IOHIDDeviceRef device)
232
+ {
233
+ Gamepad* gamepad = Gamepad_find(device);
234
+ if (!gamepad) return;
235
+
236
+ Gamepad_remove(app, gamepad);
237
+ }
238
+
239
+ static bool
240
+ can_handle (IOHIDDeviceRef device)
241
+ {
242
+ int page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
243
+ int usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
244
+ return
245
+ page == kHIDPage_GenericDesktop &&
246
+ (usage == kHIDUsage_GD_GamePad || usage == kHIDUsage_GD_Joystick);
247
+ }
248
+
249
+ static void
250
+ handle_disconnect_event (void* context, IOReturn result, void* sender)
251
+ {
252
+ IOHIDDeviceRef device = (IOHIDDeviceRef) context;
253
+
254
+ remove_gamepad(app(), device);
255
+ unregister_to_device_map(device);
256
+ }
257
+
258
+ static void
259
+ handle_connect_event (
260
+ void* context, IOReturn result, void* sender, IOHIDDeviceRef device)
261
+ {
262
+ Application* app = (Application*) context;
263
+
264
+ if (!can_handle(device))
265
+ return;
266
+
267
+ if (@available(macOS 11.0, *))
268
+ {
269
+ if ([GCController supportsHIDDevice: device])
270
+ return;
271
+ }
272
+
273
+ IOHIDDeviceRegisterRemovalCallback(device, handle_disconnect_event, device);
274
+
275
+ register_to_device_map(device);
276
+ add_gamepad(app, device);
277
+ }
278
+
279
+ enum
280
+ {
281
+ DPAD_UP = Xot::bit(0),
282
+ DPAD_RIGHT = Xot::bit(1),
283
+ DPAD_DOWN = Xot::bit(2),
284
+ DPAD_LEFT = Xot::bit(3)
285
+ };
286
+
287
+ static uint
288
+ to_dpad (CFIndex hatswitch)
289
+ {
290
+ switch (hatswitch)
291
+ {
292
+ case 0: return DPAD_UP;
293
+ case 1: return DPAD_UP | DPAD_RIGHT;
294
+ case 2: return DPAD_RIGHT;
295
+ case 3: return DPAD_RIGHT | DPAD_DOWN;
296
+ case 4: return DPAD_DOWN;
297
+ case 5: return DPAD_DOWN | DPAD_LEFT;
298
+ case 6: return DPAD_LEFT;
299
+ case 7: return DPAD_LEFT | DPAD_UP;
300
+ }
301
+ return 0;
302
+ }
303
+
304
+ static void
305
+ call_gamepad_event (int key_code, bool pressed)
306
+ {
307
+ Window* win = Window_get_active();
308
+ if (!win) return;
309
+
310
+ auto action = pressed ? KeyEvent::DOWN : KeyEvent::UP;
311
+ KeyEvent e(action, NULL, key_code, get_key_modifiers(), 0);
312
+ Window_call_key_event(win, &e);
313
+ }
314
+
315
+ static void
316
+ call_button_event (
317
+ Gamepad* gamepad, ulonglong button, int key_code, float value)
318
+ {
319
+ Gamepad::Data* self = gamepad->self.get();
320
+
321
+ bool pressed = value > Gamepad_get_button_press_threshold();
322
+ bool current = self->state.buttons & button;
323
+ if (pressed == current) return;
324
+
325
+ self->update_prev();
326
+ if (pressed)
327
+ self->state.buttons |= button;
328
+ else
329
+ self->state.buttons &= ~button;
330
+
331
+ call_gamepad_event(key_code, pressed);
332
+ }
333
+
334
+ static void
335
+ handle_hatswitch_event (
336
+ Gamepad* gamepad, IOHIDElementRef element, CFIndex hatswitch)
337
+ {
338
+ HIDGamepadData* data = get_data(gamepad);
339
+
340
+ CFIndex prev = data->prev_hatswitch;
341
+ data->prev_hatswitch = hatswitch;
342
+
343
+ uint prev_dpad = to_dpad(prev);
344
+ uint dpad = to_dpad(hatswitch);
345
+ uint diff = prev_dpad ^ dpad;
346
+
347
+ #define HANDLE_BUTTON(button, value) \
348
+ call_button_event( \
349
+ gamepad, \
350
+ Gamepad::button, KEY_GAMEPAD_##button, \
351
+ (value) ? 1 : 0)
352
+
353
+ if (diff & DPAD_UP) HANDLE_BUTTON(UP, dpad & DPAD_UP);
354
+ if (diff & DPAD_RIGHT) HANDLE_BUTTON(RIGHT, dpad & DPAD_RIGHT);
355
+ if (diff & DPAD_DOWN) HANDLE_BUTTON(DOWN, dpad & DPAD_DOWN);
356
+ if (diff & DPAD_LEFT) HANDLE_BUTTON(LEFT, dpad & DPAD_LEFT);
357
+
358
+ #undef HANDLE_BUTTON
359
+ }
360
+
361
+ static void
362
+ handle_stick_dpad_event (
363
+ Gamepad* gamepad, auto* state, float value,
364
+ ulonglong button_negative, int key_code_negative,
365
+ ulonglong button_positive, int key_code_positive)
366
+ {
367
+ *state = value;
368
+
369
+ if (value < 0)
370
+ call_button_event(gamepad, button_negative, key_code_negative, -value);
371
+ else
372
+ call_button_event(gamepad, button_positive, key_code_positive, value);
373
+ }
374
+
375
+ static void
376
+ handle_trigger_event (Gamepad* gamepad, auto* state, float value)
377
+ {
378
+ *state = value;
379
+ }
380
+
381
+ static void
382
+ handle_analog_event (
383
+ Gamepad* gamepad, uint32_t usage, IOHIDElementRef element, CFIndex intval)
384
+ {
385
+ int value = (int) intval;
386
+ int min = (int) IOHIDElementGetLogicalMin(element);
387
+ int max = (int) IOHIDElementGetLogicalMax(element);
388
+ float range = min == max ? max : max - min;
389
+ if (range == 0) range = 1;
390
+
391
+ float linear = (value - min) / range;
392
+ float centered = linear * 2 - 1;
393
+
394
+ #define HANDLE_DPAD(stick, neg, pos, var, value) \
395
+ handle_stick_dpad_event( \
396
+ gamepad, \
397
+ &gamepad->self->state.var, \
398
+ value, \
399
+ Gamepad::stick##_##neg, KEY_GAMEPAD_##stick##_##neg, \
400
+ Gamepad::stick##_##pos, KEY_GAMEPAD_##stick##_##pos)
401
+
402
+ #define HANDLE_TRIGGER(trigger, var, value) \
403
+ handle_trigger_event( \
404
+ gamepad, \
405
+ &gamepad->self->state.var, \
406
+ value)
407
+
408
+ switch (get_data(gamepad)->mapping)
409
+ {
410
+ case HIDGamepadData::RSTICK_RxRy:
411
+ switch (usage)
412
+ {
413
+ 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;
415
+ 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;
417
+ case kHIDUsage_GD_Z: HANDLE_TRIGGER(LTRIGGER, triggers[0], linear); break;
418
+ case kHIDUsage_GD_Rz: HANDLE_TRIGGER(RTRIGGER, triggers[1], linear); break;
419
+ }
420
+ break;
421
+
422
+ case HIDGamepadData::RSTICK_ZRz:
423
+ switch (usage)
424
+ {
425
+ 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;
427
+ 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;
429
+ case kHIDUsage_GD_Rx: HANDLE_TRIGGER(LTRIGGER, triggers[0], linear); break;
430
+ case kHIDUsage_GD_Ry: HANDLE_TRIGGER(RTRIGGER, triggers[1], linear); break;
431
+ }
432
+ break;
433
+
434
+ default:
435
+ switch (usage)
436
+ {
437
+ 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;
439
+ }
440
+ break;
441
+ }
442
+
443
+ #undef HANDLE_DPAD
444
+ #undef HANDLE_TRIGGER
445
+ }
446
+
447
+ static void
448
+ handle_gamepad_events (
449
+ void* context, IOReturn result, void* sender, IOHIDValueRef valref)
450
+ {
451
+ IOHIDElementRef element = IOHIDValueGetElement(valref);
452
+ if (!element) return;
453
+
454
+ Gamepad* gamepad = Gamepad_find(get_device(element));
455
+ if (!gamepad) return;
456
+
457
+ uint32_t page = IOHIDElementGetUsagePage(element);
458
+ uint32_t usage = IOHIDElementGetUsage(element);
459
+ CFIndex value = IOHIDValueGetIntegerValue(valref);
460
+
461
+ switch (page)
462
+ {
463
+ case kHIDPage_GenericDesktop:
464
+ switch (usage)
465
+ {
466
+ case kHIDUsage_GD_Hatswitch:
467
+ handle_hatswitch_event(gamepad, element, value);
468
+ break;
469
+
470
+ case kHIDUsage_GD_X:
471
+ case kHIDUsage_GD_Y:
472
+ case kHIDUsage_GD_Z:
473
+ case kHIDUsage_GD_Rx:
474
+ case kHIDUsage_GD_Ry:
475
+ case kHIDUsage_GD_Rz:
476
+ handle_analog_event(gamepad, usage, element, value);
477
+ break;
478
+ }
479
+ break;
480
+
481
+ case kHIDPage_Button:
482
+ {
483
+ int nth = (int) usage - 1;
484
+ if (0 <= nth && nth < (KEY_GAMEPAD_BUTTON_MAX - KEY_GAMEPAD_BUTTON_0))
485
+ {
486
+ ulonglong button = Xot::bit<ulonglong>(nth, Gamepad::BUTTON_0);
487
+ int key_code = KEY_GAMEPAD_BUTTON_0 + nth;
488
+ call_button_event(gamepad, button, key_code, value > 0 ? 1 : 0);
489
+ }
490
+ break;
491
+ }
492
+ }
493
+ }
494
+
495
+ static IOHIDManagerRef manager = NULL;
496
+
497
+ static void
498
+ add_connected_gamepads (Application* app)
499
+ {
500
+ std::shared_ptr<const __CFSet> set(
501
+ IOHIDManagerCopyDevices(manager),
502
+ Xot::safe_cfrelease);
503
+ if (!set) return;
504
+
505
+ CFIndex count = CFSetGetCount(set.get());
506
+ std::vector<IOHIDDeviceRef> devices(count, NULL);
507
+ CFSetGetValues(set.get(), (const void**) &devices[0]);
508
+
509
+ for (CFIndex i = 0; i < count; ++i)
510
+ handle_connect_event(app, kIOReturnSuccess, manager, devices[i]);
511
+ }
512
+
513
+ void
514
+ init_hid_gamepads (Application* app)
515
+ {
516
+ if (manager)
517
+ invalid_state_error(__FILE__, __LINE__);
518
+
519
+ manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
520
+ if (!manager)
521
+ system_error(__FILE__, __LINE__);
522
+
523
+ add_connected_gamepads(app);
524
+
525
+ NSDictionary* gamepad =
526
+ @{
527
+ @kIOHIDDeviceUsagePageKey: @(kHIDPage_GenericDesktop),
528
+ @kIOHIDDeviceUsageKey: @(kHIDUsage_GD_GamePad)
529
+ };
530
+ NSDictionary* joystick =
531
+ @{
532
+ @kIOHIDDeviceUsagePageKey: @(kHIDPage_GenericDesktop),
533
+ @kIOHIDDeviceUsageKey: @(kHIDUsage_GD_Joystick)
534
+ };
535
+ NSArray* matchings = @[gamepad, joystick];
536
+ IOHIDManagerSetDeviceMatchingMultiple(
537
+ manager, (__bridge CFArrayRef) matchings);
538
+
539
+ IOHIDManagerRegisterDeviceMatchingCallback(
540
+ manager, handle_connect_event, app);
541
+ IOHIDManagerRegisterInputValueCallback(
542
+ manager, handle_gamepad_events, app);
543
+
544
+ IOHIDManagerScheduleWithRunLoop(
545
+ manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
546
+
547
+ IOReturn ret = IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
548
+ if (ret != kIOReturnSuccess)
549
+ system_error(__FILE__, __LINE__);
550
+ }
551
+
552
+ void
553
+ fin_hid_gamepads (Application* app)
554
+ {
555
+ if (!manager)
556
+ invalid_state_error(__FILE__, __LINE__);
557
+
558
+ IOHIDManagerUnscheduleFromRunLoop(
559
+ manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
560
+ IOHIDManagerClose(manager, kIOHIDOptionsTypeNone);
561
+
562
+ CFRelease(manager);
563
+ manager = NULL;
564
+ }
565
+
566
+
567
+ }// Reflex
data/src/view.cpp CHANGED
@@ -39,23 +39,29 @@ namespace Reflex
39
39
  enum Flag
40
40
  {
41
41
 
42
- REDRAW = Xot::bit(1, FLAG_LAST),
42
+ UPDATING = Xot::bit(1, FLAG_LAST),
43
43
 
44
- APPLY_STYLE = Xot::bit(2, FLAG_LAST),
44
+ NO_SHAPE = Xot::bit(2, FLAG_LAST),
45
45
 
46
- UPDATE_STYLE = Xot::bit(3, FLAG_LAST),
46
+ HAS_VARIABLE_LENGTHS = Xot::bit(3, FLAG_LAST),
47
47
 
48
- UPDATE_SHAPES = Xot::bit(4, FLAG_LAST),
48
+ HAS_CHILDREN_TO_REMOVE = Xot::bit(4, FLAG_LAST),
49
49
 
50
- UPDATE_LAYOUT = Xot::bit(5, FLAG_LAST),
50
+ REMOVE_FROM_PARENT = Xot::bit(5, FLAG_LAST),
51
51
 
52
- SORT_CHILDREN = Xot::bit(6, FLAG_LAST),
52
+ REDRAW = Xot::bit(6, FLAG_LAST),
53
53
 
54
- FIT_TO_CONTENT = Xot::bit(7, FLAG_LAST),
54
+ APPLY_STYLE = Xot::bit(7, FLAG_LAST),
55
55
 
56
- HAS_VARIABLE_LENGTHS = Xot::bit(8, FLAG_LAST),
56
+ UPDATE_STYLE = Xot::bit(8, FLAG_LAST),
57
57
 
58
- NO_SHAPE = Xot::bit(9, FLAG_LAST),
58
+ UPDATE_SHAPES = Xot::bit(9, FLAG_LAST),
59
+
60
+ UPDATE_LAYOUT = Xot::bit(10, FLAG_LAST),
61
+
62
+ SORT_CHILDREN = Xot::bit(11, FLAG_LAST),
63
+
64
+ FIT_TO_CONTENT = Xot::bit(12, FLAG_LAST),
59
65
 
60
66
  };// Flag
61
67
 
@@ -1045,6 +1051,20 @@ namespace Reflex
1045
1051
  }
1046
1052
  }
1047
1053
 
1054
+ static void
1055
+ remove_children (View* view, View::ChildList* children)
1056
+ {
1057
+ assert(children);
1058
+
1059
+ int size = (int) children->size();
1060
+ for (int i = size - 1; i >= 0; --i)
1061
+ {
1062
+ View* child = (*children)[i].get();
1063
+ if (child->self->check_and_remove_flag(View::Data::REMOVE_FROM_PARENT))
1064
+ view->remove_child(child);
1065
+ }
1066
+ }
1067
+
1048
1068
  void
1049
1069
  View_update_tree (View* view, const UpdateEvent& event)
1050
1070
  {
@@ -1053,13 +1073,16 @@ namespace Reflex
1053
1073
 
1054
1074
  View::Data* self = view->self.get();
1055
1075
 
1076
+ self->add_flag(View::Data::UPDATING);
1077
+
1056
1078
  fire_timers(view, event.now());
1057
1079
 
1058
1080
  View::ChildList* children = self->children();
1059
1081
  if (children)
1060
1082
  {
1061
- for (auto& child : *children)
1062
- View_update_tree(child.get(), event);
1083
+ size_t size = children->size();
1084
+ for (size_t i = 0; i < size; ++i)
1085
+ View_update_tree((*children)[i].get(), event);
1063
1086
  }
1064
1087
 
1065
1088
  update_view_shapes(view);
@@ -1081,6 +1104,11 @@ namespace Reflex
1081
1104
 
1082
1105
  if (self->check_and_remove_flag(View::Data::FIT_TO_CONTENT))
1083
1106
  fit_view_to_content(view);
1107
+
1108
+ self->remove_flag(View::Data::UPDATING);
1109
+
1110
+ if (self->check_and_remove_flag(View::Data::HAS_CHILDREN_TO_REMOVE))
1111
+ remove_children(view, children);
1084
1112
  }
1085
1113
 
1086
1114
  static bool
@@ -1792,13 +1820,19 @@ namespace Reflex
1792
1820
  else if (found != belong)
1793
1821
  invalid_state_error(__FILE__, __LINE__);
1794
1822
 
1795
- set_parent(child, NULL);
1796
-
1797
- erase_child_from_children(this, child);
1798
-
1799
- self->sort_children();
1800
-
1801
- update_view_layout(this);
1823
+ if (self->has_flag(Data::UPDATING))
1824
+ {
1825
+ // delay removing child to avoid breaking child list looped on View_update_tree()
1826
+ self->add_flag(Data::HAS_CHILDREN_TO_REMOVE);
1827
+ child->self->add_flag(Data::REMOVE_FROM_PARENT);
1828
+ }
1829
+ else
1830
+ {
1831
+ set_parent(child, NULL);
1832
+ erase_child_from_children(this, child);
1833
+ self->sort_children();
1834
+ update_view_layout(this);
1835
+ }
1802
1836
  }
1803
1837
 
1804
1838
  void