blob: 6e464fa4902dad58abc6750da385eda916c4d4a0 [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
30#define XK_LATIN1
31#define XK_MISCELLANY
32#define XK_XKB_KEYS
33#include <rfb/keysymdef.h>
34#include <rfb/XF86keysym.h>
35
36#include "keysym2ucs.h"
37
38#define NoSymbol 0
Pierre Ossman407a5c32011-05-26 14:48:29 +000039
Pierre Ossman2b0a0ef2017-05-24 15:16:32 +020040// This wasn't added until 10.12
41#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 +010042const int kVK_RightCommand = 0x36;
Pierre Ossman2b0a0ef2017-05-24 15:16:32 +020043#endif
44// And this is still missing
Pierre Ossman82fda4b2017-02-09 14:46:50 +010045const int kVK_Menu = 0x6E;
46
Pierre Ossmana4f0f182011-06-14 13:36:57 +000047static bool captured = false;
48
Pierre Ossman3d759112012-08-27 14:40:51 +000049int cocoa_capture_display(Fl_Window *win, bool all_displays)
Pierre Ossman407a5c32011-05-26 14:48:29 +000050{
51 NSWindow *nsw;
52
53 nsw = (NSWindow*)fl_xid(win);
54
Pierre Ossmana4f0f182011-06-14 13:36:57 +000055 if (!captured) {
Pierre Ossman3d759112012-08-27 14:40:51 +000056 if (all_displays) {
57 if (CGCaptureAllDisplays() != kCGErrorSuccess)
58 return 1;
59 } else {
60 CGDirectDisplayID displays[16];
61 CGDisplayCount count;
62 int index;
63
64 if (CGGetActiveDisplayList(16, displays, &count) != kCGErrorSuccess)
65 return 1;
66
Pierre Ossman5c23b9e2015-03-03 16:26:03 +010067 if (count != (unsigned)Fl::screen_count())
Pierre Ossman3d759112012-08-27 14:40:51 +000068 return 1;
69
Pierre Ossman3d759112012-08-27 14:40:51 +000070 index = Fl::screen_num(win->x(), win->y(), win->w(), win->h());
Pierre Ossman3d759112012-08-27 14:40:51 +000071
72 if (CGDisplayCapture(displays[index]) != kCGErrorSuccess)
73 return 1;
74 }
Pierre Ossmana4f0f182011-06-14 13:36:57 +000075
76 captured = true;
77 }
78
Pierre Ossman407a5c32011-05-26 14:48:29 +000079 if ([nsw level] == CGShieldingWindowLevel())
80 return 0;
81
Pierre Ossman407a5c32011-05-26 14:48:29 +000082 [nsw setLevel:CGShieldingWindowLevel()];
83
84 return 0;
85}
86
87void cocoa_release_display(Fl_Window *win)
88{
89 NSWindow *nsw;
90 int newlevel;
91
Pierre Ossmana4f0f182011-06-14 13:36:57 +000092 if (captured)
Pierre Ossman3d759112012-08-27 14:40:51 +000093 CGReleaseAllDisplays();
Pierre Ossmana4f0f182011-06-14 13:36:57 +000094
95 captured = false;
Pierre Ossman407a5c32011-05-26 14:48:29 +000096
97 nsw = (NSWindow*)fl_xid(win);
98
99 // Someone else has already changed the level of this window
100 if ([nsw level] != CGShieldingWindowLevel())
101 return;
102
103 // FIXME: Store the previous level somewhere so we don't have to hard
104 // code a level here.
Pierre Ossman407a5c32011-05-26 14:48:29 +0000105 if (win->fullscreen_active() && win->contains(Fl::focus()))
106 newlevel = NSStatusWindowLevel;
107 else
Pierre Ossman407a5c32011-05-26 14:48:29 +0000108 newlevel = NSNormalWindowLevel;
109
110 // Only change if different as the level change also moves the window
111 // to the top of that level.
112 if ([nsw level] != newlevel)
113 [nsw setLevel:newlevel];
114}
Pierre Ossman6b743d02014-07-21 16:48:43 +0200115
Pierre Ossman1669a2d2017-04-28 11:37:12 +0200116CGColorSpaceRef cocoa_win_color_space(Fl_Window *win)
117{
118 NSWindow *nsw;
119 NSColorSpace *nscs;
120
121 nsw = (NSWindow*)fl_xid(win);
122
123 nscs = [nsw colorSpace];
124 if (nscs == nil) {
125 // Offscreen, so return standard SRGB color space
126 assert(false);
127 return CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
128 }
129
130 CGColorSpaceRef lut = [nscs CGColorSpace];
131
132 // We want a permanent reference, not an autorelease
133 CGColorSpaceRetain(lut);
134
135 return lut;
136}
137
Pierre Ossman6b743d02014-07-21 16:48:43 +0200138int cocoa_is_keyboard_event(const void *event)
139{
140 NSEvent *nsevent;
141
142 nsevent = (NSEvent*)event;
143
144 switch ([nsevent type]) {
145 case NSKeyDown:
146 case NSKeyUp:
147 case NSFlagsChanged:
148 return 1;
149 default:
150 return 0;
151 }
152}
153
154int cocoa_is_key_press(const void *event)
155{
156 NSEvent *nsevent;
157
158 nsevent = (NSEvent*)event;
159
160 if ([nsevent type] == NSKeyDown)
161 return 1;
162
163 if ([nsevent type] == NSFlagsChanged) {
164 UInt32 mask;
165
166 // We don't see any event on release of CapsLock
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100167 if ([nsevent keyCode] == kVK_CapsLock)
Pierre Ossman6b743d02014-07-21 16:48:43 +0200168 return 1;
169
170 // These are entirely undocumented, but I cannot find any other way
171 // of differentiating between left and right keys
172 switch ([nsevent keyCode]) {
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100173 case kVK_RightCommand:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200174 mask = 0x0010;
175 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100176 case kVK_Command:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200177 mask = 0x0008;
178 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100179 case kVK_Shift:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200180 mask = 0x0002;
181 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100182 case kVK_CapsLock:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200183 // We don't see any event on release of CapsLock
184 return 1;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100185 case kVK_Option:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200186 mask = 0x0020;
187 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100188 case kVK_Control:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200189 mask = 0x0001;
190 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100191 case kVK_RightShift:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200192 mask = 0x0004;
193 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100194 case kVK_RightOption:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200195 mask = 0x0040;
196 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100197 case kVK_RightControl:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200198 mask = 0x2000;
199 break;
200 default:
201 return 0;
202 }
203
204 if ([nsevent modifierFlags] & mask)
205 return 1;
206 else
207 return 0;
208 }
209
210 return 0;
211}
212
213int cocoa_event_keycode(const void *event)
214{
215 NSEvent *nsevent;
216
217 nsevent = (NSEvent*)event;
218
219 return [nsevent keyCode];
220}
221
222static NSString *key_translate(UInt16 keyCode, UInt32 modifierFlags)
223{
224 const UCKeyboardLayout *layout;
225 OSStatus err;
226
227 layout = NULL;
228
Pierre Ossman6b743d02014-07-21 16:48:43 +0200229 TISInputSourceRef keyboard;
230 CFDataRef uchr;
231
232 keyboard = TISCopyCurrentKeyboardInputSource();
233 uchr = (CFDataRef)TISGetInputSourceProperty(keyboard,
234 kTISPropertyUnicodeKeyLayoutData);
235 if (uchr == NULL)
236 return nil;
237
238 layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
Pierre Ossman6b743d02014-07-21 16:48:43 +0200239 if (layout == NULL)
240 return nil;
241
242 UInt32 dead_state;
243 UniCharCount max_len, actual_len;
244 UniChar string[255];
245
246 dead_state = 0;
247 max_len = sizeof(string)/sizeof(*string);
248
249 modifierFlags = (modifierFlags >> 8) & 0xff;
250
251 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
252 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
253 string);
254 if (err != noErr)
255 return nil;
256
257 // Dead key?
258 if (dead_state != 0) {
259 // We have no fool proof way of asking what dead key this is.
260 // Assume we get a spacing equivalent if we press the
261 // same key again, and try to deduce something from that.
262 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
263 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
264 string);
265 if (err != noErr)
266 return nil;
267 }
268
269 return [NSString stringWithCharacters:string length:actual_len];
270}
271
Pierre Ossman6b743d02014-07-21 16:48:43 +0200272static const int kvk_map[][2] = {
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100273 { kVK_Return, XK_Return },
274 { kVK_Tab, XK_Tab },
275 { kVK_Space, XK_space },
276 { kVK_Delete, XK_BackSpace },
277 { kVK_Escape, XK_Escape },
278 { kVK_RightCommand, XK_Super_R },
279 { kVK_Command, XK_Super_L },
280 { kVK_Shift, XK_Shift_L },
281 { kVK_CapsLock, XK_Caps_Lock },
282 { kVK_Option, XK_Alt_L },
283 { kVK_Control, XK_Control_L },
284 { kVK_RightShift, XK_Shift_R },
285 { kVK_RightOption, XK_Alt_R },
286 { kVK_RightControl, XK_Control_R },
287 { kVK_F17, XK_F17 },
288 { kVK_VolumeUp, XF86XK_AudioRaiseVolume },
289 { kVK_VolumeDown, XF86XK_AudioLowerVolume },
290 { kVK_Mute, XF86XK_AudioMute },
291 { kVK_F18, XK_F18 },
292 { kVK_F19, XK_F19 },
293 { kVK_F20, XK_F20 },
294 { kVK_F5, XK_F5 },
295 { kVK_F6, XK_F6 },
296 { kVK_F7, XK_F7 },
297 { kVK_F3, XK_F3 },
298 { kVK_F8, XK_F8 },
299 { kVK_F9, XK_F9 },
300 { kVK_F11, XK_F11 },
301 { kVK_F13, XK_F13 },
302 { kVK_F16, XK_F16 },
303 { kVK_F14, XK_F14 },
304 { kVK_F10, XK_F10 },
305 { kVK_Menu, XK_Menu },
306 { kVK_F12, XK_F12 },
307 { kVK_F15, XK_F15 },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200308 // Should we send Insert here?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100309 { kVK_Help, XK_Help },
310 { kVK_Home, XK_Home },
311 { kVK_PageUp, XK_Page_Up },
312 { kVK_ForwardDelete, XK_Delete },
313 { kVK_F4, XK_F4 },
314 { kVK_End, XK_End },
315 { kVK_F2, XK_F2 },
316 { kVK_PageDown, XK_Page_Down },
317 { kVK_F1, XK_F1 },
318 { kVK_LeftArrow, XK_Left },
319 { kVK_RightArrow, XK_Right },
320 { kVK_DownArrow, XK_Down },
321 { kVK_UpArrow, XK_Up },
322
Pierre Ossman6b743d02014-07-21 16:48:43 +0200323 // The OS X headers claim these keys are not layout independent.
324 // Could it be because of the state of the decimal key?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100325 /* { kVK_ANSI_KeypadDecimal, XK_KP_Decimal }, */ // see below
326 { kVK_ANSI_KeypadMultiply, XK_KP_Multiply },
327 { kVK_ANSI_KeypadPlus, XK_KP_Add },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200328 // OS X doesn't have NumLock, so is this really correct?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100329 { kVK_ANSI_KeypadClear, XK_Num_Lock },
330 { kVK_ANSI_KeypadDivide, XK_KP_Divide },
331 { kVK_ANSI_KeypadEnter, XK_KP_Enter },
332 { kVK_ANSI_KeypadMinus, XK_KP_Subtract },
333 { kVK_ANSI_KeypadEquals, XK_KP_Equal },
334 { kVK_ANSI_Keypad0, XK_KP_0 },
335 { kVK_ANSI_Keypad1, XK_KP_1 },
336 { kVK_ANSI_Keypad2, XK_KP_2 },
337 { kVK_ANSI_Keypad3, XK_KP_3 },
338 { kVK_ANSI_Keypad4, XK_KP_4 },
339 { kVK_ANSI_Keypad5, XK_KP_5 },
340 { kVK_ANSI_Keypad6, XK_KP_6 },
341 { kVK_ANSI_Keypad7, XK_KP_7 },
342 { kVK_ANSI_Keypad8, XK_KP_8 },
343 { kVK_ANSI_Keypad9, XK_KP_9 },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200344};
345
346int cocoa_event_keysym(const void *event)
347{
348 NSEvent *nsevent;
349
350 UInt16 key_code;
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100351 size_t i;
Pierre Ossman6b743d02014-07-21 16:48:43 +0200352
353 NSString *chars;
354 UInt32 modifiers;
355
356 nsevent = (NSEvent*)event;
357
358 key_code = [nsevent keyCode];
359
360 // Start with keys that either don't generate a symbol, or
361 // generate the same symbol as some other key.
362 for (i = 0;i < sizeof(kvk_map)/sizeof(kvk_map[0]);i++) {
363 if (key_code == kvk_map[i][0])
364 return kvk_map[i][1];
365 }
366
367 // OS X always sends the same key code for the decimal key on the
368 // num pad, but X11 wants different keysyms depending on if it should
369 // be a comma or full stop.
370 if (key_code == 0x41) {
371 switch ([[nsevent charactersIgnoringModifiers] UTF8String][0]) {
372 case ',':
373 return XK_KP_Separator;
374 case '.':
375 return XK_KP_Decimal;
376 default:
377 return NoSymbol;
378 }
379 }
380
381 // We want a "normal" symbol out of the event, which basically means
382 // we only respect the shift and alt/altgr modifiers. Cocoa can help
383 // us if we only wanted shift, but as we also want alt/altgr, we'll
384 // have to do some lookup ourselves. This matches our behaviour on
385 // other platforms.
386
387 modifiers = 0;
388 if ([nsevent modifierFlags] & NSAlphaShiftKeyMask)
389 modifiers |= alphaLock;
390 if ([nsevent modifierFlags] & NSShiftKeyMask)
391 modifiers |= shiftKey;
392 if ([nsevent modifierFlags] & NSAlternateKeyMask)
393 modifiers |= optionKey;
394
395 chars = key_translate(key_code, modifiers);
396 if (chars == nil)
397 return NoSymbol;
398
399 // FIXME: Some dead keys are given as NBSP + combining character
400 if ([chars length] != 1)
401 return NoSymbol;
402
403 // Dead key?
404 if ([[nsevent characters] length] == 0)
405 return ucs2keysym(ucs2combining([chars characterAtIndex:0]));
406
407 return ucs2keysym([chars characterAtIndex:0]);
408}