5
5
// Implemented features:
6
6
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
7
7
// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
8
- // Issues:
9
- // [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters]. .
8
+ // [X] Platform: Keyboard arrays indexed using kVK_* codes, e.g. ImGui::IsKeyPressed(kVK_Space).
9
+ // [X ] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad' .
10
10
11
11
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
12
12
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
13
13
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
14
14
// Read online: https://github.com/ocornut/imgui/tree/master/docs
15
15
16
- #include " imgui.h"
17
- #include " imgui_impl_osx.h"
16
+ #import " imgui.h"
17
+ #import " imgui_impl_osx.h"
18
18
#import < Cocoa/Cocoa.h>
19
- #include < mach/mach_time.h>
19
+ #import < mach/mach_time.h>
20
+ #import < Carbon/Carbon.h>
21
+ #import < GameController/GameController.h>
20
22
21
23
// CHANGELOG
22
24
// (minor and older changes stripped away, please see git history for details)
25
+ // 2021-11-23: Fix keyboard support, add game controller support
23
26
// 2021-09-21: Use mach_absolute_time as CFAbsoluteTimeGetCurrent can jump backwards.
24
27
// 2021-08-17: Calling io.AddFocusEvent() on NSApplicationDidBecomeActiveNotification/NSApplicationDidResignActiveNotification events.
25
28
// 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key.
37
40
// 2018-07-07: Initial version.
38
41
39
42
@class ImFocusObserver;
43
+ @class KeyEventResponder;
40
44
41
45
// Data
42
46
static double g_HostClockPeriod = 0.0 ;
46
50
static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {};
47
51
static bool g_MouseDown[ImGuiMouseButton_COUNT] = {};
48
52
static ImFocusObserver* g_FocusObserver = NULL ;
53
+ static KeyEventResponder* g_KeyEventResponder = nil ;
49
54
50
55
// Undocumented methods for creating cursors.
51
56
@interface NSCursor ()
@@ -74,6 +79,84 @@ static void resetKeys()
74
79
io.KeyCtrl = io.KeyShift = io.KeyAlt = io.KeySuper = false ;
75
80
}
76
81
82
+ @interface KeyEventResponder : NSView <NSTextInputClient >
83
+ @end
84
+
85
+ @implementation KeyEventResponder
86
+
87
+ - (void )viewDidMoveToWindow
88
+ {
89
+ [self .window makeFirstResponder: self ];
90
+ }
91
+
92
+ - (void )keyDown : (NSEvent *)event {
93
+ [self interpretKeyEvents: @[event]];
94
+ }
95
+
96
+ - (void )insertText : (id )aString replacementRange : (NSRange )replacementRange
97
+ {
98
+ ImGuiIO& io = ImGui::GetIO ();
99
+
100
+ NSString *characters;
101
+ if ([aString isKindOfClass: [NSAttributedString class ]])
102
+ characters = [aString string ];
103
+ else
104
+ characters = (NSString *)aString;
105
+
106
+ io.AddInputCharactersUTF8 (characters.UTF8String );
107
+ }
108
+
109
+ - (BOOL )acceptsFirstResponder {
110
+ return YES ;
111
+ }
112
+
113
+ - (void )doCommandBySelector : (SEL )myselector
114
+ {
115
+ }
116
+
117
+ - (nullable NSAttributedString *)attributedSubstringForProposedRange : (NSRange )range actualRange : (nullable NSRangePointer )actualRange {
118
+ return nil ;
119
+ }
120
+
121
+ - (NSUInteger )characterIndexForPoint : (NSPoint )point {
122
+ return 0 ;
123
+ }
124
+
125
+
126
+ - (NSRect )firstRectForCharacterRange : (NSRange )range actualRange : (nullable NSRangePointer )actualRange {
127
+ return NSZeroRect ;
128
+ }
129
+
130
+
131
+ - (BOOL )hasMarkedText {
132
+ return NO ;
133
+ }
134
+
135
+
136
+ - (NSRange )markedRange {
137
+ return NSMakeRange (NSNotFound , 0 );
138
+ }
139
+
140
+
141
+ - (NSRange )selectedRange {
142
+ return NSMakeRange (NSNotFound , 0 );
143
+ }
144
+
145
+
146
+ - (void )setMarkedText : (nonnull id )string selectedRange : (NSRange )selectedRange replacementRange : (NSRange )replacementRange {
147
+ }
148
+
149
+
150
+ - (void )unmarkText {
151
+ }
152
+
153
+
154
+ - (nonnull NSArray <NSAttributedStringKey> *)validAttributesForMarkedText {
155
+ return @[];
156
+ }
157
+
158
+ @end
159
+
77
160
@interface ImFocusObserver : NSObject
78
161
79
162
- (void )onApplicationBecomeActive : (NSNotification *)aNotification ;
@@ -103,7 +186,7 @@ - (void)onApplicationBecomeInactive:(NSNotification*)aNotification
103
186
@end
104
187
105
188
// Functions
106
- bool ImGui_ImplOSX_Init ()
189
+ bool ImGui_ImplOSX_Init (NSView *view )
107
190
{
108
191
ImGuiIO& io = ImGui::GetIO ();
109
192
@@ -115,29 +198,28 @@ bool ImGui_ImplOSX_Init()
115
198
io.BackendPlatformName = " imgui_impl_osx" ;
116
199
117
200
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeyDown[] array.
118
- const int offset_for_function_keys = 256 - 0xF700 ;
119
- io.KeyMap [ImGuiKey_Tab] = ' \t ' ;
120
- io.KeyMap [ImGuiKey_LeftArrow] = NSLeftArrowFunctionKey + offset_for_function_keys;
121
- io.KeyMap [ImGuiKey_RightArrow] = NSRightArrowFunctionKey + offset_for_function_keys;
122
- io.KeyMap [ImGuiKey_UpArrow] = NSUpArrowFunctionKey + offset_for_function_keys;
123
- io.KeyMap [ImGuiKey_DownArrow] = NSDownArrowFunctionKey + offset_for_function_keys;
124
- io.KeyMap [ImGuiKey_PageUp] = NSPageUpFunctionKey + offset_for_function_keys;
125
- io.KeyMap [ImGuiKey_PageDown] = NSPageDownFunctionKey + offset_for_function_keys;
126
- io.KeyMap [ImGuiKey_Home] = NSHomeFunctionKey + offset_for_function_keys;
127
- io.KeyMap [ImGuiKey_End] = NSEndFunctionKey + offset_for_function_keys;
128
- io.KeyMap [ImGuiKey_Insert] = NSInsertFunctionKey + offset_for_function_keys;
129
- io.KeyMap [ImGuiKey_Delete] = NSDeleteFunctionKey + offset_for_function_keys;
130
- io.KeyMap [ImGuiKey_Backspace] = 127 ;
131
- io.KeyMap [ImGuiKey_Space] = 32 ;
132
- io.KeyMap [ImGuiKey_Enter] = 13 ;
133
- io.KeyMap [ImGuiKey_Escape] = 27 ;
134
- io.KeyMap [ImGuiKey_KeyPadEnter] = 3 ;
135
- io.KeyMap [ImGuiKey_A] = ' A' ;
136
- io.KeyMap [ImGuiKey_C] = ' C' ;
137
- io.KeyMap [ImGuiKey_V] = ' V' ;
138
- io.KeyMap [ImGuiKey_X] = ' X' ;
139
- io.KeyMap [ImGuiKey_Y] = ' Y' ;
140
- io.KeyMap [ImGuiKey_Z] = ' Z' ;
201
+ io.KeyMap [ImGuiKey_Tab] = kVK_Tab ;
202
+ io.KeyMap [ImGuiKey_LeftArrow] = kVK_LeftArrow ;
203
+ io.KeyMap [ImGuiKey_RightArrow] = kVK_RightArrow ;
204
+ io.KeyMap [ImGuiKey_UpArrow] = kVK_UpArrow ;
205
+ io.KeyMap [ImGuiKey_DownArrow] = kVK_DownArrow ;
206
+ io.KeyMap [ImGuiKey_PageUp] = kVK_PageUp ;
207
+ io.KeyMap [ImGuiKey_PageDown] = kVK_PageDown ;
208
+ io.KeyMap [ImGuiKey_Home] = kVK_Home ;
209
+ io.KeyMap [ImGuiKey_End] = kVK_End ;
210
+ io.KeyMap [ImGuiKey_Insert] = kVK_F13 ;
211
+ io.KeyMap [ImGuiKey_Delete] = kVK_ForwardDelete ;
212
+ io.KeyMap [ImGuiKey_Backspace] = kVK_Delete ;
213
+ io.KeyMap [ImGuiKey_Space] = kVK_Space ;
214
+ io.KeyMap [ImGuiKey_Enter] = kVK_Return ;
215
+ io.KeyMap [ImGuiKey_Escape] = kVK_Escape ;
216
+ io.KeyMap [ImGuiKey_KeyPadEnter] = kVK_ANSI_KeypadEnter ;
217
+ io.KeyMap [ImGuiKey_A] = kVK_ANSI_A ;
218
+ io.KeyMap [ImGuiKey_C] = kVK_ANSI_C ;
219
+ io.KeyMap [ImGuiKey_V] = kVK_ANSI_V ;
220
+ io.KeyMap [ImGuiKey_X] = kVK_ANSI_X ;
221
+ io.KeyMap [ImGuiKey_Y] = kVK_ANSI_Y ;
222
+ io.KeyMap [ImGuiKey_Z] = kVK_ANSI_Z ;
141
223
142
224
// Load cursors. Some of them are undocumented.
143
225
g_MouseCursorHidden = false ;
@@ -189,6 +271,9 @@ bool ImGui_ImplOSX_Init()
189
271
selector: @selector (onApplicationBecomeInactive: )
190
272
name: NSApplicationDidResignActiveNotification
191
273
object: nil ];
274
+
275
+ g_KeyEventResponder = [[KeyEventResponder alloc ] initWithFrame: NSZeroRect ];
276
+ [view addSubview: g_KeyEventResponder];
192
277
193
278
return true ;
194
279
}
@@ -234,6 +319,51 @@ static void ImGui_ImplOSX_UpdateMouseCursorAndButtons()
234
319
}
235
320
}
236
321
322
+ void ImGui_ImplOSX_UpdateGamepads ()
323
+ {
324
+ ImGuiIO& io = ImGui::GetIO ();
325
+ memset (io.NavInputs , 0 , sizeof (io.NavInputs ));
326
+ if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0 )
327
+ return ;
328
+
329
+ GCController *controller;
330
+ if (@available (macOS 11.0 , *)) {
331
+ controller = GCController.current ;
332
+ } else {
333
+ controller = GCController.controllers .firstObject ;
334
+ }
335
+
336
+ if (controller == nil || controller.extendedGamepad == nil )
337
+ {
338
+ io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
339
+ return ;
340
+ }
341
+
342
+ GCExtendedGamepad *gp = controller.extendedGamepad ;
343
+
344
+ #define MAP_BUTTON (NAV_NO, NAME ) { io.NavInputs [NAV_NO] = gp.NAME .isPressed ? 1.0 : 0.0 ; }
345
+ MAP_BUTTON (ImGuiNavInput_Activate, buttonA);
346
+ MAP_BUTTON (ImGuiNavInput_Cancel, buttonB);
347
+ MAP_BUTTON (ImGuiNavInput_Menu, buttonX);
348
+ MAP_BUTTON (ImGuiNavInput_Input, buttonY);
349
+ MAP_BUTTON (ImGuiNavInput_DpadLeft, dpad.left );
350
+ MAP_BUTTON (ImGuiNavInput_DpadRight, dpad.right );
351
+ MAP_BUTTON (ImGuiNavInput_DpadUp, dpad.up );
352
+ MAP_BUTTON (ImGuiNavInput_DpadDown, dpad.down );
353
+ MAP_BUTTON (ImGuiNavInput_FocusPrev, leftShoulder);
354
+ MAP_BUTTON (ImGuiNavInput_FocusNext, rightShoulder);
355
+ MAP_BUTTON (ImGuiNavInput_TweakSlow, leftTrigger);
356
+ MAP_BUTTON (ImGuiNavInput_TweakFast, rightTrigger);
357
+ #undef MAP_BUTTON
358
+
359
+ io.NavInputs [ImGuiNavInput_LStickLeft] = gp.leftThumbstick .left .value ;
360
+ io.NavInputs [ImGuiNavInput_LStickRight] = gp.leftThumbstick .right .value ;
361
+ io.NavInputs [ImGuiNavInput_LStickUp] = gp.leftThumbstick .up .value ;
362
+ io.NavInputs [ImGuiNavInput_LStickDown] = gp.leftThumbstick .down .value ;
363
+
364
+ io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
365
+ }
366
+
237
367
void ImGui_ImplOSX_NewFrame (NSView * view)
238
368
{
239
369
// Setup display size
@@ -256,19 +386,7 @@ void ImGui_ImplOSX_NewFrame(NSView* view)
256
386
g_Time = current_time;
257
387
258
388
ImGui_ImplOSX_UpdateMouseCursorAndButtons ();
259
- }
260
-
261
- static int mapCharacterToKey (int c)
262
- {
263
- if (c >= ' a' && c <= ' z' )
264
- return c - ' a' + ' A' ;
265
- if (c == 25 ) // SHIFT+TAB -> TAB
266
- return 9 ;
267
- if (c >= 0 && c < 256 )
268
- return c;
269
- if (c >= 0xF700 && c < 0xF700 + 256 )
270
- return c - 0xF700 + 256 ;
271
- return -1 ;
389
+ ImGui_ImplOSX_UpdateGamepads ();
272
390
}
273
391
274
392
bool ImGui_ImplOSX_HandleEvent (NSEvent * event, NSView * view)
@@ -329,59 +447,39 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
329
447
return io.WantCaptureMouse ;
330
448
}
331
449
332
- // FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm.
333
- if (event.type == NSEventTypeKeyDown)
450
+ if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp)
334
451
{
335
- NSString * str = [event characters ];
336
- NSUInteger len = [str length ];
337
- for (NSUInteger i = 0 ; i < len; i++)
338
- {
339
- int c = [str characterAtIndex: i];
340
- if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF ) && c != 127 )
341
- io.AddInputCharacter ((unsigned int )c);
342
-
343
- // We must reset in case we're pressing a sequence of special keys while keeping the command pressed
344
- int key = mapCharacterToKey (c);
345
- if (key != -1 && key < 256 && !io.KeySuper )
346
- resetKeys ();
347
- if (key != -1 )
348
- io.KeysDown [key] = true ;
349
- }
452
+ unsigned short code = event.keyCode ;
453
+ IM_ASSERT (code >= 0 && code < IM_ARRAYSIZE (io.KeysDown ));
454
+ io.KeysDown [code] = event.type == NSEventTypeKeyDown;
455
+ NSEventModifierFlags flags = event.modifierFlags ;
456
+ io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0 ;
457
+ io.KeyShift = (flags & NSEventModifierFlagShift) != 0 ;
458
+ io.KeyAlt = (flags & NSEventModifierFlagOption) != 0 ;
459
+ io.KeySuper = (flags & NSEventModifierFlagCommand) != 0 ;
350
460
return io.WantCaptureKeyboard ;
351
461
}
352
462
353
- if (event.type == NSEventTypeKeyUp )
463
+ if (event.type == NSEventTypeFlagsChanged )
354
464
{
355
- NSString * str = [event characters ];
356
- NSUInteger len = [str length ];
357
- for (NSUInteger i = 0 ; i < len; i++)
465
+ NSEventModifierFlags flags = event.modifierFlags ;
466
+ switch (event.keyCode )
358
467
{
359
- int c = [str characterAtIndex: i];
360
- int key = mapCharacterToKey (c);
361
- if (key != -1 )
362
- io.KeysDown [key] = false ;
468
+ case kVK_Control :
469
+ io.KeyCtrl = (flags & NSEventModifierFlagControl) != 0 ;
470
+ break ;
471
+ case kVK_Shift :
472
+ io.KeyShift = (flags & NSEventModifierFlagShift) != 0 ;
473
+ break ;
474
+ case kVK_Option :
475
+ io.KeyAlt = (flags & NSEventModifierFlagOption) != 0 ;
476
+ break ;
477
+ case kVK_Command :
478
+ io.KeySuper = (flags & NSEventModifierFlagCommand) != 0 ;
479
+ break ;
363
480
}
364
481
return io.WantCaptureKeyboard ;
365
482
}
366
483
367
- if (event.type == NSEventTypeFlagsChanged)
368
- {
369
- unsigned int flags = [event modifierFlags ] & NSEventModifierFlagDeviceIndependentFlagsMask;
370
-
371
- bool oldKeyCtrl = io.KeyCtrl ;
372
- bool oldKeyShift = io.KeyShift ;
373
- bool oldKeyAlt = io.KeyAlt ;
374
- bool oldKeySuper = io.KeySuper ;
375
- io.KeyCtrl = flags & NSEventModifierFlagControl;
376
- io.KeyShift = flags & NSEventModifierFlagShift;
377
- io.KeyAlt = flags & NSEventModifierFlagOption;
378
- io.KeySuper = flags & NSEventModifierFlagCommand;
379
-
380
- // We must reset them as we will not receive any keyUp event if they where pressed with a modifier
381
- if ((oldKeyShift && !io.KeyShift ) || (oldKeyCtrl && !io.KeyCtrl ) || (oldKeyAlt && !io.KeyAlt ) || (oldKeySuper && !io.KeySuper ))
382
- resetKeys ();
383
- return io.WantCaptureKeyboard ;
384
- }
385
-
386
484
return false ;
387
485
}
0 commit comments