blob: fc3b5bcfe3367650900f319ebe1fa7404e26deb5 [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
113int cocoa_is_keyboard_event(const void *event)
114{
115 NSEvent *nsevent;
116
117 nsevent = (NSEvent*)event;
118
119 switch ([nsevent type]) {
120 case NSKeyDown:
121 case NSKeyUp:
122 case NSFlagsChanged:
123 return 1;
124 default:
125 return 0;
126 }
127}
128
129int cocoa_is_key_press(const void *event)
130{
131 NSEvent *nsevent;
132
133 nsevent = (NSEvent*)event;
134
135 if ([nsevent type] == NSKeyDown)
136 return 1;
137
138 if ([nsevent type] == NSFlagsChanged) {
139 UInt32 mask;
140
141 // We don't see any event on release of CapsLock
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100142 if ([nsevent keyCode] == kVK_CapsLock)
Pierre Ossman6b743d02014-07-21 16:48:43 +0200143 return 1;
144
145 // These are entirely undocumented, but I cannot find any other way
146 // of differentiating between left and right keys
147 switch ([nsevent keyCode]) {
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100148 case kVK_RightCommand:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200149 mask = 0x0010;
150 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100151 case kVK_Command:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200152 mask = 0x0008;
153 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100154 case kVK_Shift:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200155 mask = 0x0002;
156 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100157 case kVK_CapsLock:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200158 // We don't see any event on release of CapsLock
159 return 1;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100160 case kVK_Option:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200161 mask = 0x0020;
162 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100163 case kVK_Control:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200164 mask = 0x0001;
165 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100166 case kVK_RightShift:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200167 mask = 0x0004;
168 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100169 case kVK_RightOption:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200170 mask = 0x0040;
171 break;
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100172 case kVK_RightControl:
Pierre Ossman6b743d02014-07-21 16:48:43 +0200173 mask = 0x2000;
174 break;
175 default:
176 return 0;
177 }
178
179 if ([nsevent modifierFlags] & mask)
180 return 1;
181 else
182 return 0;
183 }
184
185 return 0;
186}
187
188int cocoa_event_keycode(const void *event)
189{
190 NSEvent *nsevent;
191
192 nsevent = (NSEvent*)event;
193
194 return [nsevent keyCode];
195}
196
197static NSString *key_translate(UInt16 keyCode, UInt32 modifierFlags)
198{
199 const UCKeyboardLayout *layout;
200 OSStatus err;
201
202 layout = NULL;
203
Pierre Ossman6b743d02014-07-21 16:48:43 +0200204 TISInputSourceRef keyboard;
205 CFDataRef uchr;
206
207 keyboard = TISCopyCurrentKeyboardInputSource();
208 uchr = (CFDataRef)TISGetInputSourceProperty(keyboard,
209 kTISPropertyUnicodeKeyLayoutData);
210 if (uchr == NULL)
211 return nil;
212
213 layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
Pierre Ossman6b743d02014-07-21 16:48:43 +0200214 if (layout == NULL)
215 return nil;
216
217 UInt32 dead_state;
218 UniCharCount max_len, actual_len;
219 UniChar string[255];
220
221 dead_state = 0;
222 max_len = sizeof(string)/sizeof(*string);
223
224 modifierFlags = (modifierFlags >> 8) & 0xff;
225
226 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
227 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
228 string);
229 if (err != noErr)
230 return nil;
231
232 // Dead key?
233 if (dead_state != 0) {
234 // We have no fool proof way of asking what dead key this is.
235 // Assume we get a spacing equivalent if we press the
236 // same key again, and try to deduce something from that.
237 err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
238 LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
239 string);
240 if (err != noErr)
241 return nil;
242 }
243
244 return [NSString stringWithCharacters:string length:actual_len];
245}
246
Pierre Ossman6b743d02014-07-21 16:48:43 +0200247static const int kvk_map[][2] = {
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100248 { kVK_Return, XK_Return },
249 { kVK_Tab, XK_Tab },
250 { kVK_Space, XK_space },
251 { kVK_Delete, XK_BackSpace },
252 { kVK_Escape, XK_Escape },
253 { kVK_RightCommand, XK_Super_R },
254 { kVK_Command, XK_Super_L },
255 { kVK_Shift, XK_Shift_L },
256 { kVK_CapsLock, XK_Caps_Lock },
257 { kVK_Option, XK_Alt_L },
258 { kVK_Control, XK_Control_L },
259 { kVK_RightShift, XK_Shift_R },
260 { kVK_RightOption, XK_Alt_R },
261 { kVK_RightControl, XK_Control_R },
262 { kVK_F17, XK_F17 },
263 { kVK_VolumeUp, XF86XK_AudioRaiseVolume },
264 { kVK_VolumeDown, XF86XK_AudioLowerVolume },
265 { kVK_Mute, XF86XK_AudioMute },
266 { kVK_F18, XK_F18 },
267 { kVK_F19, XK_F19 },
268 { kVK_F20, XK_F20 },
269 { kVK_F5, XK_F5 },
270 { kVK_F6, XK_F6 },
271 { kVK_F7, XK_F7 },
272 { kVK_F3, XK_F3 },
273 { kVK_F8, XK_F8 },
274 { kVK_F9, XK_F9 },
275 { kVK_F11, XK_F11 },
276 { kVK_F13, XK_F13 },
277 { kVK_F16, XK_F16 },
278 { kVK_F14, XK_F14 },
279 { kVK_F10, XK_F10 },
280 { kVK_Menu, XK_Menu },
281 { kVK_F12, XK_F12 },
282 { kVK_F15, XK_F15 },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200283 // Should we send Insert here?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100284 { kVK_Help, XK_Help },
285 { kVK_Home, XK_Home },
286 { kVK_PageUp, XK_Page_Up },
287 { kVK_ForwardDelete, XK_Delete },
288 { kVK_F4, XK_F4 },
289 { kVK_End, XK_End },
290 { kVK_F2, XK_F2 },
291 { kVK_PageDown, XK_Page_Down },
292 { kVK_F1, XK_F1 },
293 { kVK_LeftArrow, XK_Left },
294 { kVK_RightArrow, XK_Right },
295 { kVK_DownArrow, XK_Down },
296 { kVK_UpArrow, XK_Up },
297
Pierre Ossman6b743d02014-07-21 16:48:43 +0200298 // The OS X headers claim these keys are not layout independent.
299 // Could it be because of the state of the decimal key?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100300 /* { kVK_ANSI_KeypadDecimal, XK_KP_Decimal }, */ // see below
301 { kVK_ANSI_KeypadMultiply, XK_KP_Multiply },
302 { kVK_ANSI_KeypadPlus, XK_KP_Add },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200303 // OS X doesn't have NumLock, so is this really correct?
Pierre Ossman82fda4b2017-02-09 14:46:50 +0100304 { kVK_ANSI_KeypadClear, XK_Num_Lock },
305 { kVK_ANSI_KeypadDivide, XK_KP_Divide },
306 { kVK_ANSI_KeypadEnter, XK_KP_Enter },
307 { kVK_ANSI_KeypadMinus, XK_KP_Subtract },
308 { kVK_ANSI_KeypadEquals, XK_KP_Equal },
309 { kVK_ANSI_Keypad0, XK_KP_0 },
310 { kVK_ANSI_Keypad1, XK_KP_1 },
311 { kVK_ANSI_Keypad2, XK_KP_2 },
312 { kVK_ANSI_Keypad3, XK_KP_3 },
313 { kVK_ANSI_Keypad4, XK_KP_4 },
314 { kVK_ANSI_Keypad5, XK_KP_5 },
315 { kVK_ANSI_Keypad6, XK_KP_6 },
316 { kVK_ANSI_Keypad7, XK_KP_7 },
317 { kVK_ANSI_Keypad8, XK_KP_8 },
318 { kVK_ANSI_Keypad9, XK_KP_9 },
Pierre Ossman6b743d02014-07-21 16:48:43 +0200319};
320
321int cocoa_event_keysym(const void *event)
322{
323 NSEvent *nsevent;
324
325 UInt16 key_code;
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100326 size_t i;
Pierre Ossman6b743d02014-07-21 16:48:43 +0200327
328 NSString *chars;
329 UInt32 modifiers;
330
331 nsevent = (NSEvent*)event;
332
333 key_code = [nsevent keyCode];
334
335 // Start with keys that either don't generate a symbol, or
336 // generate the same symbol as some other key.
337 for (i = 0;i < sizeof(kvk_map)/sizeof(kvk_map[0]);i++) {
338 if (key_code == kvk_map[i][0])
339 return kvk_map[i][1];
340 }
341
342 // OS X always sends the same key code for the decimal key on the
343 // num pad, but X11 wants different keysyms depending on if it should
344 // be a comma or full stop.
345 if (key_code == 0x41) {
346 switch ([[nsevent charactersIgnoringModifiers] UTF8String][0]) {
347 case ',':
348 return XK_KP_Separator;
349 case '.':
350 return XK_KP_Decimal;
351 default:
352 return NoSymbol;
353 }
354 }
355
356 // We want a "normal" symbol out of the event, which basically means
357 // we only respect the shift and alt/altgr modifiers. Cocoa can help
358 // us if we only wanted shift, but as we also want alt/altgr, we'll
359 // have to do some lookup ourselves. This matches our behaviour on
360 // other platforms.
361
362 modifiers = 0;
363 if ([nsevent modifierFlags] & NSAlphaShiftKeyMask)
364 modifiers |= alphaLock;
365 if ([nsevent modifierFlags] & NSShiftKeyMask)
366 modifiers |= shiftKey;
367 if ([nsevent modifierFlags] & NSAlternateKeyMask)
368 modifiers |= optionKey;
369
370 chars = key_translate(key_code, modifiers);
371 if (chars == nil)
372 return NoSymbol;
373
374 // FIXME: Some dead keys are given as NBSP + combining character
375 if ([chars length] != 1)
376 return NoSymbol;
377
378 // Dead key?
379 if ([[nsevent characters] length] == 0)
380 return ucs2keysym(ucs2combining([chars characterAtIndex:0]));
381
382 return ucs2keysym([chars characterAtIndex:0]);
383}