blob: 6483291c6d3794c5ecc840478a55c82e60e807e0 [file] [log] [blame]
Pierre Ossman407a5c32011-05-26 14:48:29 +00001/* Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
Pierre Ossmanb808ffd2012-09-11 11:12:56 +000019#ifdef HAVE_CONFIG_H
20#include <config.h>
21#endif
22
Pierre Ossman407a5c32011-05-26 14:48:29 +000023#include <FL/Fl.H>
24#include <FL/Fl_Window.H>
25#include <FL/x.H>
26
27#import <Cocoa/Cocoa.h>
Pierre Ossman6b743d02014-07-21 16:48:43 +020028#import <Carbon/Carbon.h>
29
Pierre Ossman2fa63f82016-12-05 15:26:21 +010030#include <IOKit/hidsystem/IOHIDLib.h>
31#include <IOKit/hidsystem/IOHIDParameter.h>
32
Pierre Ossman6b743d02014-07-21 16:48:43 +020033#define XK_LATIN1
34#define XK_MISCELLANY
35#define XK_XKB_KEYS
36#include <rfb/keysymdef.h>
37#include <rfb/XF86keysym.h>
38
39#include "keysym2ucs.h"
40
41#define NoSymbol 0
Pierre Ossman407a5c32011-05-26 14:48:29 +000042
Pierre Ossman2b0a0ef2017-05-24 15:16:32 +020043// This wasn't added until 10.12
44#if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
Pierre Ossman82fda4b2017-02-09 14:46:50 +010045const int kVK_RightCommand = 0x36;
Pierre Ossman2b0a0ef2017-05-24 15:16:32 +020046#endif
47// And this is still missing
Pierre Ossman82fda4b2017-02-09 14:46:50 +010048const int kVK_Menu = 0x6E;
49
Pierre Ossmana4f0f182011-06-14 13:36:57 +000050static bool captured = false;
51
Pierre Ossman3d759112012-08-27 14:40:51 +000052int cocoa_capture_display(Fl_Window *win, bool all_displays)
Pierre Ossman407a5c32011-05-26 14:48:29 +000053{
54 NSWindow *nsw;
55
56 nsw = (NSWindow*)fl_xid(win);
57
Pierre Ossmana4f0f182011-06-14 13:36:57 +000058 if (!captured) {
Pierre Ossman3d759112012-08-27 14:40:51 +000059 if (all_displays) {
60 if (CGCaptureAllDisplays() != kCGErrorSuccess)
61 return 1;
62 } else {
63 CGDirectDisplayID displays[16];
64 CGDisplayCount count;
65 int index;
66
67 if (CGGetActiveDisplayList(16, displays, &count) != kCGErrorSuccess)
68 return 1;
69
Pierre Ossman5c23b9e2015-03-03 16:26:03 +010070 if (count != (unsigned)Fl::screen_count())
Pierre Ossman3d759112012-08-27 14:40:51 +000071 return 1;
72
Pierre Ossman3d759112012-08-27 14:40:51 +000073 index = Fl::screen_num(win->x(), win->y(), win->w(), win->h());
Pierre Ossman3d759112012-08-27 14:40:51 +000074
75 if (CGDisplayCapture(displays[index]) != kCGErrorSuccess)
76 return 1;
77 }
Pierre Ossmana4f0f182011-06-14 13:36:57 +000078
79 captured = true;
80 }
81
Pierre Ossman407a5c32011-05-26 14:48:29 +000082 if ([nsw level] == CGShieldingWindowLevel())
83 return 0;
84
Pierre Ossman407a5c32011-05-26 14:48:29 +000085 [nsw setLevel:CGShieldingWindowLevel()];
86
87 return 0;
88}
89
90void cocoa_release_display(Fl_Window *win)
91{
92 NSWindow *nsw;
93 int newlevel;
94
Pierre Ossmana4f0f182011-06-14 13:36:57 +000095 if (captured)
Pierre Ossman3d759112012-08-27 14:40:51 +000096 CGReleaseAllDisplays();
Pierre Ossmana4f0f182011-06-14 13:36:57 +000097
98 captured = false;
Pierre Ossman407a5c32011-05-26 14:48:29 +000099
100 nsw = (NSWindow*)fl_xid(win);
101
102 // Someone else has already changed the level of this window
103 if ([nsw level] != CGShieldingWindowLevel())
104 return;
105
106 // FIXME: Store the previous level somewhere so we don't have to hard
107 // code a level here.
Pierre Ossman407a5c32011-05-26 14:48:29 +0000108 if (win->fullscreen_active() && win->contains(Fl::focus()))
109 newlevel = NSStatusWindowLevel;
110 else
Pierre Ossman407a5c32011-05-26 14:48:29 +0000111 newlevel = NSNormalWindowLevel;
112
113 // Only change if different as the level change also moves the window
114 // to the top of that level.
115 if ([nsw level] != newlevel)
116 [nsw setLevel:newlevel];
117}
Pierre Ossman6b743d02014-07-21 16:48:43 +0200118
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200119CGColorSpaceRef cocoa_win_color_space(Fl_Window *win)
120{
121 NSWindow *nsw;
122 NSColorSpace *nscs;
123
124 nsw = (NSWindow*)fl_xid(win);
125
126 nscs = [nsw colorSpace];
127 if (nscs == nil) {
128 // Offscreen, so return standard SRGB color space
129 assert(false);
130 return CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
131 }
132
133 CGColorSpaceRef lut = [nscs CGColorSpace];
134
135 // We want a permanent reference, not an autorelease
136 CGColorSpaceRetain(lut);
137
138 return lut;
139}
140
Pierre Ossman6b743d02014-07-21 16:48:43 +0200141int cocoa_is_keyboard_event(const void *event)
142{
143 NSEvent *nsevent;
144
145 nsevent = (NSEvent*)event;
146
147 switch ([nsevent type]) {
148 case NSKeyDown:
149 case NSKeyUp:
150 case NSFlagsChanged:
151 return 1;
152 default:
153 return 0;
154 }
155}
156
157int cocoa_is_key_press(const void *event)
158{
159 NSEvent *nsevent;
160
161 nsevent = (NSEvent*)event;
162
163 if ([nsevent type] == NSKeyDown)
164 return 1;
165
166 if ([nsevent type] == NSFlagsChanged) {
167 UInt32 mask;
168
169 // We don't see any event on release of CapsLock
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100170 if ([nsevent keyCode] == kVK_CapsLock)
Pierre Ossman6b743d02014-07-21 16:48:43 +0200171 return 1;
172
173 // These are entirely undocumented, but I cannot find any other way
174 // of differentiating between left and right keys
175 switch ([nsevent keyCode]) {
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100176 case kVK_RightCommand:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200177 mask = 0x0010;
178 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100179 case kVK_Command:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200180 mask = 0x0008;
181 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100182 case kVK_Shift:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200183 mask = 0x0002;
184 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100185 case kVK_CapsLock:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200186 // We don't see any event on release of CapsLock
187 return 1;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100188 case kVK_Option:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200189 mask = 0x0020;
190 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100191 case kVK_Control:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200192 mask = 0x0001;
193 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100194 case kVK_RightShift:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200195 mask = 0x0004;
196 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100197 case kVK_RightOption:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200198 mask = 0x0040;
199 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100200 case kVK_RightControl:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200201 mask = 0x2000;
202 break;
203 default:
204 return 0;
205 }
206
207 if ([nsevent modifierFlags] & mask)
208 return 1;
209 else
210 return 0;
211 }
212
213 return 0;
214}
215
216int cocoa_event_keycode(const void *event)
217{
218 NSEvent *nsevent;
219
220 nsevent = (NSEvent*)event;
221
222 return [nsevent keyCode];
223}
224
225static NSString *key_translate(UInt16 keyCode, UInt32 modifierFlags)
226{
227 const UCKeyboardLayout *layout;
228 OSStatus err;
229
230 layout = NULL;
231
Pierre Ossman6b743d02014-07-21 16:48:43 +0200232 TISInputSourceRef keyboard;
233 CFDataRef uchr;
234
235 keyboard = TISCopyCurrentKeyboardInputSource();
236 uchr = (CFDataRef)TISGetInputSourceProperty(keyboard,
237 kTISPropertyUnicodeKeyLayoutData);
238 if (uchr == NULL)
239 return nil;
240
241 layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
Pierre Ossman6b743d02014-07-21 16:48:43 +0200242 if (layout == NULL)
243 return nil;
244
245 UInt32 dead_state;
246 UniCharCount max_len, actual_len;
247 UniChar string[255];
248
249 dead_state = 0;
250 max_len = sizeof(string)/sizeof(*string);
251
252 modifierFlags = (modifierFlags >> 8) & 0xff;
253
254 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
255 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
256 string);
257 if (err != noErr)
258 return nil;
259
260 // Dead key?
261 if (dead_state != 0) {
262 // We have no fool proof way of asking what dead key this is.
263 // Assume we get a spacing equivalent if we press the
264 // same key again, and try to deduce something from that.
265 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
266 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
267 string);
268 if (err != noErr)
269 return nil;
270 }
271
272 return [NSString stringWithCharacters:string length:actual_len];
273}
274
Pierre Ossman6b743d02014-07-21 16:48:43 +0200275static const int kvk_map[][2] = {
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100276 { kVK_Return, XK_Return },
277 { kVK_Tab, XK_Tab },
278 { kVK_Space, XK_space },
279 { kVK_Delete, XK_BackSpace },
280 { kVK_Escape, XK_Escape },
281 { kVK_RightCommand, XK_Super_R },
282 { kVK_Command, XK_Super_L },
283 { kVK_Shift, XK_Shift_L },
284 { kVK_CapsLock, XK_Caps_Lock },
285 { kVK_Option, XK_Alt_L },
286 { kVK_Control, XK_Control_L },
287 { kVK_RightShift, XK_Shift_R },
288 { kVK_RightOption, XK_Alt_R },
289 { kVK_RightControl, XK_Control_R },
290 { kVK_F17, XK_F17 },
291 { kVK_VolumeUp, XF86XK_AudioRaiseVolume },
292 { kVK_VolumeDown, XF86XK_AudioLowerVolume },
293 { kVK_Mute, XF86XK_AudioMute },
294 { kVK_F18, XK_F18 },
295 { kVK_F19, XK_F19 },
296 { kVK_F20, XK_F20 },
297 { kVK_F5, XK_F5 },
298 { kVK_F6, XK_F6 },
299 { kVK_F7, XK_F7 },
300 { kVK_F3, XK_F3 },
301 { kVK_F8, XK_F8 },
302 { kVK_F9, XK_F9 },
303 { kVK_F11, XK_F11 },
304 { kVK_F13, XK_F13 },
305 { kVK_F16, XK_F16 },
306 { kVK_F14, XK_F14 },
307 { kVK_F10, XK_F10 },
308 { kVK_Menu, XK_Menu },
309 { kVK_F12, XK_F12 },
310 { kVK_F15, XK_F15 },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200311 // Should we send Insert here?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100312 { kVK_Help, XK_Help },
313 { kVK_Home, XK_Home },
314 { kVK_PageUp, XK_Page_Up },
315 { kVK_ForwardDelete, XK_Delete },
316 { kVK_F4, XK_F4 },
317 { kVK_End, XK_End },
318 { kVK_F2, XK_F2 },
319 { kVK_PageDown, XK_Page_Down },
320 { kVK_F1, XK_F1 },
321 { kVK_LeftArrow, XK_Left },
322 { kVK_RightArrow, XK_Right },
323 { kVK_DownArrow, XK_Down },
324 { kVK_UpArrow, XK_Up },
325
Pierre Ossman6b743d02014-07-21 16:48:43 +0200326 // The OS X headers claim these keys are not layout independent.
327 // Could it be because of the state of the decimal key?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100328 /* { kVK_ANSI_KeypadDecimal, XK_KP_Decimal }, */ // see below
329 { kVK_ANSI_KeypadMultiply, XK_KP_Multiply },
330 { kVK_ANSI_KeypadPlus, XK_KP_Add },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200331 // OS X doesn't have NumLock, so is this really correct?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100332 { kVK_ANSI_KeypadClear, XK_Num_Lock },
333 { kVK_ANSI_KeypadDivide, XK_KP_Divide },
334 { kVK_ANSI_KeypadEnter, XK_KP_Enter },
335 { kVK_ANSI_KeypadMinus, XK_KP_Subtract },
336 { kVK_ANSI_KeypadEquals, XK_KP_Equal },
337 { kVK_ANSI_Keypad0, XK_KP_0 },
338 { kVK_ANSI_Keypad1, XK_KP_1 },
339 { kVK_ANSI_Keypad2, XK_KP_2 },
340 { kVK_ANSI_Keypad3, XK_KP_3 },
341 { kVK_ANSI_Keypad4, XK_KP_4 },
342 { kVK_ANSI_Keypad5, XK_KP_5 },
343 { kVK_ANSI_Keypad6, XK_KP_6 },
344 { kVK_ANSI_Keypad7, XK_KP_7 },
345 { kVK_ANSI_Keypad8, XK_KP_8 },
346 { kVK_ANSI_Keypad9, XK_KP_9 },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200347};
348
349int cocoa_event_keysym(const void *event)
350{
351 NSEvent *nsevent;
352
353 UInt16 key_code;
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100354 size_t i;
Pierre Ossman6b743d02014-07-21 16:48:43 +0200355
356 NSString *chars;
357 UInt32 modifiers;
358
359 nsevent = (NSEvent*)event;
360
361 key_code = [nsevent keyCode];
362
363 // Start with keys that either don't generate a symbol, or
364 // generate the same symbol as some other key.
365 for (i = 0;i < sizeof(kvk_map)/sizeof(kvk_map[0]);i++) {
366 if (key_code == kvk_map[i][0])
367 return kvk_map[i][1];
368 }
369
370 // OS X always sends the same key code for the decimal key on the
371 // num pad, but X11 wants different keysyms depending on if it should
372 // be a comma or full stop.
373 if (key_code == 0x41) {
374 switch ([[nsevent charactersIgnoringModifiers] UTF8String][0]) {
375 case ',':
376 return XK_KP_Separator;
377 case '.':
378 return XK_KP_Decimal;
379 default:
380 return NoSymbol;
381 }
382 }
383
384 // We want a "normal" symbol out of the event, which basically means
385 // we only respect the shift and alt/altgr modifiers. Cocoa can help
386 // us if we only wanted shift, but as we also want alt/altgr, we'll
387 // have to do some lookup ourselves. This matches our behaviour on
388 // other platforms.
389
390 modifiers = 0;
391 if ([nsevent modifierFlags] & NSAlphaShiftKeyMask)
392 modifiers |= alphaLock;
393 if ([nsevent modifierFlags] & NSShiftKeyMask)
394 modifiers |= shiftKey;
395 if ([nsevent modifierFlags] & NSAlternateKeyMask)
396 modifiers |= optionKey;
397
398 chars = key_translate(key_code, modifiers);
399 if (chars == nil)
400 return NoSymbol;
401
402 // FIXME: Some dead keys are given as NBSP + combining character
403 if ([chars length] != 1)
404 return NoSymbol;
405
406 // Dead key?
407 if ([[nsevent characters] length] == 0)
408 return ucs2keysym(ucs2combining([chars characterAtIndex:0]));
409
410 return ucs2keysym([chars characterAtIndex:0]);
411}
Pierre Ossman2fa63f82016-12-05 15:26:21 +0100412
413static int cocoa_open_hid(io_connect_t *ioc)
414{
415 kern_return_t ret;
416 io_service_t ios;
417 CFMutableDictionaryRef mdict;
418
419 mdict = IOServiceMatching(kIOHIDSystemClass);
420 ios = IOServiceGetMatchingService(kIOMasterPortDefault,
421 (CFDictionaryRef) mdict);
422 if (!ios)
423 return KERN_FAILURE;
424
425 ret = IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, ioc);
426 IOObjectRelease(ios);
427 if (ret != KERN_SUCCESS)
428 return ret;
429
430 return KERN_SUCCESS;
431}
432
433static int cocoa_set_modifier_lock_state(int modifier, bool on)
434{
435 kern_return_t ret;
436 io_connect_t ioc;
437
438 ret = cocoa_open_hid(&ioc);
439 if (ret != KERN_SUCCESS)
440 return ret;
441
442 ret = IOHIDSetModifierLockState(ioc, modifier, on);
443 IOServiceClose(ioc);
444 if (ret != KERN_SUCCESS)
445 return ret;
446
447 return KERN_SUCCESS;
448}
449
450int cocoa_set_caps_lock_state(bool on)
451{
452 return cocoa_set_modifier_lock_state(kIOHIDCapsLockState, on);
453}
454
455int cocoa_set_num_lock_state(bool on)
456{
457 return cocoa_set_modifier_lock_state(kIOHIDNumLockState, on);
458}