blob: e51cba9de40a5597d3e9ea988d960d3637cca0ef [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 Ossmana4f0f182011-06-14 13:36:57 +000040static bool captured = false;
41
Pierre Ossman3d759112012-08-27 14:40:51 +000042int cocoa_capture_display(Fl_Window *win, bool all_displays)
Pierre Ossman407a5c32011-05-26 14:48:29 +000043{
44 NSWindow *nsw;
45
46 nsw = (NSWindow*)fl_xid(win);
47
Pierre Ossmana4f0f182011-06-14 13:36:57 +000048 if (!captured) {
Pierre Ossman3d759112012-08-27 14:40:51 +000049 if (all_displays) {
50 if (CGCaptureAllDisplays() != kCGErrorSuccess)
51 return 1;
52 } else {
53 CGDirectDisplayID displays[16];
54 CGDisplayCount count;
55 int index;
56
57 if (CGGetActiveDisplayList(16, displays, &count) != kCGErrorSuccess)
58 return 1;
59
Pierre Ossman5c23b9e2015-03-03 16:26:03 +010060 if (count != (unsigned)Fl::screen_count())
Pierre Ossman3d759112012-08-27 14:40:51 +000061 return 1;
62
63#ifdef HAVE_FLTK_FULLSCREEN_SCREENS
64 index = Fl::screen_num(win->x(), win->y(), win->w(), win->h());
65#else
66 index = 0;
67#endif
68
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.
102#ifdef HAVE_FLTK_FULLSCREEN
103 if (win->fullscreen_active() && win->contains(Fl::focus()))
104 newlevel = NSStatusWindowLevel;
105 else
106#endif
107 newlevel = NSNormalWindowLevel;
108
109 // Only change if different as the level change also moves the window
110 // to the top of that level.
111 if ([nsw level] != newlevel)
112 [nsw setLevel:newlevel];
113}
Pierre Ossman6b743d02014-07-21 16:48:43 +0200114
115int cocoa_is_keyboard_event(const void *event)
116{
117 NSEvent *nsevent;
118
119 nsevent = (NSEvent*)event;
120
121 switch ([nsevent type]) {
122 case NSKeyDown:
123 case NSKeyUp:
124 case NSFlagsChanged:
125 return 1;
126 default:
127 return 0;
128 }
129}
130
131int cocoa_is_key_press(const void *event)
132{
133 NSEvent *nsevent;
134
135 nsevent = (NSEvent*)event;
136
137 if ([nsevent type] == NSKeyDown)
138 return 1;
139
140 if ([nsevent type] == NSFlagsChanged) {
141 UInt32 mask;
142
143 // We don't see any event on release of CapsLock
144 if ([nsevent keyCode] == 0x39)
145 return 1;
146
147 // These are entirely undocumented, but I cannot find any other way
148 // of differentiating between left and right keys
149 switch ([nsevent keyCode]) {
150 case 0x36:
151 mask = 0x0010;
152 break;
153 case 0x37:
154 mask = 0x0008;
155 break;
156 case 0x38:
157 mask = 0x0002;
158 break;
159 case 0x39:
160 // We don't see any event on release of CapsLock
161 return 1;
162 case 0x3A:
163 mask = 0x0020;
164 break;
165 case 0x3B:
166 mask = 0x0001;
167 break;
168 case 0x3C:
169 mask = 0x0004;
170 break;
171 case 0x3D:
172 mask = 0x0040;
173 break;
174 case 0x3E:
175 mask = 0x2000;
176 break;
177 default:
178 return 0;
179 }
180
181 if ([nsevent modifierFlags] & mask)
182 return 1;
183 else
184 return 0;
185 }
186
187 return 0;
188}
189
190int cocoa_event_keycode(const void *event)
191{
192 NSEvent *nsevent;
193
194 nsevent = (NSEvent*)event;
195
196 return [nsevent keyCode];
197}
198
199static NSString *key_translate(UInt16 keyCode, UInt32 modifierFlags)
200{
201 const UCKeyboardLayout *layout;
202 OSStatus err;
203
204 layout = NULL;
205
Pierre Ossman7665b792015-02-25 14:43:42 +0100206#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) || defined(__x86_64__)
Pierre Ossman6b743d02014-07-21 16:48:43 +0200207 TISInputSourceRef keyboard;
208 CFDataRef uchr;
209
210 keyboard = TISCopyCurrentKeyboardInputSource();
211 uchr = (CFDataRef)TISGetInputSourceProperty(keyboard,
212 kTISPropertyUnicodeKeyLayoutData);
213 if (uchr == NULL)
214 return nil;
215
216 layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
217#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
218 KeyboardLayoutRef old_layout;
219 int kind;
220
221 err = KLGetCurrentKeyboardLayout(&old_layout);
222 if (err != noErr)
223 return nil;
224
225 err = KLGetKeyboardLayoutProperty(old_layout, kKLKind,
226 (const void**)&kind);
227 if (err != noErr)
228 return nil;
229
230 // Old, crufty layout format?
231 if (kind == kKLKCHRKind) {
232 void *kchr_layout;
233
234 UInt32 chars, state;
235 char buf[3];
236
237 unichar result[16];
238 ByteCount in_len, out_len;
239
240 err = KLGetKeyboardLayoutProperty(old_layout, kKLKCHRData,
241 (const void**)&kchr_layout);
242 if (err != noErr)
243 return nil;
244
245 state = 0;
246
247 keyCode &= 0x7f;
248 modifierFlags &= 0xff00;
249
250 chars = KeyTranslate(kchr_layout, keyCode | modifierFlags, &state);
251
252 // Dead key?
253 if (state != 0) {
254 // We have no fool proof way of asking what dead key this is.
255 // Assume we get a spacing equivalent if we press the
256 // same key again, and try to deduce something from that.
257 chars = KeyTranslate(kchr_layout, keyCode | modifierFlags, &state);
258 }
259
260 buf[0] = (chars >> 16) & 0xff;
261 buf[1] = chars & 0xff;
262 buf[2] = '\0';
263
264 if (buf[0] == '\0') {
265 buf[0] = buf[1];
266 buf[1] = '\0';
267 }
268
269 // The data is now in some layout specific encoding. Need to convert
270 // this to unicode.
271
272 ScriptCode script;
273 TextEncoding encoding;
274 TECObjectRef converter;
275
276 script = (ScriptCode)GetScriptManagerVariable(smKeyScript);
277
278 err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
279 kTextRegionDontCare, NULL,
280 &encoding);
281 if (err != noErr)
282 return nil;
283
284 err = TECCreateConverter(&converter, encoding, kTextEncodingUnicodeV4_0);
285 if (err != noErr)
286 return nil;
287
288 in_len = strlen(buf);
289 out_len = sizeof(result);
290
291 err = TECConvertText(converter, (ConstTextPtr)buf, in_len, &in_len,
292 (TextPtr)result, out_len, &out_len);
293
294 TECDisposeConverter(converter);
295
296 if (err != noErr)
297 return nil;
298
299 return [NSString stringWithCharacters:result
300 length:(out_len / sizeof(unichar))];
301 }
302
303 if ((kind != kKLKCHRuchrKind) && (kind != kKLuchrKind))
304 return nil;
305
306 err = KLGetKeyboardLayoutProperty(old_layout, kKLuchrData,
307 (const void**)&layout);
308 if (err != noErr)
309 return nil;
310#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
311
312 if (layout == NULL)
313 return nil;
314
315 UInt32 dead_state;
316 UniCharCount max_len, actual_len;
317 UniChar string[255];
318
319 dead_state = 0;
320 max_len = sizeof(string)/sizeof(*string);
321
322 modifierFlags = (modifierFlags >> 8) & 0xff;
323
324 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
325 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
326 string);
327 if (err != noErr)
328 return nil;
329
330 // Dead key?
331 if (dead_state != 0) {
332 // We have no fool proof way of asking what dead key this is.
333 // Assume we get a spacing equivalent if we press the
334 // same key again, and try to deduce something from that.
335 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
336 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
337 string);
338 if (err != noErr)
339 return nil;
340 }
341
342 return [NSString stringWithCharacters:string length:actual_len];
343}
344
345// FIXME: We use hard coded values here as the constants didn't appear
346// in the OS X headers until 10.5.
347static const int kvk_map[][2] = {
348 { 0x24, XK_Return },
349 { 0x30, XK_Tab },
350 { 0x31, XK_space },
351 { 0x33, XK_BackSpace },
352 { 0x35, XK_Escape },
353 // This one is undocumented for unknown reasons
354 { 0x36, XK_Super_R },
355 { 0x37, XK_Super_L },
356 { 0x38, XK_Shift_L },
357 { 0x39, XK_Caps_Lock },
358 { 0x3A, XK_Alt_L },
359 { 0x3B, XK_Control_L },
360 { 0x3C, XK_Shift_R },
361 { 0x3D, XK_Alt_R },
362 { 0x3E, XK_Control_R },
363 { 0x40, XK_F17 },
364 { 0x48, XF86XK_AudioRaiseVolume },
365 { 0x49, XF86XK_AudioLowerVolume },
366 { 0x4A, XF86XK_AudioMute },
367 { 0x4F, XK_F18 },
368 { 0x50, XK_F19 },
369 { 0x5A, XK_F20 },
370 { 0x60, XK_F5 },
371 { 0x61, XK_F6 },
372 { 0x62, XK_F7 },
373 { 0x63, XK_F3 },
374 { 0x64, XK_F8 },
375 { 0x65, XK_F9 },
376 { 0x67, XK_F11 },
377 { 0x69, XK_F13 },
378 { 0x6A, XK_F16 },
379 { 0x6B, XK_F14 },
380 { 0x6D, XK_F10 },
381 // Also undocumented
382 { 0x6E, XK_Menu },
383 { 0x6F, XK_F12 },
384 { 0x71, XK_F15 },
385 // Should we send Insert here?
386 { 0x72, XK_Help },
387 { 0x73, XK_Home },
388 { 0x74, XK_Page_Up },
389 { 0x75, XK_Delete },
390 { 0x76, XK_F4 },
391 { 0x77, XK_End },
392 { 0x78, XK_F2 },
393 { 0x79, XK_Page_Down },
394 { 0x7A, XK_F1 },
395 { 0x7B, XK_Left },
396 { 0x7C, XK_Right },
397 { 0x7D, XK_Down },
398 { 0x7E, XK_Up },
399 // The OS X headers claim these keys are not layout independent.
400 // Could it be because of the state of the decimal key?
401 /* { 0x41, XK_KP_Decimal }, */ // see below
402 { 0x43, XK_KP_Multiply },
403 { 0x45, XK_KP_Add },
404 // OS X doesn't have NumLock, so is this really correct?
405 { 0x47, XK_Num_Lock },
406 { 0x4B, XK_KP_Divide },
407 { 0x4C, XK_KP_Enter },
408 { 0x4E, XK_KP_Subtract },
409 { 0x51, XK_KP_Equal },
410 { 0x52, XK_KP_0 },
411 { 0x53, XK_KP_1 },
412 { 0x54, XK_KP_2 },
413 { 0x55, XK_KP_3 },
414 { 0x56, XK_KP_4 },
415 { 0x57, XK_KP_5 },
416 { 0x58, XK_KP_6 },
417 { 0x59, XK_KP_7 },
418 { 0x5B, XK_KP_8 },
419 { 0x5C, XK_KP_9 },
420};
421
422int cocoa_event_keysym(const void *event)
423{
424 NSEvent *nsevent;
425
426 UInt16 key_code;
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100427 size_t i;
Pierre Ossman6b743d02014-07-21 16:48:43 +0200428
429 NSString *chars;
430 UInt32 modifiers;
431
432 nsevent = (NSEvent*)event;
433
434 key_code = [nsevent keyCode];
435
436 // Start with keys that either don't generate a symbol, or
437 // generate the same symbol as some other key.
438 for (i = 0;i < sizeof(kvk_map)/sizeof(kvk_map[0]);i++) {
439 if (key_code == kvk_map[i][0])
440 return kvk_map[i][1];
441 }
442
443 // OS X always sends the same key code for the decimal key on the
444 // num pad, but X11 wants different keysyms depending on if it should
445 // be a comma or full stop.
446 if (key_code == 0x41) {
447 switch ([[nsevent charactersIgnoringModifiers] UTF8String][0]) {
448 case ',':
449 return XK_KP_Separator;
450 case '.':
451 return XK_KP_Decimal;
452 default:
453 return NoSymbol;
454 }
455 }
456
457 // We want a "normal" symbol out of the event, which basically means
458 // we only respect the shift and alt/altgr modifiers. Cocoa can help
459 // us if we only wanted shift, but as we also want alt/altgr, we'll
460 // have to do some lookup ourselves. This matches our behaviour on
461 // other platforms.
462
463 modifiers = 0;
464 if ([nsevent modifierFlags] & NSAlphaShiftKeyMask)
465 modifiers |= alphaLock;
466 if ([nsevent modifierFlags] & NSShiftKeyMask)
467 modifiers |= shiftKey;
468 if ([nsevent modifierFlags] & NSAlternateKeyMask)
469 modifiers |= optionKey;
470
471 chars = key_translate(key_code, modifiers);
472 if (chars == nil)
473 return NoSymbol;
474
475 // FIXME: Some dead keys are given as NBSP + combining character
476 if ([chars length] != 1)
477 return NoSymbol;
478
479 // Dead key?
480 if ([[nsevent characters] length] == 0)
481 return ucs2keysym(ucs2combining([chars characterAtIndex:0]));
482
483 return ucs2keysym([chars characterAtIndex:0]);
484}