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