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