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