Cocoa :
[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 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
530 - (void)applicationWillTerminate:(NSNotification *)aNotification;
531 @end
532
533 @implementation CocoaAppDelegate : NSObject
534 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
535 {
536         systemCocoa = sysCocoa;
537 }
538
539 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
540 {
541         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled
542         //Note that Cmd+Q is already handled by keyhandler
543     if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
544                 return NSTerminateCancel;//NSTerminateNow;
545         else
546                 return NSTerminateCancel;
547 }
548
549 // To avoid cancelling a log off process, we must use Cocoa termination process
550 // And this function is the only chance to perform clean up
551 // So WM_exit needs to be called directly, as the event loop will never run before termination
552 - (void)applicationWillTerminate:(NSNotification *)aNotification
553 {
554         /*G.afbreek = 0; //Let Cocoa perform the termination at the end
555         WM_exit(C);*/
556 }
557 @end
558
559
560
561 #pragma mark initialization/finalization
562
563
564 GHOST_SystemCocoa::GHOST_SystemCocoa()
565 {
566         m_modifierMask =0;
567         m_pressedMouseButtons =0;
568         m_displayManager = new GHOST_DisplayManagerCocoa ();
569         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
570         m_displayManager->initialize();
571
572         //NSEvent timeStamp is given in system uptime, state start date is boot time
573         //FIXME : replace by Cocoa equivalent
574         int mib[2];
575         struct timeval boottime;
576         size_t len;
577         
578         mib[0] = CTL_KERN;
579         mib[1] = KERN_BOOTTIME;
580         len = sizeof(struct timeval);
581         
582         sysctl(mib, 2, &boottime, &len, NULL, 0);
583         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
584         
585         m_ignoreWindowSizedMessages = false;
586 }
587
588 GHOST_SystemCocoa::~GHOST_SystemCocoa()
589 {
590 }
591
592
593 GHOST_TSuccess GHOST_SystemCocoa::init()
594 {
595         
596     GHOST_TSuccess success = GHOST_System::init();
597     if (success) {
598                 //ProcessSerialNumber psn;
599                 
600                 //Carbon stuff to move window & menu to foreground
601                 /*if (!GetCurrentProcess(&psn)) {
602                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
603                         SetFrontProcess(&psn);
604                 }*/
605                 
606                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
607                 if (NSApp == nil) {
608                         [NSApplication sharedApplication];
609                         
610                         if ([NSApp mainMenu] == nil) {
611                                 NSMenu *mainMenubar = [[NSMenu alloc] init];
612                                 NSMenuItem *menuItem;
613                                 NSMenu *windowMenu;
614                                 NSMenu *appMenu;
615                                 
616                                 //Create the application menu
617                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
618                                 
619                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
620                                 [appMenu addItem:[NSMenuItem separatorItem]];
621                                 
622                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
623                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
624                                  
625                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
626                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
627                                 
628                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
629                                 
630                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
631                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
632                                 
633                                 menuItem = [[NSMenuItem alloc] init];
634                                 [menuItem setSubmenu:appMenu];
635                                 
636                                 [mainMenubar addItem:menuItem];
637                                 [menuItem release];
638                                 [appMenu release];
639                                 
640                                 //Create the window menu
641                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
642                                 
643                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
644                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
645                                 
646                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
647                                 
648                                 menuItem = [[NSMenuItem alloc] init];
649                                 [menuItem setSubmenu:windowMenu];
650                                 
651                                 [mainMenubar addItem:menuItem];
652                                 [menuItem release];
653                                 
654                                 [NSApp setMainMenu:mainMenubar];
655                                 [NSApp setWindowsMenu:windowMenu];
656                                 [windowMenu release];
657                         }
658                         [NSApp finishLaunching];
659                 }
660                 if ([NSApp delegate] == nil) {
661                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
662                         [appDelegate setSystemCocoa:this];
663                         [NSApp setDelegate:appDelegate];
664                 }
665                                 
666                 [pool drain];
667     }
668     return success;
669 }
670
671
672 #pragma mark window management
673
674 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
675 {
676         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
677         int mib[2];
678         struct timeval boottime;
679         size_t len;
680         
681         mib[0] = CTL_KERN;
682         mib[1] = KERN_BOOTTIME;
683         len = sizeof(struct timeval);
684         
685         sysctl(mib, 2, &boottime, &len, NULL, 0);
686
687         return ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
688 }
689
690
691 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
692 {
693         //Note that OS X supports monitor hot plug
694         // We do not support multiple monitors at the moment
695         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
696
697         GHOST_TUns8 count = [[NSScreen screens] count];
698
699         [pool drain];
700         return count;
701 }
702
703
704 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
705 {
706         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
707         //Get visible frame, that is frame excluding dock and top menu bar
708         NSRect frame = [[NSScreen mainScreen] visibleFrame];
709         
710         //Returns max window contents (excluding title bar...)
711         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
712                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
713         
714         width = contentRect.size.width;
715         height = contentRect.size.height;
716
717         [pool drain];
718 }
719
720
721 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
722         const STR_String& title, 
723         GHOST_TInt32 left,
724         GHOST_TInt32 top,
725         GHOST_TUns32 width,
726         GHOST_TUns32 height,
727         GHOST_TWindowState state,
728         GHOST_TDrawingContextType type,
729         bool stereoVisual,
730         const GHOST_TEmbedderWindowID parentWindow
731 )
732 {
733     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
734         GHOST_IWindow* window = 0;
735         
736         //First check if we are in fullscreen mode
737         //If so, exit it before creating a new window
738         window = m_windowManager->getActiveWindow();
739         if (window && (window->getState() == GHOST_kWindowStateFullScreen))
740                 window->setState(GHOST_kWindowStateNormal);
741         window = NULL;
742         
743         //Get the available rect for including window contents
744         NSRect frame = [[NSScreen mainScreen] visibleFrame];
745         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
746                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
747         
748         //Ensures window top left is inside this available rect
749         left = left > contentRect.origin.x ? left : contentRect.origin.x;
750         top = top > contentRect.origin.y ? top : contentRect.origin.y;
751         
752         window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type);
753
754     if (window) {
755         if (window->getValid()) {
756             // Store the pointer to the window 
757             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
758             m_windowManager->addWindow(window);
759             m_windowManager->setActiveWindow(window);
760                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
761             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
762                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
763
764         }
765         else {
766                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
767             delete window;
768             window = 0;
769         }
770     }
771         else {
772                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
773         }
774         [pool drain];
775     return window;
776 }
777
778 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
779 {       
780         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
781
782         *window = currentWindow;
783         
784         return currentWindow->setState(GHOST_kWindowStateFullScreen);
785 }
786
787 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
788 {       
789         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
790         
791         return currentWindow->setState(GHOST_kWindowStateNormal);
792 }
793
794
795         
796
797 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
798 {
799     NSPoint mouseLoc = [NSEvent mouseLocation];
800         
801     // Returns the mouse location in screen coordinates
802     x = (GHOST_TInt32)mouseLoc.x;
803     y = (GHOST_TInt32)mouseLoc.y;
804     return GHOST_kSuccess;
805 }
806
807
808 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
809 {
810         float xf=(float)x, yf=(float)y;
811         
812         //Quartz Display Services uses the old coordinates (top left origin)
813         yf = [[NSScreen mainScreen] frame].size.height -yf;
814         
815         CGAssociateMouseAndMouseCursorPosition(false);
816         CGWarpMouseCursorPosition(CGPointMake(xf, yf));
817         CGAssociateMouseAndMouseCursorPosition(true);
818
819     return GHOST_kSuccess;
820 }
821
822
823 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
824 {
825         NSUInteger modifiers = [[NSApp currentEvent] modifierFlags];
826         //Direct query to modifierFlags can be used in 10.6
827
828     keys.set(GHOST_kModifierKeyCommand, (modifiers & NSCommandKeyMask) ? true : false);
829     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & NSAlternateKeyMask) ? true : false);
830     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & NSShiftKeyMask) ? true : false);
831     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & NSControlKeyMask) ? true : false);
832         
833     return GHOST_kSuccess;
834 }
835
836 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
837 {
838         buttons.clear();
839     buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
840         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
841         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
842         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
843         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
844     return GHOST_kSuccess;
845 }
846
847
848
849 #pragma mark Event handlers
850
851 /**
852  * The event queue polling function
853  */
854 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
855 {
856         bool anyProcessed = false;
857         NSEvent *event;
858         
859         //      SetMouseCoalescingEnabled(false, NULL);
860         //TODO : implement timer ??
861         
862         /*do {
863                 GHOST_TimerManager* timerMgr = getTimerManager();
864                 
865                  if (waitForEvent) {
866                  GHOST_TUns64 next = timerMgr->nextFireTime();
867                  double timeOut;
868                  
869                  if (next == GHOST_kFireTimeNever) {
870                  timeOut = kEventDurationForever;
871                  } else {
872                  timeOut = (double)(next - getMilliSeconds())/1000.0;
873                  if (timeOut < 0.0)
874                  timeOut = 0.0;
875                  }
876                  
877                  ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
878                  }
879                  
880                  if (timerMgr->fireTimers(getMilliSeconds())) {
881                  anyProcessed = true;
882                  }
883                  
884                          if (getFullScreen()) {
885                  // Check if the full-screen window is dirty
886                  GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
887                  if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
888                  pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
889                  anyProcessed = true;
890                  }
891                  }*/
892                 
893                 do {
894                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
895                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
896                                                                            untilDate:[NSDate distantPast]
897                                                                                   inMode:NSDefaultRunLoopMode
898                                                                                  dequeue:YES];
899                         if (event==nil) {
900                                 [pool drain];
901                                 break;
902                         }
903                         
904                         anyProcessed = true;
905                         
906                         switch ([event type]) {
907                                 case NSKeyDown:
908                                 case NSKeyUp:
909                                 case NSFlagsChanged:
910                                         handleKeyEvent(event);
911                                         
912                                         /* Support system-wide keyboard shortcuts, like Exposé, ...) =>included in always NSApp sendEvent */
913                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
914                                          [NSApp sendEvent:event];
915                                          }*/
916                                         break;
917                                         
918                                 case NSLeftMouseDown:
919                                 case NSLeftMouseUp:
920                                 case NSRightMouseDown:
921                                 case NSRightMouseUp:
922                                 case NSMouseMoved:
923                                 case NSLeftMouseDragged:
924                                 case NSRightMouseDragged:
925                                 case NSScrollWheel:
926                                 case NSOtherMouseDown:
927                                 case NSOtherMouseUp:
928                                 case NSOtherMouseDragged:                               
929                                         handleMouseEvent(event);
930                                         break;
931                                         
932                                 case NSTabletPoint:
933                                 case NSTabletProximity:
934                                         handleTabletEvent(event,[event type]);
935                                         break;
936                                         
937                                         /* Trackpad features, will need OS X 10.6 for implementation
938                                          case NSEventTypeGesture:
939                                          case NSEventTypeMagnify:
940                                          case NSEventTypeSwipe:
941                                          case NSEventTypeRotate:
942                                          case NSEventTypeBeginGesture:
943                                          case NSEventTypeEndGesture:
944                                          break; */
945                                         
946                                         /*Unused events
947                                          NSMouseEntered       = 8,
948                                          NSMouseExited        = 9,
949                                          NSAppKitDefined      = 13,
950                                          NSSystemDefined      = 14,
951                                          NSApplicationDefined = 15,
952                                          NSPeriodic           = 16,
953                                          NSCursorUpdate       = 17,*/
954                                         
955                                 default:
956                                         break;
957                         }
958                         //Resend event to NSApp to ensure Mac wide events are handled
959                         [NSApp sendEvent:event];
960                         [pool drain];
961                 } while (event!= nil);          
962         //} while (waitForEvent && !anyProcessed); Needed only for timer implementation
963         
964         
965         
966     return anyProcessed;
967 }
968
969 //Note: called from NSWindow delegate
970 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
971 {
972         if (!validWindow(window)) {
973                 return GHOST_kFailure;
974         }
975                 switch(eventType) 
976                 {
977                         case GHOST_kEventWindowClose:
978                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
979                                 break;
980                         case GHOST_kEventWindowActivate:
981                                 m_windowManager->setActiveWindow(window);
982                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
983                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
984                                 break;
985                         case GHOST_kEventWindowDeactivate:
986                                 m_windowManager->setWindowInactive(window);
987                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
988                                 break;
989                         case GHOST_kEventWindowUpdate:
990                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
991                                 break;
992                         case GHOST_kEventWindowSize:
993                                 if (!m_ignoreWindowSizedMessages)
994                                 {
995                                         window->updateDrawingContext();
996                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
997                                 }
998                                 break;
999                         default:
1000                                 return GHOST_kFailure;
1001                                 break;
1002                 }
1003         return GHOST_kSuccess;
1004 }
1005
1006 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1007 {
1008         //Check open windows if some changes are not saved
1009         if (m_windowManager->getAnyModifiedState())
1010         {
1011                 //First check if we are in fullscreen mode
1012                 //If so, exit it before creating a new window
1013                 GHOST_IWindow *window = m_windowManager->getActiveWindow();
1014                 if (window && (window->getState() == GHOST_kWindowStateFullScreen))
1015                         window->setState(GHOST_kWindowStateNormal);
1016                 window = NULL;          
1017                 
1018                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved. Do you really want to quit ?",
1019                                                                                  @"Cancel", @"Quit anyway", nil);
1020                 if (shouldQuit == NSAlertAlternateReturn)
1021                 {
1022                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1023                         return GHOST_kExitNow;
1024                 }
1025         }
1026         else {
1027                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1028                 return GHOST_kExitNow;
1029         }
1030         
1031         return GHOST_kExitCancel;
1032 }
1033
1034
1035 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)
1036 {
1037         NSEvent *event = (NSEvent *)eventPtr;
1038         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1039         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
1040         
1041         switch (eventType) {
1042                 case NSTabletPoint:
1043                         ct.Pressure = [event tangentialPressure];
1044                         ct.Xtilt = [event tilt].x;
1045                         ct.Ytilt = [event tilt].y;
1046                         break;
1047                 
1048                 case NSTabletProximity:
1049                         ct.Pressure = 0;
1050                         ct.Xtilt = 0;
1051                         ct.Ytilt = 0;
1052                         if ([event isEnteringProximity])
1053                         {
1054                                 //pointer is entering tablet area proximity
1055                                 switch ([event pointingDeviceType]) {
1056                                         case NSPenPointingDevice:
1057                                                 ct.Active = GHOST_kTabletModeStylus;
1058                                                 break;
1059                                         case NSEraserPointingDevice:
1060                                                 ct.Active = GHOST_kTabletModeEraser;
1061                                                 break;
1062                                         case NSCursorPointingDevice:
1063                                         case NSUnknownPointingDevice:
1064                                         default:
1065                                                 ct.Active = GHOST_kTabletModeNone;
1066                                                 break;
1067                                 }
1068                         } else {
1069                                 // pointer is leaving - return to mouse
1070                                 ct.Active = GHOST_kTabletModeNone;
1071                         }
1072                         break;
1073                 
1074                 default:
1075                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1076                         return GHOST_kFailure;
1077                         break;
1078         }
1079         return GHOST_kSuccess;
1080 }
1081
1082
1083 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1084 {
1085         NSEvent *event = (NSEvent *)eventPtr;
1086     GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1087         
1088         if (!window) {
1089                 return GHOST_kFailure;
1090         }
1091         
1092         switch ([event type])
1093     {
1094                 case NSLeftMouseDown:
1095                 case NSRightMouseDown:
1096                 case NSOtherMouseDown:
1097                         pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1098                         //Handle tablet events combined with mouse events
1099                         switch ([event subtype]) {
1100                                 case NX_SUBTYPE_TABLET_POINT:
1101                                         handleTabletEvent(eventPtr, NSTabletPoint);
1102                                         break;
1103                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1104                                         handleTabletEvent(eventPtr, NSTabletProximity);
1105                                         break;
1106                                 default:
1107                                         //No tablet event included : do nothing
1108                                         break;
1109                         }
1110                         break;
1111                                                 
1112                 case NSLeftMouseUp:
1113                 case NSRightMouseUp:
1114                 case NSOtherMouseUp:
1115                         pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1116                         //Handle tablet events combined with mouse events
1117                         switch ([event subtype]) {
1118                                 case NX_SUBTYPE_TABLET_POINT:
1119                                         handleTabletEvent(eventPtr, NSTabletPoint);
1120                                         break;
1121                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1122                                         handleTabletEvent(eventPtr, NSTabletProximity);
1123                                         break;
1124                                 default:
1125                                         //No tablet event included : do nothing
1126                                         break;
1127                         }
1128                         break;
1129                         
1130                 case NSLeftMouseDragged:
1131                 case NSRightMouseDragged:
1132                 case NSOtherMouseDragged:                               
1133                         //Handle tablet events combined with mouse events
1134                         switch ([event subtype]) {
1135                                 case NX_SUBTYPE_TABLET_POINT:
1136                                         handleTabletEvent(eventPtr, NSTabletPoint);
1137                                         break;
1138                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1139                                         handleTabletEvent(eventPtr, NSTabletProximity);
1140                                         break;
1141                                 default:
1142                                         //No tablet event included : do nothing
1143                                         break;
1144                         }
1145                 case NSMouseMoved:
1146                         {
1147                                 if(window->getCursorWarp()) {
1148                                         GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
1149                                         
1150                                         window->getCursorWarpPos(x_warp, y_warp);
1151                                         
1152                                         window->getCursorWarpAccum(x_accum, y_accum);
1153                                         x_accum += [event deltaX];
1154                                         y_accum += [event deltaY];
1155                                         window->setCursorWarpAccum(x_accum, y_accum);
1156                                         
1157                                         pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
1158                                 } 
1159                                 else { //Normal cursor operation: send mouse position in window
1160                                         NSPoint mousePos = [event locationInWindow];
1161                                         pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1162                                 }
1163                                 break;
1164                         }
1165                         
1166                 case NSScrollWheel:
1167                         {
1168                                 GHOST_TInt32 delta;
1169                                 delta = [event deltaY] > 0 ? 1 : -1;
1170                                 pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
1171
1172                         }
1173                         break;
1174                         
1175                 default:
1176                         return GHOST_kFailure;
1177                         break;
1178                 }
1179         
1180         return GHOST_kSuccess;
1181 }
1182
1183
1184 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1185 {
1186         NSEvent *event = (NSEvent *)eventPtr;
1187         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1188         NSUInteger modifiers;
1189         NSString *characters;
1190         GHOST_TKey keyCode;
1191         unsigned char ascii;
1192
1193         /* Can happen, very rarely - seems to only be when command-H makes
1194          * the window go away and we still get an HKey up. 
1195          */
1196         if (!window) {
1197                 printf("\nW failure");
1198                 return GHOST_kFailure;
1199         }
1200         
1201         switch ([event type]) {
1202                 case NSKeyDown:
1203                 case NSKeyUp:
1204                         characters = [event characters];
1205                         if ([characters length]) { //Check for dead keys
1206                                 keyCode = convertKey([event keyCode],
1207                                                                          [[event charactersIgnoringModifiers] characterAtIndex:0]);
1208                                 ascii= convertRomanToLatin((char)[characters characterAtIndex:0]);
1209                         } else {
1210                                 keyCode = convertKey([event keyCode],0);
1211                                 ascii= 0;
1212                         }
1213                         
1214                         
1215                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1216                                 break; //Cmd-Q is directly handled by Cocoa
1217
1218                         if ([event type] == NSKeyDown) {
1219                                 pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyDown, window, keyCode, ascii) );
1220                                 //printf("\nKey pressed keyCode=%u ascii=%i %c",keyCode,ascii,ascii);
1221                         } else {
1222                                 pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyUp, window, keyCode, ascii) );
1223                         }
1224                         break;
1225         
1226                 case NSFlagsChanged: 
1227                         modifiers = [event modifierFlags];
1228                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1229                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1230                         }
1231                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1232                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1233                         }
1234                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1235                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1236                         }
1237                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1238                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1239                         }
1240                         
1241                         m_modifierMask = modifiers;
1242                         break;
1243                         
1244                 default:
1245                         return GHOST_kFailure;
1246                         break;
1247         }
1248         
1249         return GHOST_kSuccess;
1250 }
1251
1252
1253
1254 #pragma mark Clipboard get/set
1255
1256 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1257 {
1258         GHOST_TUns8 * temp_buff;
1259         size_t pastedTextSize;  
1260         
1261         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1262         
1263         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1264         
1265         if (pasteBoard == nil) {
1266                 [pool drain];
1267                 return NULL;
1268         }
1269         
1270         NSArray *supportedTypes =
1271                 [NSArray arrayWithObjects: @"public.utf8-plain-text", nil];
1272         
1273         NSString *bestType = [[NSPasteboard generalPasteboard]
1274                                                   availableTypeFromArray:supportedTypes];
1275         
1276         if (bestType == nil) {
1277                 [pool drain];
1278                 return NULL;
1279         }
1280         
1281         NSString * textPasted = [pasteBoard stringForType:@"public.utf8-plain-text"];
1282
1283         if (textPasted == nil) {
1284                 [pool drain];
1285                 return NULL;
1286         }
1287         
1288         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1289         
1290         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1291
1292         if (temp_buff == NULL) {
1293                 [pool drain];
1294                 return NULL;
1295         }
1296         
1297         strncpy((char*)temp_buff, [textPasted UTF8String], pastedTextSize);
1298         
1299         temp_buff[pastedTextSize] = '\0';
1300         
1301         [pool drain];
1302
1303         if(temp_buff) {
1304                 return temp_buff;
1305         } else {
1306                 return NULL;
1307         }
1308 }
1309
1310 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1311 {
1312         NSString *textToCopy;
1313         
1314         if(selection) {return;} // for copying the selection, used on X11
1315
1316         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1317                 
1318         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1319         
1320         if (pasteBoard == nil) {
1321                 [pool drain];
1322                 return;
1323         }
1324         
1325         NSArray *supportedTypes = [NSArray arrayWithObject:@"public.utf8-plain-text"];
1326         
1327         [pasteBoard declareTypes:supportedTypes owner:nil];
1328         
1329         textToCopy = [NSString stringWithUTF8String:buffer];
1330         
1331         [pasteBoard setString:textToCopy forType:@"public.utf8-plain-text"];
1332         
1333         [pool drain];
1334 }
1335
1336 #pragma mark Carbon stuff to remove
1337
1338 #ifdef WITH_CARBON
1339
1340
1341 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1342 {
1343         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1344         
1345         return noErr;
1346 }
1347
1348 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1349 {
1350         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1351         AEDescList docs;
1352         SInt32 ndocs;
1353         OSErr err;
1354         
1355         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
1356         if (err != noErr)  return err;
1357         
1358         err = AECountItems(&docs, &ndocs);
1359         if (err==noErr) {
1360                 int i;
1361                 
1362                 for (i=0; i<ndocs; i++) {
1363                         FSSpec fss;
1364                         AEKeyword kwd;
1365                         DescType actType;
1366                         Size actSize;
1367                         
1368                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
1369                         if (err!=noErr)
1370                                 break;
1371                         
1372                         if (i==0) {
1373                                 FSRef fsref;
1374                                 
1375                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
1376                                         break;
1377                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
1378                                         break;
1379                                 
1380                                 g_hasFirstFile = true;
1381                         }
1382                 }
1383         }
1384         
1385         AEDisposeDesc(&docs);
1386         
1387         return err;
1388 }
1389
1390 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1391 {
1392         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1393         
1394         return noErr;
1395 }
1396
1397 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1398 {
1399         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1400         
1401         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
1402         
1403         return noErr;
1404 }
1405 #endif