merged changes to revision 24077
[blender-staging.git] / intern / ghost / intern / GHOST_SystemCocoa.mm
1 /**
2  * $Id$
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s):      Maarten Gribnau 05/2001
25  *                                      Damien Plisson 09/2009
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #import <Cocoa/Cocoa.h>
31
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
35
36 #include "GHOST_SystemCocoa.h"
37
38 #include "GHOST_DisplayManagerCocoa.h"
39 #include "GHOST_EventKey.h"
40 #include "GHOST_EventButton.h"
41 #include "GHOST_EventCursor.h"
42 #include "GHOST_EventWheel.h"
43 #include "GHOST_EventNDOF.h"
44
45 #include "GHOST_TimerManager.h"
46 #include "GHOST_TimerTask.h"
47 #include "GHOST_WindowManager.h"
48 #include "GHOST_WindowCocoa.h"
49 #include "GHOST_NDOFManager.h"
50 #include "AssertMacros.h"
51
52 #pragma mark KeyMap, mouse converters
53
54
55 /* Keycodes from Carbon include file */
56 /*  
57  *  Summary:
58  *    Virtual keycodes
59  *  
60  *  Discussion:
61  *    These constants are the virtual keycodes defined originally in
62  *    Inside Mac Volume V, pg. V-191. They identify physical keys on a
63  *    keyboard. Those constants with "ANSI" in the name are labeled
64  *    according to the key position on an ANSI-standard US keyboard.
65  *    For example, kVK_ANSI_A indicates the virtual keycode for the key
66  *    with the letter 'A' in the US keyboard layout. Other keyboard
67  *    layouts may have the 'A' key label on a different physical key;
68  *    in this case, pressing 'A' will generate a different virtual
69  *    keycode.
70  */
71 enum {
72         kVK_ANSI_A                    = 0x00,
73         kVK_ANSI_S                    = 0x01,
74         kVK_ANSI_D                    = 0x02,
75         kVK_ANSI_F                    = 0x03,
76         kVK_ANSI_H                    = 0x04,
77         kVK_ANSI_G                    = 0x05,
78         kVK_ANSI_Z                    = 0x06,
79         kVK_ANSI_X                    = 0x07,
80         kVK_ANSI_C                    = 0x08,
81         kVK_ANSI_V                    = 0x09,
82         kVK_ANSI_B                    = 0x0B,
83         kVK_ANSI_Q                    = 0x0C,
84         kVK_ANSI_W                    = 0x0D,
85         kVK_ANSI_E                    = 0x0E,
86         kVK_ANSI_R                    = 0x0F,
87         kVK_ANSI_Y                    = 0x10,
88         kVK_ANSI_T                    = 0x11,
89         kVK_ANSI_1                    = 0x12,
90         kVK_ANSI_2                    = 0x13,
91         kVK_ANSI_3                    = 0x14,
92         kVK_ANSI_4                    = 0x15,
93         kVK_ANSI_6                    = 0x16,
94         kVK_ANSI_5                    = 0x17,
95         kVK_ANSI_Equal                = 0x18,
96         kVK_ANSI_9                    = 0x19,
97         kVK_ANSI_7                    = 0x1A,
98         kVK_ANSI_Minus                = 0x1B,
99         kVK_ANSI_8                    = 0x1C,
100         kVK_ANSI_0                    = 0x1D,
101         kVK_ANSI_RightBracket         = 0x1E,
102         kVK_ANSI_O                    = 0x1F,
103         kVK_ANSI_U                    = 0x20,
104         kVK_ANSI_LeftBracket          = 0x21,
105         kVK_ANSI_I                    = 0x22,
106         kVK_ANSI_P                    = 0x23,
107         kVK_ANSI_L                    = 0x25,
108         kVK_ANSI_J                    = 0x26,
109         kVK_ANSI_Quote                = 0x27,
110         kVK_ANSI_K                    = 0x28,
111         kVK_ANSI_Semicolon            = 0x29,
112         kVK_ANSI_Backslash            = 0x2A,
113         kVK_ANSI_Comma                = 0x2B,
114         kVK_ANSI_Slash                = 0x2C,
115         kVK_ANSI_N                    = 0x2D,
116         kVK_ANSI_M                    = 0x2E,
117         kVK_ANSI_Period               = 0x2F,
118         kVK_ANSI_Grave                = 0x32,
119         kVK_ANSI_KeypadDecimal        = 0x41,
120         kVK_ANSI_KeypadMultiply       = 0x43,
121         kVK_ANSI_KeypadPlus           = 0x45,
122         kVK_ANSI_KeypadClear          = 0x47,
123         kVK_ANSI_KeypadDivide         = 0x4B,
124         kVK_ANSI_KeypadEnter          = 0x4C,
125         kVK_ANSI_KeypadMinus          = 0x4E,
126         kVK_ANSI_KeypadEquals         = 0x51,
127         kVK_ANSI_Keypad0              = 0x52,
128         kVK_ANSI_Keypad1              = 0x53,
129         kVK_ANSI_Keypad2              = 0x54,
130         kVK_ANSI_Keypad3              = 0x55,
131         kVK_ANSI_Keypad4              = 0x56,
132         kVK_ANSI_Keypad5              = 0x57,
133         kVK_ANSI_Keypad6              = 0x58,
134         kVK_ANSI_Keypad7              = 0x59,
135         kVK_ANSI_Keypad8              = 0x5B,
136         kVK_ANSI_Keypad9              = 0x5C
137 };
138
139 /* keycodes for keys that are independent of keyboard layout*/
140 enum {
141         kVK_Return                    = 0x24,
142         kVK_Tab                       = 0x30,
143         kVK_Space                     = 0x31,
144         kVK_Delete                    = 0x33,
145         kVK_Escape                    = 0x35,
146         kVK_Command                   = 0x37,
147         kVK_Shift                     = 0x38,
148         kVK_CapsLock                  = 0x39,
149         kVK_Option                    = 0x3A,
150         kVK_Control                   = 0x3B,
151         kVK_RightShift                = 0x3C,
152         kVK_RightOption               = 0x3D,
153         kVK_RightControl              = 0x3E,
154         kVK_Function                  = 0x3F,
155         kVK_F17                       = 0x40,
156         kVK_VolumeUp                  = 0x48,
157         kVK_VolumeDown                = 0x49,
158         kVK_Mute                      = 0x4A,
159         kVK_F18                       = 0x4F,
160         kVK_F19                       = 0x50,
161         kVK_F20                       = 0x5A,
162         kVK_F5                        = 0x60,
163         kVK_F6                        = 0x61,
164         kVK_F7                        = 0x62,
165         kVK_F3                        = 0x63,
166         kVK_F8                        = 0x64,
167         kVK_F9                        = 0x65,
168         kVK_F11                       = 0x67,
169         kVK_F13                       = 0x69,
170         kVK_F16                       = 0x6A,
171         kVK_F14                       = 0x6B,
172         kVK_F10                       = 0x6D,
173         kVK_F12                       = 0x6F,
174         kVK_F15                       = 0x71,
175         kVK_Help                      = 0x72,
176         kVK_Home                      = 0x73,
177         kVK_PageUp                    = 0x74,
178         kVK_ForwardDelete             = 0x75,
179         kVK_F4                        = 0x76,
180         kVK_End                       = 0x77,
181         kVK_F2                        = 0x78,
182         kVK_PageDown                  = 0x79,
183         kVK_F1                        = 0x7A,
184         kVK_LeftArrow                 = 0x7B,
185         kVK_RightArrow                = 0x7C,
186         kVK_DownArrow                 = 0x7D,
187         kVK_UpArrow                   = 0x7E
188 };
189
190 /* ISO keyboards only*/
191 enum {
192         kVK_ISO_Section               = 0x0A
193 };
194
195 /* JIS keyboards only*/
196 enum {
197         kVK_JIS_Yen                   = 0x5D,
198         kVK_JIS_Underscore            = 0x5E,
199         kVK_JIS_KeypadComma           = 0x5F,
200         kVK_JIS_Eisu                  = 0x66,
201         kVK_JIS_Kana                  = 0x68
202 };
203
204
205 static GHOST_TButtonMask convertButton(int button)
206 {
207         switch (button) {
208                 case 0:
209                         return GHOST_kButtonMaskLeft;
210                 case 1:
211                         return GHOST_kButtonMaskRight;
212                 case 2:
213                         return GHOST_kButtonMaskMiddle;
214                 case 3:
215                         return GHOST_kButtonMaskButton4;
216                 case 4:
217                         return GHOST_kButtonMaskButton5;
218                 default:
219                         return GHOST_kButtonMaskLeft;
220         }
221 }
222
223 /**
224  * Converts Mac rawkey codes (same for Cocoa & Carbon)
225  * into GHOST key codes
226  * @param rawCode The raw physical key code
227  * @param recvChar the character ignoring modifiers (except for shift)
228  * @return Ghost key code
229  */
230 static GHOST_TKey convertKey(int rawCode, unichar recvChar) 
231 {       
232         
233         //printf("\nrecvchar %c 0x%x",recvChar,recvChar);
234         switch (rawCode) {
235                 /*Physical keycodes not used due to map changes in int'l keyboards
236                 case kVK_ANSI_A:        return GHOST_kKeyA;
237                 case kVK_ANSI_B:        return GHOST_kKeyB;
238                 case kVK_ANSI_C:        return GHOST_kKeyC;
239                 case kVK_ANSI_D:        return GHOST_kKeyD;
240                 case kVK_ANSI_E:        return GHOST_kKeyE;
241                 case kVK_ANSI_F:        return GHOST_kKeyF;
242                 case kVK_ANSI_G:        return GHOST_kKeyG;
243                 case kVK_ANSI_H:        return GHOST_kKeyH;
244                 case kVK_ANSI_I:        return GHOST_kKeyI;
245                 case kVK_ANSI_J:        return GHOST_kKeyJ;
246                 case kVK_ANSI_K:        return GHOST_kKeyK;
247                 case kVK_ANSI_L:        return GHOST_kKeyL;
248                 case kVK_ANSI_M:        return GHOST_kKeyM;
249                 case kVK_ANSI_N:        return GHOST_kKeyN;
250                 case kVK_ANSI_O:        return GHOST_kKeyO;
251                 case kVK_ANSI_P:        return GHOST_kKeyP;
252                 case kVK_ANSI_Q:        return GHOST_kKeyQ;
253                 case kVK_ANSI_R:        return GHOST_kKeyR;
254                 case kVK_ANSI_S:        return GHOST_kKeyS;
255                 case kVK_ANSI_T:        return GHOST_kKeyT;
256                 case kVK_ANSI_U:        return GHOST_kKeyU;
257                 case kVK_ANSI_V:        return GHOST_kKeyV;
258                 case kVK_ANSI_W:        return GHOST_kKeyW;
259                 case kVK_ANSI_X:        return GHOST_kKeyX;
260                 case kVK_ANSI_Y:        return GHOST_kKeyY;
261                 case kVK_ANSI_Z:        return GHOST_kKeyZ;*/
262                 
263                 /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/
264                 case kVK_ISO_Section: return    GHOST_kKeyUnknown;
265                 case kVK_ANSI_1:        return GHOST_kKey1;
266                 case kVK_ANSI_2:        return GHOST_kKey2;
267                 case kVK_ANSI_3:        return GHOST_kKey3;
268                 case kVK_ANSI_4:        return GHOST_kKey4;
269                 case kVK_ANSI_5:        return GHOST_kKey5;
270                 case kVK_ANSI_6:        return GHOST_kKey6;
271                 case kVK_ANSI_7:        return GHOST_kKey7;
272                 case kVK_ANSI_8:        return GHOST_kKey8;
273                 case kVK_ANSI_9:        return GHOST_kKey9;
274                 case kVK_ANSI_0:        return GHOST_kKey0;
275         
276                 case kVK_ANSI_Keypad0:                  return GHOST_kKeyNumpad0;
277                 case kVK_ANSI_Keypad1:                  return GHOST_kKeyNumpad1;
278                 case kVK_ANSI_Keypad2:                  return GHOST_kKeyNumpad2;
279                 case kVK_ANSI_Keypad3:                  return GHOST_kKeyNumpad3;
280                 case kVK_ANSI_Keypad4:                  return GHOST_kKeyNumpad4;
281                 case kVK_ANSI_Keypad5:                  return GHOST_kKeyNumpad5;
282                 case kVK_ANSI_Keypad6:                  return GHOST_kKeyNumpad6;
283                 case kVK_ANSI_Keypad7:                  return GHOST_kKeyNumpad7;
284                 case kVK_ANSI_Keypad8:                  return GHOST_kKeyNumpad8;
285                 case kVK_ANSI_Keypad9:                  return GHOST_kKeyNumpad9;
286                 case kVK_ANSI_KeypadDecimal:    return GHOST_kKeyNumpadPeriod;
287                 case kVK_ANSI_KeypadEnter:              return GHOST_kKeyNumpadEnter;
288                 case kVK_ANSI_KeypadPlus:               return GHOST_kKeyNumpadPlus;
289                 case kVK_ANSI_KeypadMinus:              return GHOST_kKeyNumpadMinus;
290                 case kVK_ANSI_KeypadMultiply:   return GHOST_kKeyNumpadAsterisk;
291                 case kVK_ANSI_KeypadDivide:     return GHOST_kKeyNumpadSlash;
292                 case kVK_ANSI_KeypadClear:              return GHOST_kKeyUnknown;
293
294                 case kVK_F1:                            return GHOST_kKeyF1;
295                 case kVK_F2:                            return GHOST_kKeyF2;
296                 case kVK_F3:                            return GHOST_kKeyF3;
297                 case kVK_F4:                            return GHOST_kKeyF4;
298                 case kVK_F5:                            return GHOST_kKeyF5;
299                 case kVK_F6:                            return GHOST_kKeyF6;
300                 case kVK_F7:                            return GHOST_kKeyF7;
301                 case kVK_F8:                            return GHOST_kKeyF8;
302                 case kVK_F9:                            return GHOST_kKeyF9;
303                 case kVK_F10:                           return GHOST_kKeyF10;
304                 case kVK_F11:                           return GHOST_kKeyF11;
305                 case kVK_F12:                           return GHOST_kKeyF12;
306                 case kVK_F13:                           return GHOST_kKeyF13;
307                 case kVK_F14:                           return GHOST_kKeyF14;
308                 case kVK_F15:                           return GHOST_kKeyF15;
309                 case kVK_F16:                           return GHOST_kKeyF16;
310                 case kVK_F17:                           return GHOST_kKeyF17;
311                 case kVK_F18:                           return GHOST_kKeyF18;
312                 case kVK_F19:                           return GHOST_kKeyF19;
313                 case kVK_F20:                           return GHOST_kKeyF20;
314                         
315                 case kVK_UpArrow:                       return GHOST_kKeyUpArrow;
316                 case kVK_DownArrow:                     return GHOST_kKeyDownArrow;
317                 case kVK_LeftArrow:                     return GHOST_kKeyLeftArrow;
318                 case kVK_RightArrow:            return GHOST_kKeyRightArrow;
319                         
320                 case kVK_Return:                        return GHOST_kKeyEnter;
321                 case kVK_Delete:                        return GHOST_kKeyBackSpace;
322                 case kVK_ForwardDelete:         return GHOST_kKeyDelete;
323                 case kVK_Escape:                        return GHOST_kKeyEsc;
324                 case kVK_Tab:                           return GHOST_kKeyTab;
325                 case kVK_Space:                         return GHOST_kKeySpace;
326                         
327                 case kVK_Home:                          return GHOST_kKeyHome;
328                 case kVK_End:                           return GHOST_kKeyEnd;
329                 case kVK_PageUp:                        return GHOST_kKeyUpPage;
330                 case kVK_PageDown:                      return GHOST_kKeyDownPage;
331                         
332                 /*case kVK_ANSI_Minus:          return GHOST_kKeyMinus;
333                 case kVK_ANSI_Equal:            return GHOST_kKeyEqual;
334                 case kVK_ANSI_Comma:            return GHOST_kKeyComma;
335                 case kVK_ANSI_Period:           return GHOST_kKeyPeriod;
336                 case kVK_ANSI_Slash:            return GHOST_kKeySlash;
337                 case kVK_ANSI_Semicolon:        return GHOST_kKeySemicolon;
338                 case kVK_ANSI_Quote:            return GHOST_kKeyQuote;
339                 case kVK_ANSI_Backslash:        return GHOST_kKeyBackslash;
340                 case kVK_ANSI_LeftBracket:      return GHOST_kKeyLeftBracket;
341                 case kVK_ANSI_RightBracket:     return GHOST_kKeyRightBracket;
342                 case kVK_ANSI_Grave:            return GHOST_kKeyAccentGrave;*/
343                         
344                 case kVK_VolumeUp:
345                 case kVK_VolumeDown:
346                 case kVK_Mute:
347                         return GHOST_kKeyUnknown;
348                         
349                 default:
350                         /*Then detect on character value for "remappable" keys in int'l keyboards*/
351                         if ((recvChar >= 'A') && (recvChar <= 'Z')) {
352                                 return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA);
353                         } else if ((recvChar >= 'a') && (recvChar <= 'z')) {
354                                 return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA);
355                         } else
356                         switch (recvChar) {
357                                 case '-':       return GHOST_kKeyMinus;
358                                 case '=':       return GHOST_kKeyEqual;
359                                 case ',':       return GHOST_kKeyComma;
360                                 case '.':       return GHOST_kKeyPeriod;
361                                 case '/':       return GHOST_kKeySlash;
362                                 case ';':       return GHOST_kKeySemicolon;
363                                 case '\'':      return GHOST_kKeyQuote;
364                                 case '\\':      return GHOST_kKeyBackslash;
365                                 case '[':       return GHOST_kKeyLeftBracket;
366                                 case ']':       return GHOST_kKeyRightBracket;
367                                 case '`':       return GHOST_kKeyAccentGrave;
368                                 default:
369                                         return GHOST_kKeyUnknown;
370                         }
371         }
372         return GHOST_kKeyUnknown;
373 }
374
375 /* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
376  * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
377  * I am not sure how international this works...
378  * For cross-platform convention, we'll use the Latin ascii set instead.
379  * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
380  * 
381  */
382 static unsigned char convertRomanToLatin(unsigned char ascii)
383 {
384
385         if(ascii<128) return ascii;
386         
387         switch(ascii) {
388         case 128:       return 142;
389         case 129:       return 143;
390         case 130:       return 128;
391         case 131:       return 201;
392         case 132:       return 209;
393         case 133:       return 214;
394         case 134:       return 220;
395         case 135:       return 225;
396         case 136:       return 224;
397         case 137:       return 226;
398         case 138:       return 228;
399         case 139:       return 227;
400         case 140:       return 229;
401         case 141:       return 231;
402         case 142:       return 233;
403         case 143:       return 232;
404         case 144:       return 234;
405         case 145:       return 235;
406         case 146:       return 237;
407         case 147:       return 236;
408         case 148:       return 238;
409         case 149:       return 239;
410         case 150:       return 241;
411         case 151:       return 243;
412         case 152:       return 242;
413         case 153:       return 244;
414         case 154:       return 246;
415         case 155:       return 245;
416         case 156:       return 250;
417         case 157:       return 249;
418         case 158:       return 251;
419         case 159:       return 252;
420         case 160:       return 0;
421         case 161:       return 176;
422         case 162:       return 162;
423         case 163:       return 163;
424         case 164:       return 167;
425         case 165:       return 183;
426         case 166:       return 182;
427         case 167:       return 223;
428         case 168:       return 174;
429         case 169:       return 169;
430         case 170:       return 174;
431         case 171:       return 180;
432         case 172:       return 168;
433         case 173:       return 0;
434         case 174:       return 198;
435         case 175:       return 216;
436         case 176:       return 0;
437         case 177:       return 177;
438         case 178:       return 0;
439         case 179:       return 0;
440         case 180:       return 165;
441         case 181:       return 181;
442         case 182:       return 0;
443         case 183:       return 0;
444         case 184:       return 215;
445         case 185:       return 0;
446         case 186:       return 0;
447         case 187:       return 170;
448         case 188:       return 186;
449         case 189:       return 0;
450         case 190:       return 230;
451         case 191:       return 248;
452         case 192:       return 191;
453         case 193:       return 161;
454         case 194:       return 172;
455         case 195:       return 0;
456         case 196:       return 0;
457         case 197:       return 0;
458         case 198:       return 0;
459         case 199:       return 171;
460         case 200:       return 187;
461         case 201:       return 201;
462         case 202:       return 0;
463         case 203:       return 192;
464         case 204:       return 195;
465         case 205:       return 213;
466         case 206:       return 0;
467         case 207:       return 0;
468         case 208:       return 0;
469         case 209:       return 0;
470         case 210:       return 0;
471         
472         case 214:       return 247;
473
474         case 229:       return 194;
475         case 230:       return 202;
476         case 231:       return 193;
477         case 232:       return 203;
478         case 233:       return 200;
479         case 234:       return 205;
480         case 235:       return 206;
481         case 236:       return 207;
482         case 237:       return 204;
483         case 238:       return 211;
484         case 239:       return 212;
485         case 240:       return 0;
486         case 241:       return 210;
487         case 242:       return 218;
488         case 243:       return 219;
489         case 244:       return 217;
490         case 245:       return 0;
491         case 246:       return 0;
492         case 247:       return 0;
493         case 248:       return 0;
494         case 249:       return 0;
495         case 250:       return 0;
496
497         
498                 default: return 0;
499         }
500
501 }
502
503 #define FIRSTFILEBUFLG 512
504 static bool g_hasFirstFile = false;
505 static char g_firstFileBuf[512];
506
507 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true
508 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
509         if (g_hasFirstFile) {
510                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
511                 buf[FIRSTFILEBUFLG - 1] = '\0';
512                 return 1;
513         } else {
514                 return 0; 
515         }
516 }
517
518
519 #pragma mark Cocoa objects
520
521 /**
522  * CocoaAppDelegate
523  * ObjC object to capture applicationShouldTerminate, and send quit event
524  **/
525 @interface CocoaAppDelegate : NSObject {
526         GHOST_SystemCocoa *systemCocoa;
527 }
528 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
529 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
530 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
531 - (void)applicationWillTerminate:(NSNotification *)aNotification;
532 @end
533
534 @implementation CocoaAppDelegate : NSObject
535 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
536 {
537         systemCocoa = sysCocoa;
538 }
539
540 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
541 {
542         NSLog(@"\nGet open file event from cocoa : %@",filename);
543         return YES;
544 }
545
546 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
547 {
548         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled
549         //Note that Cmd+Q is already handled by keyhandler
550     if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
551                 return NSTerminateCancel;//NSTerminateNow;
552         else
553                 return NSTerminateCancel;
554 }
555
556 // To avoid cancelling a log off process, we must use Cocoa termination process
557 // And this function is the only chance to perform clean up
558 // So WM_exit needs to be called directly, as the event loop will never run before termination
559 - (void)applicationWillTerminate:(NSNotification *)aNotification
560 {
561         /*G.afbreek = 0; //Let Cocoa perform the termination at the end
562         WM_exit(C);*/
563 }
564 @end
565
566
567
568 #pragma mark initialization/finalization
569
570
571 GHOST_SystemCocoa::GHOST_SystemCocoa()
572 {
573         m_modifierMask =0;
574         m_pressedMouseButtons =0;
575         m_cursorDelta_x=0;
576         m_cursorDelta_y=0;
577         m_displayManager = new GHOST_DisplayManagerCocoa ();
578         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
579         m_displayManager->initialize();
580
581         //NSEvent timeStamp is given in system uptime, state start date is boot time
582         //FIXME : replace by Cocoa equivalent
583         int mib[2];
584         struct timeval boottime;
585         size_t len;
586         
587         mib[0] = CTL_KERN;
588         mib[1] = KERN_BOOTTIME;
589         len = sizeof(struct timeval);
590         
591         sysctl(mib, 2, &boottime, &len, NULL, 0);
592         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
593         
594         m_ignoreWindowSizedMessages = false;
595 }
596
597 GHOST_SystemCocoa::~GHOST_SystemCocoa()
598 {
599 }
600
601
602 GHOST_TSuccess GHOST_SystemCocoa::init()
603 {
604         
605     GHOST_TSuccess success = GHOST_System::init();
606     if (success) {
607                 //ProcessSerialNumber psn;
608                 
609                 //Carbon stuff to move window & menu to foreground
610                 /*if (!GetCurrentProcess(&psn)) {
611                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
612                         SetFrontProcess(&psn);
613                 }*/
614                 
615                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
616                 if (NSApp == nil) {
617                         [NSApplication sharedApplication];
618                         
619                         if ([NSApp mainMenu] == nil) {
620                                 NSMenu *mainMenubar = [[NSMenu alloc] init];
621                                 NSMenuItem *menuItem;
622                                 NSMenu *windowMenu;
623                                 NSMenu *appMenu;
624                                 
625                                 //Create the application menu
626                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
627                                 
628                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
629                                 [appMenu addItem:[NSMenuItem separatorItem]];
630                                 
631                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
632                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
633                                  
634                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
635                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
636                                 
637                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
638                                 
639                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
640                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
641                                 
642                                 menuItem = [[NSMenuItem alloc] init];
643                                 [menuItem setSubmenu:appMenu];
644                                 
645                                 [mainMenubar addItem:menuItem];
646                                 [menuItem release];
647                                 [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5
648                                 [appMenu release];
649                                 
650                                 //Create the window menu
651                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
652                                 
653                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
654                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
655                                 
656                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
657                                 
658                                 menuItem = [[NSMenuItem alloc] init];
659                                 [menuItem setSubmenu:windowMenu];
660                                 
661                                 [mainMenubar addItem:menuItem];
662                                 [menuItem release];
663                                 
664                                 [NSApp setMainMenu:mainMenubar];
665                                 [NSApp setWindowsMenu:windowMenu];
666                                 [windowMenu release];
667                         }
668                 }
669                 if ([NSApp delegate] == nil) {
670                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
671                         [appDelegate setSystemCocoa:this];
672                         [NSApp setDelegate:appDelegate];
673                 }
674                 
675                 [NSApp finishLaunching];
676                                 
677                 [pool drain];
678     }
679     return success;
680 }
681
682
683 #pragma mark window management
684
685 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
686 {
687         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
688         int mib[2];
689         struct timeval boottime;
690         size_t len;
691         
692         mib[0] = CTL_KERN;
693         mib[1] = KERN_BOOTTIME;
694         len = sizeof(struct timeval);
695         
696         sysctl(mib, 2, &boottime, &len, NULL, 0);
697
698         return ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
699 }
700
701
702 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
703 {
704         //Note that OS X supports monitor hot plug
705         // We do not support multiple monitors at the moment
706         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
707
708         GHOST_TUns8 count = [[NSScreen screens] count];
709
710         [pool drain];
711         return count;
712 }
713
714
715 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
716 {
717         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
718         //Get visible frame, that is frame excluding dock and top menu bar
719         NSRect frame = [[NSScreen mainScreen] visibleFrame];
720         
721         //Returns max window contents (excluding title bar...)
722         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
723                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
724         
725         width = contentRect.size.width;
726         height = contentRect.size.height;
727
728         [pool drain];
729 }
730
731
732 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
733         const STR_String& title, 
734         GHOST_TInt32 left,
735         GHOST_TInt32 top,
736         GHOST_TUns32 width,
737         GHOST_TUns32 height,
738         GHOST_TWindowState state,
739         GHOST_TDrawingContextType type,
740         bool stereoVisual,
741         const GHOST_TEmbedderWindowID parentWindow
742 )
743 {
744     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
745         GHOST_IWindow* window = 0;
746         
747         //Get the available rect for including window contents
748         NSRect frame = [[NSScreen mainScreen] visibleFrame];
749         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
750                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
751         
752         //Ensures window top left is inside this available rect
753         left = left > contentRect.origin.x ? left : contentRect.origin.x;
754         top = top > contentRect.origin.y ? top : contentRect.origin.y;
755         
756         window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type);
757
758     if (window) {
759         if (window->getValid()) {
760             // Store the pointer to the window 
761             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
762             m_windowManager->addWindow(window);
763             m_windowManager->setActiveWindow(window);
764                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
765             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
766                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
767
768         }
769         else {
770                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
771             delete window;
772             window = 0;
773         }
774     }
775         else {
776                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
777         }
778         [pool drain];
779     return window;
780 }
781
782 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
783 {       
784         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
785
786         *window = currentWindow;
787         
788         return currentWindow->setState(GHOST_kWindowStateFullScreen);
789 }
790
791 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
792 {       
793         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
794         
795         return currentWindow->setState(GHOST_kWindowStateNormal);
796 }
797
798
799         
800 /**
801  * @note : returns coordinates in Cocoa screen coordinates
802  */
803 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
804 {
805     NSPoint mouseLoc = [NSEvent mouseLocation];
806         
807     // Returns the mouse location in screen coordinates
808     x = (GHOST_TInt32)mouseLoc.x;
809     y = (GHOST_TInt32)mouseLoc.y;
810     return GHOST_kSuccess;
811 }
812
813 /**
814  * @note : expect Cocoa screen coordinates
815  */
816 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
817 {
818         float xf=(float)x, yf=(float)y;
819         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
820         NSScreen *windowScreen = window->getScreen();
821         NSRect screenRect = [windowScreen frame];
822         
823         //Set position relative to current screen
824         xf -= screenRect.origin.x;
825         yf -= screenRect.origin.y;
826         
827         //Quartz Display Services uses the old coordinates (top left origin)
828         yf = screenRect.size.height -yf;
829
830         CGDisplayMoveCursorToPoint([[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
831
832     return GHOST_kSuccess;
833 }
834
835
836 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
837 {
838         unsigned int modifiers = [[NSApp currentEvent] modifierFlags];
839         //Direct query to modifierFlags can be used in 10.6
840
841     keys.set(GHOST_kModifierKeyCommand, (modifiers & NSCommandKeyMask) ? true : false);
842     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & NSAlternateKeyMask) ? true : false);
843     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & NSShiftKeyMask) ? true : false);
844     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & NSControlKeyMask) ? true : false);
845         
846     return GHOST_kSuccess;
847 }
848
849 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
850 {
851         buttons.clear();
852     buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
853         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
854         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
855         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
856         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
857     return GHOST_kSuccess;
858 }
859
860
861
862 #pragma mark Event handlers
863
864 /**
865  * The event queue polling function
866  */
867 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
868 {
869         bool anyProcessed = false;
870         NSEvent *event;
871         
872         //      SetMouseCoalescingEnabled(false, NULL);
873         //TODO : implement timer ??
874         
875         /*do {
876                 GHOST_TimerManager* timerMgr = getTimerManager();
877                 
878                  if (waitForEvent) {
879                  GHOST_TUns64 next = timerMgr->nextFireTime();
880                  double timeOut;
881                  
882                  if (next == GHOST_kFireTimeNever) {
883                  timeOut = kEventDurationForever;
884                  } else {
885                  timeOut = (double)(next - getMilliSeconds())/1000.0;
886                  if (timeOut < 0.0)
887                  timeOut = 0.0;
888                  }
889                  
890                  ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
891                  }
892                  
893                  if (timerMgr->fireTimers(getMilliSeconds())) {
894                  anyProcessed = true;
895                  }*/
896                 
897                 do {
898                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
899                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
900                                                                            untilDate:[NSDate distantPast]
901                                                                                   inMode:NSDefaultRunLoopMode
902                                                                                  dequeue:YES];
903                         if (event==nil) {
904                                 [pool drain];
905                                 break;
906                         }
907                         
908                         anyProcessed = true;
909                         
910                         switch ([event type]) {
911                                 case NSKeyDown:
912                                 case NSKeyUp:
913                                 case NSFlagsChanged:
914                                         handleKeyEvent(event);
915                                         
916                                         /* Support system-wide keyboard shortcuts, like Exposé, ...) =>included in always NSApp sendEvent */
917                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
918                                          [NSApp sendEvent:event];
919                                          }*/
920                                         break;
921                                         
922                                 case NSLeftMouseDown:
923                                 case NSLeftMouseUp:
924                                 case NSRightMouseDown:
925                                 case NSRightMouseUp:
926                                 case NSMouseMoved:
927                                 case NSLeftMouseDragged:
928                                 case NSRightMouseDragged:
929                                 case NSScrollWheel:
930                                 case NSOtherMouseDown:
931                                 case NSOtherMouseUp:
932                                 case NSOtherMouseDragged:                               
933                                         handleMouseEvent(event);
934                                         break;
935                                         
936                                 case NSTabletPoint:
937                                 case NSTabletProximity:
938                                         handleTabletEvent(event,[event type]);
939                                         break;
940                                         
941                                         /* Trackpad features, will need OS X 10.6 for implementation
942                                          case NSEventTypeGesture:
943                                          case NSEventTypeMagnify:
944                                          case NSEventTypeSwipe:
945                                          case NSEventTypeRotate:
946                                          case NSEventTypeBeginGesture:
947                                          case NSEventTypeEndGesture:
948                                          break; */
949                                         
950                                         /*Unused events
951                                          NSMouseEntered       = 8,
952                                          NSMouseExited        = 9,
953                                          NSAppKitDefined      = 13,
954                                          NSSystemDefined      = 14,
955                                          NSApplicationDefined = 15,
956                                          NSPeriodic           = 16,
957                                          NSCursorUpdate       = 17,*/
958                                         
959                                 default:
960                                         break;
961                         }
962                         //Resend event to NSApp to ensure Mac wide events are handled
963                         [NSApp sendEvent:event];
964                         [pool drain];
965                 } while (event!= nil);          
966         //} while (waitForEvent && !anyProcessed); Needed only for timer implementation
967         
968         
969         
970     return anyProcessed;
971 }
972
973 //Note: called from NSWindow delegate
974 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
975 {
976         if (!validWindow(window)) {
977                 return GHOST_kFailure;
978         }
979                 switch(eventType) 
980                 {
981                         case GHOST_kEventWindowClose:
982                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
983                                 break;
984                         case GHOST_kEventWindowActivate:
985                                 m_windowManager->setActiveWindow(window);
986                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
987                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
988                                 break;
989                         case GHOST_kEventWindowDeactivate:
990                                 m_windowManager->setWindowInactive(window);
991                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
992                                 break;
993                         case GHOST_kEventWindowUpdate:
994                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
995                                 break;
996                         case GHOST_kEventWindowSize:
997                                 if (!m_ignoreWindowSizedMessages)
998                                 {
999                                         window->updateDrawingContext();
1000                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
1001                                 }
1002                                 break;
1003                         default:
1004                                 return GHOST_kFailure;
1005                                 break;
1006                 }
1007         return GHOST_kSuccess;
1008 }
1009
1010 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1011 {
1012         GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1013         
1014         //Discard quit event if we are in cursor grab sequence
1015         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1016                 return GHOST_kExitCancel;
1017         
1018         //Check open windows if some changes are not saved
1019         if (m_windowManager->getAnyModifiedState())
1020         {
1021                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved. Do you really want to quit ?",
1022                                                                                  @"Cancel", @"Quit anyway", nil);
1023                 if (shouldQuit == NSAlertAlternateReturn)
1024                 {
1025                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1026                         return GHOST_kExitNow;
1027                 } else {
1028                         //Give back focus to the blender window if user selected cancel quit
1029                         NSArray *windowsList = [NSApp orderedWindows];
1030                         if ([windowsList count]) {
1031                                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1032                         }
1033                 }
1034
1035         }
1036         else {
1037                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1038                 return GHOST_kExitNow;
1039         }
1040         
1041         return GHOST_kExitCancel;
1042 }
1043
1044
1045 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)
1046 {
1047         NSEvent *event = (NSEvent *)eventPtr;
1048         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1049         
1050         if (!window) return GHOST_kFailure;
1051         
1052         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
1053         
1054         switch (eventType) {
1055                 case NSTabletPoint:
1056                         ct.Pressure = [event pressure];
1057                         ct.Xtilt = [event tilt].x;
1058                         ct.Ytilt = [event tilt].y;
1059                         break;
1060                 
1061                 case NSTabletProximity:
1062                         ct.Pressure = 0;
1063                         ct.Xtilt = 0;
1064                         ct.Ytilt = 0;
1065                         if ([event isEnteringProximity])
1066                         {
1067                                 //pointer is entering tablet area proximity
1068                                 switch ([event pointingDeviceType]) {
1069                                         case NSPenPointingDevice:
1070                                                 ct.Active = GHOST_kTabletModeStylus;
1071                                                 break;
1072                                         case NSEraserPointingDevice:
1073                                                 ct.Active = GHOST_kTabletModeEraser;
1074                                                 break;
1075                                         case NSCursorPointingDevice:
1076                                         case NSUnknownPointingDevice:
1077                                         default:
1078                                                 ct.Active = GHOST_kTabletModeNone;
1079                                                 break;
1080                                 }
1081                         } else {
1082                                 // pointer is leaving - return to mouse
1083                                 ct.Active = GHOST_kTabletModeNone;
1084                         }
1085                         break;
1086                 
1087                 default:
1088                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1089                         return GHOST_kFailure;
1090                         break;
1091         }
1092         return GHOST_kSuccess;
1093 }
1094
1095
1096 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1097 {
1098         NSEvent *event = (NSEvent *)eventPtr;
1099     GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1100         
1101         if (!window) {
1102                 return GHOST_kFailure;
1103         }
1104         
1105         switch ([event type])
1106     {
1107                 case NSLeftMouseDown:
1108                 case NSRightMouseDown:
1109                 case NSOtherMouseDown:
1110                         pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1111                         //Handle tablet events combined with mouse events
1112                         switch ([event subtype]) {
1113                                 case NX_SUBTYPE_TABLET_POINT:
1114                                         handleTabletEvent(eventPtr, NSTabletPoint);
1115                                         break;
1116                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1117                                         handleTabletEvent(eventPtr, NSTabletProximity);
1118                                         break;
1119                                 default:
1120                                         //No tablet event included : do nothing
1121                                         break;
1122                         }
1123                         break;
1124                                                 
1125                 case NSLeftMouseUp:
1126                 case NSRightMouseUp:
1127                 case NSOtherMouseUp:
1128                         pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1129                         //Handle tablet events combined with mouse events
1130                         switch ([event subtype]) {
1131                                 case NX_SUBTYPE_TABLET_POINT:
1132                                         handleTabletEvent(eventPtr, NSTabletPoint);
1133                                         break;
1134                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1135                                         handleTabletEvent(eventPtr, NSTabletProximity);
1136                                         break;
1137                                 default:
1138                                         //No tablet event included : do nothing
1139                                         break;
1140                         }
1141                         break;
1142                         
1143                 case NSLeftMouseDragged:
1144                 case NSRightMouseDragged:
1145                 case NSOtherMouseDragged:                               
1146                         //Handle tablet events combined with mouse events
1147                         switch ([event subtype]) {
1148                                 case NX_SUBTYPE_TABLET_POINT:
1149                                         handleTabletEvent(eventPtr, NSTabletPoint);
1150                                         break;
1151                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1152                                         handleTabletEvent(eventPtr, NSTabletProximity);
1153                                         break;
1154                                 default:
1155                                         //No tablet event included : do nothing
1156                                         break;
1157                         }
1158                         
1159                 case NSMouseMoved:
1160                                 switch (window->getCursorGrabMode()) {
1161                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1162                                         {
1163                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
1164                                                 
1165                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1166                                                 
1167                                                 window->getCursorGrabAccum(x_accum, y_accum);
1168                                                 x_accum += [event deltaX];
1169                                                 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1170                                                 window->setCursorGrabAccum(x_accum, y_accum);
1171                                                 
1172                                                 pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
1173                                         }
1174                                                 break;
1175                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1176                                         {
1177                                                 NSPoint mousePos = [event locationInWindow];
1178                                                 GHOST_TInt32 x_mouse= mousePos.x;
1179                                                 GHOST_TInt32 y_mouse= mousePos.y;
1180                                                 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur;
1181                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1182                                                 
1183                                                 /* fallback to window bounds */
1184                                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
1185                                                         window->getClientBounds(bounds);
1186                                                 
1187                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1188                                                 window->getClientBounds(windowBounds);
1189                                                 window->screenToClient(bounds.m_l,bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1190                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1191                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1192                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1193                                                 
1194                                                 //Update accumulation counts
1195                                                 window->getCursorGrabAccum(x_accum, y_accum);
1196                                                 x_accum += [event deltaX]-m_cursorDelta_x;
1197                                                 y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1198                                                 window->setCursorGrabAccum(x_accum, y_accum);
1199                                                 
1200                                                 
1201                                                 //Warp mouse cursor if needed
1202                                                 x_mouse += [event deltaX]-m_cursorDelta_x;
1203                                                 y_mouse += -[event deltaY]-m_cursorDelta_y;
1204                                                 correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
1205                                                 
1206                                                 //Compensate for mouse moved event taking cursor position set into account
1207                                                 m_cursorDelta_x = x_mouse-mousePos.x;
1208                                                 m_cursorDelta_y = y_mouse-mousePos.y;
1209                                                 
1210                                                 //Set new cursor position
1211                                                 window->clientToScreen(x_mouse, y_mouse, x_cur, y_cur);
1212                                                 setCursorPosition(x_cur, y_cur); /* wrap */
1213                                                 
1214                                                 //Post event
1215                                                 window->getCursorGrabInitPos(x_cur, y_cur);
1216                                                 pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, x_cur + x_accum, y_cur + y_accum));
1217                                         }
1218                                                 break;
1219                                         default:
1220                                         {
1221                                                 //Normal cursor operation: send mouse position in window
1222                                                 NSPoint mousePos = [event locationInWindow];
1223                                                 pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1224                                                 m_cursorDelta_x=0;
1225                                                 m_cursorDelta_y=0; //Mouse motion occured between two cursor warps, so we can reset the delta counter
1226                                         }
1227                                                 break;
1228                                 }
1229                                 break;
1230                         
1231                 case NSScrollWheel:
1232                         {
1233                                 GHOST_TInt32 delta;
1234                                 
1235                                 double deltaF = [event deltaY];
1236                                 if (deltaF == 0.0) break; //discard trackpad delta=0 events
1237                                 
1238                                 delta = deltaF > 0.0 ? 1 : -1;
1239                                 pushEvent(new GHOST_EventWheel([event timestamp], window, delta));
1240                         }
1241                         break;
1242                         
1243                 default:
1244                         return GHOST_kFailure;
1245                         break;
1246                 }
1247         
1248         return GHOST_kSuccess;
1249 }
1250
1251
1252 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1253 {
1254         NSEvent *event = (NSEvent *)eventPtr;
1255         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1256         unsigned int modifiers;
1257         NSString *characters;
1258         GHOST_TKey keyCode;
1259         unsigned char ascii;
1260
1261         /* Can happen, very rarely - seems to only be when command-H makes
1262          * the window go away and we still get an HKey up. 
1263          */
1264         if (!window) {
1265                 printf("\nW failure");
1266                 return GHOST_kFailure;
1267         }
1268         
1269         switch ([event type]) {
1270                 case NSKeyDown:
1271                 case NSKeyUp:
1272                         characters = [event characters];
1273                         if ([characters length]) { //Check for dead keys
1274                                 keyCode = convertKey([event keyCode],
1275                                                                          [[event charactersIgnoringModifiers] characterAtIndex:0]);
1276                                 ascii= convertRomanToLatin((char)[characters characterAtIndex:0]);
1277                         } else {
1278                                 keyCode = convertKey([event keyCode],0);
1279                                 ascii= 0;
1280                         }
1281                         
1282                         
1283                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1284                                 break; //Cmd-Q is directly handled by Cocoa
1285
1286                         if ([event type] == NSKeyDown) {
1287                                 pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyDown, window, keyCode, ascii) );
1288                                 //printf("\nKey pressed keyCode=%u ascii=%i %c",keyCode,ascii,ascii);
1289                         } else {
1290                                 pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyUp, window, keyCode, ascii) );
1291                         }
1292                         break;
1293         
1294                 case NSFlagsChanged: 
1295                         modifiers = [event modifierFlags];
1296                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1297                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1298                         }
1299                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1300                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1301                         }
1302                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1303                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1304                         }
1305                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1306                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1307                         }
1308                         
1309                         m_modifierMask = modifiers;
1310                         break;
1311                         
1312                 default:
1313                         return GHOST_kFailure;
1314                         break;
1315         }
1316         
1317         return GHOST_kSuccess;
1318 }
1319
1320
1321
1322 #pragma mark Clipboard get/set
1323
1324 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1325 {
1326         GHOST_TUns8 * temp_buff;
1327         size_t pastedTextSize;  
1328         
1329         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1330         
1331         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1332         
1333         if (pasteBoard == nil) {
1334                 [pool drain];
1335                 return NULL;
1336         }
1337         
1338         NSArray *supportedTypes =
1339                 [NSArray arrayWithObjects: @"public.utf8-plain-text", nil];
1340         
1341         NSString *bestType = [[NSPasteboard generalPasteboard]
1342                                                   availableTypeFromArray:supportedTypes];
1343         
1344         if (bestType == nil) {
1345                 [pool drain];
1346                 return NULL;
1347         }
1348         
1349         NSString * textPasted = [pasteBoard stringForType:@"public.utf8-plain-text"];
1350
1351         if (textPasted == nil) {
1352                 [pool drain];
1353                 return NULL;
1354         }
1355         
1356         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1357         
1358         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1359
1360         if (temp_buff == NULL) {
1361                 [pool drain];
1362                 return NULL;
1363         }
1364         
1365         strncpy((char*)temp_buff, [textPasted UTF8String], pastedTextSize);
1366         
1367         temp_buff[pastedTextSize] = '\0';
1368         
1369         [pool drain];
1370
1371         if(temp_buff) {
1372                 return temp_buff;
1373         } else {
1374                 return NULL;
1375         }
1376 }
1377
1378 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1379 {
1380         NSString *textToCopy;
1381         
1382         if(selection) {return;} // for copying the selection, used on X11
1383
1384         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1385                 
1386         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1387         
1388         if (pasteBoard == nil) {
1389                 [pool drain];
1390                 return;
1391         }
1392         
1393         NSArray *supportedTypes = [NSArray arrayWithObject:@"public.utf8-plain-text"];
1394         
1395         [pasteBoard declareTypes:supportedTypes owner:nil];
1396         
1397         textToCopy = [NSString stringWithUTF8String:buffer];
1398         
1399         [pasteBoard setString:textToCopy forType:@"public.utf8-plain-text"];
1400         
1401         [pool drain];
1402 }