7f98f67a1ba597ba6af851ee7571729fd2c7f8f1
[blender.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 //TODO: remove (kept as reminder to implement window events)
55 /*
56 const EventTypeSpec     kEvents[] =
57 {
58         { kEventClassAppleEvent, kEventAppleEvent },
59
60 //      { kEventClassApplication, kEventAppActivated },
61 //      { kEventClassApplication, kEventAppDeactivated },
62         
63         { kEventClassKeyboard, kEventRawKeyDown },
64         { kEventClassKeyboard, kEventRawKeyRepeat },
65         { kEventClassKeyboard, kEventRawKeyUp },
66         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
67         
68         { kEventClassMouse, kEventMouseDown },
69         { kEventClassMouse, kEventMouseUp },
70         { kEventClassMouse, kEventMouseMoved },
71         { kEventClassMouse, kEventMouseDragged },
72         { kEventClassMouse, kEventMouseWheelMoved },
73         
74         { kEventClassWindow, kEventWindowClickZoomRgn } ,  // for new zoom behaviour  
75         { kEventClassWindow, kEventWindowZoom },  // for new zoom behaviour  
76         { kEventClassWindow, kEventWindowExpand } ,  // for new zoom behaviour 
77         { kEventClassWindow, kEventWindowExpandAll },  // for new zoom behaviour 
78
79         { kEventClassWindow, kEventWindowClose },
80         { kEventClassWindow, kEventWindowActivated },
81         { kEventClassWindow, kEventWindowDeactivated },
82         { kEventClassWindow, kEventWindowUpdate },
83         { kEventClassWindow, kEventWindowBoundsChanged },
84         
85         { kEventClassBlender, kEventBlenderNdofAxis },
86         { kEventClassBlender, kEventBlenderNdofButtons }
87         
88         
89         
90 };*/
91
92 /* Keycodes from Carbon include file */
93 /*  
94  *  Summary:
95  *    Virtual keycodes
96  *  
97  *  Discussion:
98  *    These constants are the virtual keycodes defined originally in
99  *    Inside Mac Volume V, pg. V-191. They identify physical keys on a
100  *    keyboard. Those constants with "ANSI" in the name are labeled
101  *    according to the key position on an ANSI-standard US keyboard.
102  *    For example, kVK_ANSI_A indicates the virtual keycode for the key
103  *    with the letter 'A' in the US keyboard layout. Other keyboard
104  *    layouts may have the 'A' key label on a different physical key;
105  *    in this case, pressing 'A' will generate a different virtual
106  *    keycode.
107  */
108 enum {
109         kVK_ANSI_A                    = 0x00,
110         kVK_ANSI_S                    = 0x01,
111         kVK_ANSI_D                    = 0x02,
112         kVK_ANSI_F                    = 0x03,
113         kVK_ANSI_H                    = 0x04,
114         kVK_ANSI_G                    = 0x05,
115         kVK_ANSI_Z                    = 0x06,
116         kVK_ANSI_X                    = 0x07,
117         kVK_ANSI_C                    = 0x08,
118         kVK_ANSI_V                    = 0x09,
119         kVK_ANSI_B                    = 0x0B,
120         kVK_ANSI_Q                    = 0x0C,
121         kVK_ANSI_W                    = 0x0D,
122         kVK_ANSI_E                    = 0x0E,
123         kVK_ANSI_R                    = 0x0F,
124         kVK_ANSI_Y                    = 0x10,
125         kVK_ANSI_T                    = 0x11,
126         kVK_ANSI_1                    = 0x12,
127         kVK_ANSI_2                    = 0x13,
128         kVK_ANSI_3                    = 0x14,
129         kVK_ANSI_4                    = 0x15,
130         kVK_ANSI_6                    = 0x16,
131         kVK_ANSI_5                    = 0x17,
132         kVK_ANSI_Equal                = 0x18,
133         kVK_ANSI_9                    = 0x19,
134         kVK_ANSI_7                    = 0x1A,
135         kVK_ANSI_Minus                = 0x1B,
136         kVK_ANSI_8                    = 0x1C,
137         kVK_ANSI_0                    = 0x1D,
138         kVK_ANSI_RightBracket         = 0x1E,
139         kVK_ANSI_O                    = 0x1F,
140         kVK_ANSI_U                    = 0x20,
141         kVK_ANSI_LeftBracket          = 0x21,
142         kVK_ANSI_I                    = 0x22,
143         kVK_ANSI_P                    = 0x23,
144         kVK_ANSI_L                    = 0x25,
145         kVK_ANSI_J                    = 0x26,
146         kVK_ANSI_Quote                = 0x27,
147         kVK_ANSI_K                    = 0x28,
148         kVK_ANSI_Semicolon            = 0x29,
149         kVK_ANSI_Backslash            = 0x2A,
150         kVK_ANSI_Comma                = 0x2B,
151         kVK_ANSI_Slash                = 0x2C,
152         kVK_ANSI_N                    = 0x2D,
153         kVK_ANSI_M                    = 0x2E,
154         kVK_ANSI_Period               = 0x2F,
155         kVK_ANSI_Grave                = 0x32,
156         kVK_ANSI_KeypadDecimal        = 0x41,
157         kVK_ANSI_KeypadMultiply       = 0x43,
158         kVK_ANSI_KeypadPlus           = 0x45,
159         kVK_ANSI_KeypadClear          = 0x47,
160         kVK_ANSI_KeypadDivide         = 0x4B,
161         kVK_ANSI_KeypadEnter          = 0x4C,
162         kVK_ANSI_KeypadMinus          = 0x4E,
163         kVK_ANSI_KeypadEquals         = 0x51,
164         kVK_ANSI_Keypad0              = 0x52,
165         kVK_ANSI_Keypad1              = 0x53,
166         kVK_ANSI_Keypad2              = 0x54,
167         kVK_ANSI_Keypad3              = 0x55,
168         kVK_ANSI_Keypad4              = 0x56,
169         kVK_ANSI_Keypad5              = 0x57,
170         kVK_ANSI_Keypad6              = 0x58,
171         kVK_ANSI_Keypad7              = 0x59,
172         kVK_ANSI_Keypad8              = 0x5B,
173         kVK_ANSI_Keypad9              = 0x5C
174 };
175
176 /* keycodes for keys that are independent of keyboard layout*/
177 enum {
178         kVK_Return                    = 0x24,
179         kVK_Tab                       = 0x30,
180         kVK_Space                     = 0x31,
181         kVK_Delete                    = 0x33,
182         kVK_Escape                    = 0x35,
183         kVK_Command                   = 0x37,
184         kVK_Shift                     = 0x38,
185         kVK_CapsLock                  = 0x39,
186         kVK_Option                    = 0x3A,
187         kVK_Control                   = 0x3B,
188         kVK_RightShift                = 0x3C,
189         kVK_RightOption               = 0x3D,
190         kVK_RightControl              = 0x3E,
191         kVK_Function                  = 0x3F,
192         kVK_F17                       = 0x40,
193         kVK_VolumeUp                  = 0x48,
194         kVK_VolumeDown                = 0x49,
195         kVK_Mute                      = 0x4A,
196         kVK_F18                       = 0x4F,
197         kVK_F19                       = 0x50,
198         kVK_F20                       = 0x5A,
199         kVK_F5                        = 0x60,
200         kVK_F6                        = 0x61,
201         kVK_F7                        = 0x62,
202         kVK_F3                        = 0x63,
203         kVK_F8                        = 0x64,
204         kVK_F9                        = 0x65,
205         kVK_F11                       = 0x67,
206         kVK_F13                       = 0x69,
207         kVK_F16                       = 0x6A,
208         kVK_F14                       = 0x6B,
209         kVK_F10                       = 0x6D,
210         kVK_F12                       = 0x6F,
211         kVK_F15                       = 0x71,
212         kVK_Help                      = 0x72,
213         kVK_Home                      = 0x73,
214         kVK_PageUp                    = 0x74,
215         kVK_ForwardDelete             = 0x75,
216         kVK_F4                        = 0x76,
217         kVK_End                       = 0x77,
218         kVK_F2                        = 0x78,
219         kVK_PageDown                  = 0x79,
220         kVK_F1                        = 0x7A,
221         kVK_LeftArrow                 = 0x7B,
222         kVK_RightArrow                = 0x7C,
223         kVK_DownArrow                 = 0x7D,
224         kVK_UpArrow                   = 0x7E
225 };
226
227 /* ISO keyboards only*/
228 enum {
229         kVK_ISO_Section               = 0x0A
230 };
231
232 /* JIS keyboards only*/
233 enum {
234         kVK_JIS_Yen                   = 0x5D,
235         kVK_JIS_Underscore            = 0x5E,
236         kVK_JIS_KeypadComma           = 0x5F,
237         kVK_JIS_Eisu                  = 0x66,
238         kVK_JIS_Kana                  = 0x68
239 };
240
241
242 static GHOST_TButtonMask convertButton(int button)
243 {
244         switch (button) {
245                 case 0:
246                         return GHOST_kButtonMaskLeft;
247                 case 1:
248                         return GHOST_kButtonMaskRight;
249                 case 2:
250                         return GHOST_kButtonMaskMiddle;
251                 case 3:
252                         return GHOST_kButtonMaskButton4;
253                 case 4:
254                         return GHOST_kButtonMaskButton5;
255                 default:
256                         return GHOST_kButtonMaskLeft;
257         }
258 }
259
260 /**
261  * Converts Mac rawkey codes (same for Cocoa & Carbon)
262  * into GHOST key codes
263  * @param rawCode The raw physical key code
264  * @param recvChar the character ignoring modifiers (except for shift)
265  * @return Ghost key code
266  */
267 static GHOST_TKey convertKey(int rawCode, unichar recvChar) 
268 {       
269         
270         //printf("\nrecvchar %c 0x%x",recvChar,recvChar);
271         switch (rawCode) {
272                 /*Physical keycodes not used due to map changes in int'l keyboards
273                 case kVK_ANSI_A:        return GHOST_kKeyA;
274                 case kVK_ANSI_B:        return GHOST_kKeyB;
275                 case kVK_ANSI_C:        return GHOST_kKeyC;
276                 case kVK_ANSI_D:        return GHOST_kKeyD;
277                 case kVK_ANSI_E:        return GHOST_kKeyE;
278                 case kVK_ANSI_F:        return GHOST_kKeyF;
279                 case kVK_ANSI_G:        return GHOST_kKeyG;
280                 case kVK_ANSI_H:        return GHOST_kKeyH;
281                 case kVK_ANSI_I:        return GHOST_kKeyI;
282                 case kVK_ANSI_J:        return GHOST_kKeyJ;
283                 case kVK_ANSI_K:        return GHOST_kKeyK;
284                 case kVK_ANSI_L:        return GHOST_kKeyL;
285                 case kVK_ANSI_M:        return GHOST_kKeyM;
286                 case kVK_ANSI_N:        return GHOST_kKeyN;
287                 case kVK_ANSI_O:        return GHOST_kKeyO;
288                 case kVK_ANSI_P:        return GHOST_kKeyP;
289                 case kVK_ANSI_Q:        return GHOST_kKeyQ;
290                 case kVK_ANSI_R:        return GHOST_kKeyR;
291                 case kVK_ANSI_S:        return GHOST_kKeyS;
292                 case kVK_ANSI_T:        return GHOST_kKeyT;
293                 case kVK_ANSI_U:        return GHOST_kKeyU;
294                 case kVK_ANSI_V:        return GHOST_kKeyV;
295                 case kVK_ANSI_W:        return GHOST_kKeyW;
296                 case kVK_ANSI_X:        return GHOST_kKeyX;
297                 case kVK_ANSI_Y:        return GHOST_kKeyY;
298                 case kVK_ANSI_Z:        return GHOST_kKeyZ;*/
299                 
300                 /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/
301                 case kVK_ISO_Section: return    GHOST_kKeyUnknown;
302                 case kVK_ANSI_1:        return GHOST_kKey1;
303                 case kVK_ANSI_2:        return GHOST_kKey2;
304                 case kVK_ANSI_3:        return GHOST_kKey3;
305                 case kVK_ANSI_4:        return GHOST_kKey4;
306                 case kVK_ANSI_5:        return GHOST_kKey5;
307                 case kVK_ANSI_6:        return GHOST_kKey6;
308                 case kVK_ANSI_7:        return GHOST_kKey7;
309                 case kVK_ANSI_8:        return GHOST_kKey8;
310                 case kVK_ANSI_9:        return GHOST_kKey9;
311                 case kVK_ANSI_0:        return GHOST_kKey0;
312         
313                 case kVK_ANSI_Keypad0:                  return GHOST_kKeyNumpad0;
314                 case kVK_ANSI_Keypad1:                  return GHOST_kKeyNumpad1;
315                 case kVK_ANSI_Keypad2:                  return GHOST_kKeyNumpad2;
316                 case kVK_ANSI_Keypad3:                  return GHOST_kKeyNumpad3;
317                 case kVK_ANSI_Keypad4:                  return GHOST_kKeyNumpad4;
318                 case kVK_ANSI_Keypad5:                  return GHOST_kKeyNumpad5;
319                 case kVK_ANSI_Keypad6:                  return GHOST_kKeyNumpad6;
320                 case kVK_ANSI_Keypad7:                  return GHOST_kKeyNumpad7;
321                 case kVK_ANSI_Keypad8:                  return GHOST_kKeyNumpad8;
322                 case kVK_ANSI_Keypad9:                  return GHOST_kKeyNumpad9;
323                 case kVK_ANSI_KeypadDecimal:    return GHOST_kKeyNumpadPeriod;
324                 case kVK_ANSI_KeypadEnter:              return GHOST_kKeyNumpadEnter;
325                 case kVK_ANSI_KeypadPlus:               return GHOST_kKeyNumpadPlus;
326                 case kVK_ANSI_KeypadMinus:              return GHOST_kKeyNumpadMinus;
327                 case kVK_ANSI_KeypadMultiply:   return GHOST_kKeyNumpadAsterisk;
328                 case kVK_ANSI_KeypadDivide:     return GHOST_kKeyNumpadSlash;
329                 case kVK_ANSI_KeypadClear:              return GHOST_kKeyUnknown;
330
331                 case kVK_F1:                            return GHOST_kKeyF1;
332                 case kVK_F2:                            return GHOST_kKeyF2;
333                 case kVK_F3:                            return GHOST_kKeyF3;
334                 case kVK_F4:                            return GHOST_kKeyF4;
335                 case kVK_F5:                            return GHOST_kKeyF5;
336                 case kVK_F6:                            return GHOST_kKeyF6;
337                 case kVK_F7:                            return GHOST_kKeyF7;
338                 case kVK_F8:                            return GHOST_kKeyF8;
339                 case kVK_F9:                            return GHOST_kKeyF9;
340                 case kVK_F10:                           return GHOST_kKeyF10;
341                 case kVK_F11:                           return GHOST_kKeyF11;
342                 case kVK_F12:                           return GHOST_kKeyF12;
343                 case kVK_F13:                           return GHOST_kKeyF13;
344                 case kVK_F14:                           return GHOST_kKeyF14;
345                 case kVK_F15:                           return GHOST_kKeyF15;
346                 case kVK_F16:                           return GHOST_kKeyF16;
347                 case kVK_F17:                           return GHOST_kKeyF17;
348                 case kVK_F18:                           return GHOST_kKeyF18;
349                 case kVK_F19:                           return GHOST_kKeyF19;
350                 case kVK_F20:                           return GHOST_kKeyF20;
351                         
352                 case kVK_UpArrow:                       return GHOST_kKeyUpArrow;
353                 case kVK_DownArrow:                     return GHOST_kKeyDownArrow;
354                 case kVK_LeftArrow:                     return GHOST_kKeyLeftArrow;
355                 case kVK_RightArrow:            return GHOST_kKeyRightArrow;
356                         
357                 case kVK_Return:                        return GHOST_kKeyEnter;
358                 case kVK_Delete:                        return GHOST_kKeyBackSpace;
359                 case kVK_ForwardDelete:         return GHOST_kKeyDelete;
360                 case kVK_Escape:                        return GHOST_kKeyEsc;
361                 case kVK_Tab:                           return GHOST_kKeyTab;
362                 case kVK_Space:                         return GHOST_kKeySpace;
363                         
364                 case kVK_Home:                          return GHOST_kKeyHome;
365                 case kVK_End:                           return GHOST_kKeyEnd;
366                 case kVK_PageUp:                        return GHOST_kKeyUpPage;
367                 case kVK_PageDown:                      return GHOST_kKeyDownPage;
368                         
369                 /*case kVK_ANSI_Minus:          return GHOST_kKeyMinus;
370                 case kVK_ANSI_Equal:            return GHOST_kKeyEqual;
371                 case kVK_ANSI_Comma:            return GHOST_kKeyComma;
372                 case kVK_ANSI_Period:           return GHOST_kKeyPeriod;
373                 case kVK_ANSI_Slash:            return GHOST_kKeySlash;
374                 case kVK_ANSI_Semicolon:        return GHOST_kKeySemicolon;
375                 case kVK_ANSI_Quote:            return GHOST_kKeyQuote;
376                 case kVK_ANSI_Backslash:        return GHOST_kKeyBackslash;
377                 case kVK_ANSI_LeftBracket:      return GHOST_kKeyLeftBracket;
378                 case kVK_ANSI_RightBracket:     return GHOST_kKeyRightBracket;
379                 case kVK_ANSI_Grave:            return GHOST_kKeyAccentGrave;*/
380                         
381                 case kVK_VolumeUp:
382                 case kVK_VolumeDown:
383                 case kVK_Mute:
384                         return GHOST_kKeyUnknown;
385                         
386                 default:
387                         /*Then detect on character value for "remappable" keys in int'l keyboards*/
388                         if ((recvChar >= 'A') && (recvChar <= 'Z')) {
389                                 return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA);
390                         } else if ((recvChar >= 'a') && (recvChar <= 'z')) {
391                                 return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA);
392                         } else
393                         switch (recvChar) {
394                                 case '-':       return GHOST_kKeyMinus;
395                                 case '=':       return GHOST_kKeyEqual;
396                                 case ',':       return GHOST_kKeyComma;
397                                 case '.':       return GHOST_kKeyPeriod;
398                                 case '/':       return GHOST_kKeySlash;
399                                 case ';':       return GHOST_kKeySemicolon;
400                                 case '\'':      return GHOST_kKeyQuote;
401                                 case '\\':      return GHOST_kKeyBackslash;
402                                 case '[':       return GHOST_kKeyLeftBracket;
403                                 case ']':       return GHOST_kKeyRightBracket;
404                                 case '`':       return GHOST_kKeyAccentGrave;
405                                 default:
406                                         return GHOST_kKeyUnknown;
407                         }
408         }
409         return GHOST_kKeyUnknown;
410 }
411
412 /* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
413  * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
414  * I am not sure how international this works...
415  * For cross-platform convention, we'll use the Latin ascii set instead.
416  * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
417  * 
418  */
419 static unsigned char convertRomanToLatin(unsigned char ascii)
420 {
421
422         if(ascii<128) return ascii;
423         
424         switch(ascii) {
425         case 128:       return 142;
426         case 129:       return 143;
427         case 130:       return 128;
428         case 131:       return 201;
429         case 132:       return 209;
430         case 133:       return 214;
431         case 134:       return 220;
432         case 135:       return 225;
433         case 136:       return 224;
434         case 137:       return 226;
435         case 138:       return 228;
436         case 139:       return 227;
437         case 140:       return 229;
438         case 141:       return 231;
439         case 142:       return 233;
440         case 143:       return 232;
441         case 144:       return 234;
442         case 145:       return 235;
443         case 146:       return 237;
444         case 147:       return 236;
445         case 148:       return 238;
446         case 149:       return 239;
447         case 150:       return 241;
448         case 151:       return 243;
449         case 152:       return 242;
450         case 153:       return 244;
451         case 154:       return 246;
452         case 155:       return 245;
453         case 156:       return 250;
454         case 157:       return 249;
455         case 158:       return 251;
456         case 159:       return 252;
457         case 160:       return 0;
458         case 161:       return 176;
459         case 162:       return 162;
460         case 163:       return 163;
461         case 164:       return 167;
462         case 165:       return 183;
463         case 166:       return 182;
464         case 167:       return 223;
465         case 168:       return 174;
466         case 169:       return 169;
467         case 170:       return 174;
468         case 171:       return 180;
469         case 172:       return 168;
470         case 173:       return 0;
471         case 174:       return 198;
472         case 175:       return 216;
473         case 176:       return 0;
474         case 177:       return 177;
475         case 178:       return 0;
476         case 179:       return 0;
477         case 180:       return 165;
478         case 181:       return 181;
479         case 182:       return 0;
480         case 183:       return 0;
481         case 184:       return 215;
482         case 185:       return 0;
483         case 186:       return 0;
484         case 187:       return 170;
485         case 188:       return 186;
486         case 189:       return 0;
487         case 190:       return 230;
488         case 191:       return 248;
489         case 192:       return 191;
490         case 193:       return 161;
491         case 194:       return 172;
492         case 195:       return 0;
493         case 196:       return 0;
494         case 197:       return 0;
495         case 198:       return 0;
496         case 199:       return 171;
497         case 200:       return 187;
498         case 201:       return 201;
499         case 202:       return 0;
500         case 203:       return 192;
501         case 204:       return 195;
502         case 205:       return 213;
503         case 206:       return 0;
504         case 207:       return 0;
505         case 208:       return 0;
506         case 209:       return 0;
507         case 210:       return 0;
508         
509         case 214:       return 247;
510
511         case 229:       return 194;
512         case 230:       return 202;
513         case 231:       return 193;
514         case 232:       return 203;
515         case 233:       return 200;
516         case 234:       return 205;
517         case 235:       return 206;
518         case 236:       return 207;
519         case 237:       return 204;
520         case 238:       return 211;
521         case 239:       return 212;
522         case 240:       return 0;
523         case 241:       return 210;
524         case 242:       return 218;
525         case 243:       return 219;
526         case 244:       return 217;
527         case 245:       return 0;
528         case 246:       return 0;
529         case 247:       return 0;
530         case 248:       return 0;
531         case 249:       return 0;
532         case 250:       return 0;
533
534         
535                 default: return 0;
536         }
537
538 }
539
540 #define FIRSTFILEBUFLG 512
541 static bool g_hasFirstFile = false;
542 static char g_firstFileBuf[512];
543
544 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true
545 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
546         if (g_hasFirstFile) {
547                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
548                 buf[FIRSTFILEBUFLG - 1] = '\0';
549                 return 1;
550         } else {
551                 return 0; 
552         }
553 }
554
555
556 #pragma mark Cocoa objects
557
558 /**
559  * CocoaAppDelegate
560  * ObjC object to capture applicationShouldTerminate, and send quit event
561  **/
562 @interface CocoaAppDelegate : NSObject {
563         GHOST_SystemCocoa *systemCocoa;
564 }
565 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
566 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
567 - (void)applicationWillTerminate:(NSNotification *)aNotification;
568 @end
569
570 @implementation CocoaAppDelegate : NSObject
571 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
572 {
573         systemCocoa = sysCocoa;
574 }
575
576 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
577 {
578         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled
579         //Note that Cmd+Q is already handled by keyhandler
580     if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
581                 return NSTerminateCancel;//NSTerminateNow;
582         else
583                 return NSTerminateCancel;
584 }
585
586 // To avoid cancelling a log off process, we must use Cocoa termination process
587 // And this function is the only chance to perform clean up
588 // So WM_exit needs to be called directly, as the event loop will never run before termination
589 - (void)applicationWillTerminate:(NSNotification *)aNotification
590 {
591         /*G.afbreek = 0; //Let Cocoa perform the termination at the end
592         WM_exit(C);*/
593 }
594 @end
595
596
597
598 #pragma mark initialization/finalization
599
600
601 GHOST_SystemCocoa::GHOST_SystemCocoa()
602 {
603         m_modifierMask =0;
604         m_pressedMouseButtons =0;
605         m_displayManager = new GHOST_DisplayManagerCocoa ();
606         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
607         m_displayManager->initialize();
608
609         //NSEvent timeStamp is given in system uptime, state start date is boot time
610         //FIXME : replace by Cocoa equivalent
611         int mib[2];
612         struct timeval boottime;
613         size_t len;
614         
615         mib[0] = CTL_KERN;
616         mib[1] = KERN_BOOTTIME;
617         len = sizeof(struct timeval);
618         
619         sysctl(mib, 2, &boottime, &len, NULL, 0);
620         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
621         
622         m_ignoreWindowSizedMessages = false;
623 }
624
625 GHOST_SystemCocoa::~GHOST_SystemCocoa()
626 {
627 }
628
629
630 GHOST_TSuccess GHOST_SystemCocoa::init()
631 {
632         
633     GHOST_TSuccess success = GHOST_System::init();
634     if (success) {
635                 //ProcessSerialNumber psn;
636                 
637                 //Carbon stuff to move window & menu to foreground
638                 /*if (!GetCurrentProcess(&psn)) {
639                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
640                         SetFrontProcess(&psn);
641                 }*/
642                 
643                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
644                 if (NSApp == nil) {
645                         [NSApplication sharedApplication];
646                         
647                         if ([NSApp mainMenu] == nil) {
648                                 NSMenu *mainMenubar = [[NSMenu alloc] init];
649                                 NSMenuItem *menuItem;
650                                 NSMenu *windowMenu;
651                                 NSMenu *appMenu;
652                                 
653                                 //Create the application menu
654                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
655                                 
656                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
657                                 [appMenu addItem:[NSMenuItem separatorItem]];
658                                 
659                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
660                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
661                                  
662                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
663                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
664                                 
665                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
666                                 
667                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
668                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
669                                 
670                                 menuItem = [[NSMenuItem alloc] init];
671                                 [menuItem setSubmenu:appMenu];
672                                 
673                                 [mainMenubar addItem:menuItem];
674                                 [menuItem release];
675                                 [appMenu release];
676                                 
677                                 //Create the window menu
678                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
679                                 
680                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
681                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
682                                 
683                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
684                                 
685                                 menuItem = [[NSMenuItem alloc] init];
686                                 [menuItem setSubmenu:windowMenu];
687                                 
688                                 [mainMenubar addItem:menuItem];
689                                 [menuItem release];
690                                 
691                                 [NSApp setMainMenu:mainMenubar];
692                                 [NSApp setWindowsMenu:windowMenu];
693                                 [windowMenu release];
694                         }
695                         [NSApp finishLaunching];
696                 }
697                 if ([NSApp delegate] == nil) {
698                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
699                         [appDelegate setSystemCocoa:this];
700                         [NSApp setDelegate:appDelegate];
701                 }
702                                 
703                 [pool drain];
704                 
705                 /*
706          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
707          * This initializes the cursor's visibility counter to 0.
708          */
709         /*::InitCursor();
710                 
711                 MenuRef windMenu;
712                 ::CreateStandardWindowMenu(0, &windMenu);
713                 ::InsertMenu(windMenu, 0);
714                 ::DrawMenuBar();
715                 
716         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
717                 
718                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
719                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
720                 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
721                 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
722                 */
723     }
724     return success;
725 }
726
727
728 #pragma mark window management
729
730 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
731 {
732         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
733         int mib[2];
734         struct timeval boottime;
735         size_t len;
736         
737         mib[0] = CTL_KERN;
738         mib[1] = KERN_BOOTTIME;
739         len = sizeof(struct timeval);
740         
741         sysctl(mib, 2, &boottime, &len, NULL, 0);
742
743         return ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
744 }
745
746
747 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
748 {
749         //Note that OS X supports monitor hot plug
750         // We do not support multiple monitors at the moment
751         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
752
753         GHOST_TUns8 count = [[NSScreen screens] count];
754
755         [pool drain];
756         return count;
757 }
758
759
760 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
761 {
762         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
763         //Get visible frame, that is frame excluding dock and top menu bar
764         NSRect frame = [[NSScreen mainScreen] visibleFrame];
765         
766         //Returns max window contents (excluding title bar...)
767         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
768                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
769         
770         width = contentRect.size.width;
771         height = contentRect.size.height;
772
773         [pool drain];
774 }
775
776
777 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
778         const STR_String& title, 
779         GHOST_TInt32 left,
780         GHOST_TInt32 top,
781         GHOST_TUns32 width,
782         GHOST_TUns32 height,
783         GHOST_TWindowState state,
784         GHOST_TDrawingContextType type,
785         bool stereoVisual,
786         const GHOST_TEmbedderWindowID parentWindow
787 )
788 {
789     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
790         GHOST_IWindow* window = 0;
791         
792         //Get the available rect for including window contents
793         NSRect frame = [[NSScreen mainScreen] visibleFrame];
794         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
795                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
796         
797         //Ensures window top left is inside this available rect
798         left = left > contentRect.origin.x ? left : contentRect.origin.x;
799         top = top > contentRect.origin.y ? top : contentRect.origin.y;
800         
801         window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type);
802
803     if (window) {
804         if (window->getValid()) {
805             // Store the pointer to the window 
806             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
807             m_windowManager->addWindow(window);
808             m_windowManager->setActiveWindow(window);
809                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
810             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
811                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
812
813         }
814         else {
815                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
816             delete window;
817             window = 0;
818         }
819     }
820         else {
821                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
822         }
823         [pool drain];
824     return window;
825 }
826
827 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
828 {       
829         GHOST_TSuccess success = GHOST_kFailure;
830
831         //TODO: update this method
832         // need yo make this Carbon all on 10.5 for fullscreen to work correctly
833         CGCaptureAllDisplays();
834         
835         success = GHOST_System::beginFullScreen( setting, window, stereoVisual);
836         
837         if( success != GHOST_kSuccess ) {
838                         // fullscreen failed for other reasons, release
839                         CGReleaseAllDisplays(); 
840         }
841
842         return success;
843 }
844
845 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
846 {       
847         //TODO: update this method
848         CGReleaseAllDisplays();
849         return GHOST_System::endFullScreen();
850 }
851
852
853         
854
855 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
856 {
857     NSPoint mouseLoc = [NSEvent mouseLocation];
858         
859     // Convert the coordinates to screen coordinates
860     x = (GHOST_TInt32)mouseLoc.x;
861     y = (GHOST_TInt32)mouseLoc.y;
862     return GHOST_kSuccess;
863 }
864
865
866 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
867 {
868         float xf=(float)x, yf=(float)y;
869
870         CGAssociateMouseAndMouseCursorPosition(false);
871         CGWarpMouseCursorPosition(CGPointMake(xf, yf));
872         CGAssociateMouseAndMouseCursorPosition(true);
873
874     return GHOST_kSuccess;
875 }
876
877
878 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
879 {
880         NSUInteger modifiers = [[NSApp currentEvent] modifierFlags];
881         //Direct query to modifierFlags can be used in 10.6
882
883     keys.set(GHOST_kModifierKeyCommand, (modifiers & NSCommandKeyMask) ? true : false);
884     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & NSAlternateKeyMask) ? true : false);
885     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & NSShiftKeyMask) ? true : false);
886     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & NSControlKeyMask) ? true : false);
887         
888     return GHOST_kSuccess;
889 }
890
891 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
892 {
893         buttons.clear();
894     buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
895         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
896         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
897         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
898         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
899     return GHOST_kSuccess;
900 }
901
902
903
904 #pragma mark Event handlers
905
906 /**
907  * The event queue polling function
908  */
909 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
910 {
911         bool anyProcessed = false;
912         NSEvent *event;
913         
914         //      SetMouseCoalescingEnabled(false, NULL);
915         //TODO : implement timer ??
916         
917         /*do {
918                 GHOST_TimerManager* timerMgr = getTimerManager();
919                 
920                  if (waitForEvent) {
921                  GHOST_TUns64 next = timerMgr->nextFireTime();
922                  double timeOut;
923                  
924                  if (next == GHOST_kFireTimeNever) {
925                  timeOut = kEventDurationForever;
926                  } else {
927                  timeOut = (double)(next - getMilliSeconds())/1000.0;
928                  if (timeOut < 0.0)
929                  timeOut = 0.0;
930                  }
931                  
932                  ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
933                  }
934                  
935                  if (timerMgr->fireTimers(getMilliSeconds())) {
936                  anyProcessed = true;
937                  }
938                  
939                  //TODO: check fullscreen redrawing issues
940                  if (getFullScreen()) {
941                  // Check if the full-screen window is dirty
942                  GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
943                  if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
944                  pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
945                  anyProcessed = true;
946                  }
947                  }*/
948                 
949                 do {
950                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
951                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
952                                                                            untilDate:[NSDate distantPast]
953                                                                                   inMode:NSDefaultRunLoopMode
954                                                                                  dequeue:YES];
955                         if (event==nil) {
956                                 [pool drain];
957                                 break;
958                         }
959                         
960                         anyProcessed = true;
961                         
962                         switch ([event type]) {
963                                 case NSKeyDown:
964                                 case NSKeyUp:
965                                 case NSFlagsChanged:
966                                         handleKeyEvent(event);
967                                         
968                                         /* Support system-wide keyboard shortcuts, like Exposé, ...) =>included in always NSApp sendEvent */
969                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
970                                          [NSApp sendEvent:event];
971                                          }*/
972                                         break;
973                                         
974                                 case NSLeftMouseDown:
975                                 case NSLeftMouseUp:
976                                 case NSRightMouseDown:
977                                 case NSRightMouseUp:
978                                 case NSMouseMoved:
979                                 case NSLeftMouseDragged:
980                                 case NSRightMouseDragged:
981                                 case NSScrollWheel:
982                                 case NSOtherMouseDown:
983                                 case NSOtherMouseUp:
984                                 case NSOtherMouseDragged:                               
985                                         handleMouseEvent(event);
986                                         break;
987                                         
988                                 case NSTabletPoint:
989                                 case NSTabletProximity:
990                                         handleTabletEvent(event);
991                                         break;
992                                         
993                                         /* Trackpad features, will need OS X 10.6 for implementation
994                                          case NSEventTypeGesture:
995                                          case NSEventTypeMagnify:
996                                          case NSEventTypeSwipe:
997                                          case NSEventTypeRotate:
998                                          case NSEventTypeBeginGesture:
999                                          case NSEventTypeEndGesture:
1000                                          break; */
1001                                         
1002                                         /*Unused events
1003                                          NSMouseEntered       = 8,
1004                                          NSMouseExited        = 9,
1005                                          NSAppKitDefined      = 13,
1006                                          NSSystemDefined      = 14,
1007                                          NSApplicationDefined = 15,
1008                                          NSPeriodic           = 16,
1009                                          NSCursorUpdate       = 17,*/
1010                                         
1011                                 default:
1012                                         break;
1013                         }
1014                         //Resend event to NSApp to ensure Mac wide events are handled
1015                         [NSApp sendEvent:event];
1016                         [pool drain];
1017                 } while (event!= nil);          
1018         //} while (waitForEvent && !anyProcessed); Needed only for timer implementation
1019         
1020         
1021         
1022     return anyProcessed;
1023 }
1024
1025 //Note: called from NSWindow delegate
1026 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
1027 {
1028         if (!validWindow(window)) {
1029                 return GHOST_kFailure;
1030         }
1031
1032         //if (!getFullScreen()) {
1033                 switch(eventType) 
1034                 {
1035                         case GHOST_kEventWindowClose:
1036                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
1037                                 break;
1038                         case GHOST_kEventWindowActivate:
1039                                 m_windowManager->setActiveWindow(window);
1040                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1041                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
1042                                 break;
1043                         case GHOST_kEventWindowDeactivate:
1044                                 m_windowManager->setWindowInactive(window);
1045                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
1046                                 break;
1047                         case GHOST_kEventWindowUpdate:
1048                                 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
1049                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
1050                                 break;
1051                         case GHOST_kEventWindowSize:
1052                                 if (!m_ignoreWindowSizedMessages)
1053                                 {
1054                                         window->updateDrawingContext();
1055                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
1056                                 }
1057                                 break;
1058                         default:
1059                                 return GHOST_kFailure;
1060                                 break;
1061                 }
1062 //      }
1063         //else {
1064                 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
1065                 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
1066                 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
1067         //}
1068         return GHOST_kSuccess;
1069 }
1070
1071 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1072 {
1073         //Check open windows if some changes are not saved
1074         if (m_windowManager->getAnyModifiedState())
1075         {
1076                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved. Do you really want to quit ?",
1077                                                                                  @"Cancel", @"Quit anyway", nil);
1078                 if (shouldQuit == NSAlertAlternateReturn)
1079                 {
1080                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1081                         return GHOST_kExitNow;
1082                 }
1083         }
1084         else {
1085                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1086                 return GHOST_kExitNow;
1087         }
1088         
1089         return GHOST_kExitCancel;
1090 }
1091
1092
1093 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr)
1094 {
1095         NSEvent *event = (NSEvent *)eventPtr;
1096         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1097         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
1098         NSUInteger tabletEvent;
1099         
1100         //Handle tablet events combined with mouse events
1101         @try {
1102                 switch ([event subtype]) {
1103                 case NX_SUBTYPE_TABLET_POINT:
1104                         tabletEvent = NSTabletPoint;
1105                         break;
1106                 case NX_SUBTYPE_TABLET_PROXIMITY:
1107                         tabletEvent = NSTabletProximity;
1108                         break;
1109
1110                 default:
1111                         tabletEvent = [event type];
1112                         break;
1113                 }
1114         }
1115         @catch (NSException * e) {
1116                 //FIXME: check why we get such exceptions when using a tablet
1117                 return GHOST_kFailure;
1118         }
1119         
1120         
1121         switch (tabletEvent) {
1122                 case NSTabletPoint:
1123                         ct.Pressure = [event tangentialPressure];
1124                         ct.Xtilt = [event tilt].x;
1125                         ct.Ytilt = [event tilt].y;
1126                         break;
1127                 
1128                 case NSTabletProximity:
1129                         ct.Pressure = 0;
1130                         ct.Xtilt = 0;
1131                         ct.Ytilt = 0;
1132                         if ([event isEnteringProximity])
1133                         {
1134                                 //pointer is entering tablet area proximity
1135                                 switch ([event pointingDeviceType]) {
1136                                         case NSPenPointingDevice:
1137                                                 ct.Active = GHOST_kTabletModeStylus;
1138                                                 break;
1139                                         case NSEraserPointingDevice:
1140                                                 ct.Active = GHOST_kTabletModeEraser;
1141                                                 break;
1142                                         case NSCursorPointingDevice:
1143                                         case NSUnknownPointingDevice:
1144                                         default:
1145                                                 ct.Active = GHOST_kTabletModeNone;
1146                                                 break;
1147                                 }
1148                         } else {
1149                                 // pointer is leaving - return to mouse
1150                                 ct.Active = GHOST_kTabletModeNone;
1151                         }
1152                         break;
1153                 
1154                 default:
1155                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1156                         return GHOST_kFailure;
1157                         break;
1158         }
1159         return GHOST_kSuccess;
1160 }
1161
1162
1163 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1164 {
1165         NSEvent *event = (NSEvent *)eventPtr;
1166     GHOST_IWindow* window = m_windowManager->getActiveWindow();
1167         
1168         if (!window) {
1169                 return GHOST_kFailure;
1170         }
1171         
1172         switch ([event type])
1173     {
1174                 case NSLeftMouseDown:
1175                 case NSRightMouseDown:
1176                 case NSOtherMouseDown:
1177                         pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1178                         handleTabletEvent(eventPtr);
1179                         break;
1180                                                 
1181                 case NSLeftMouseUp:
1182                 case NSRightMouseUp:
1183                 case NSOtherMouseUp:
1184                         pushEvent(new GHOST_EventButton([event timestamp], GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1185                         handleTabletEvent(eventPtr);
1186                         break;
1187                         
1188                 case NSLeftMouseDragged:
1189                 case NSRightMouseDragged:
1190                 case NSOtherMouseDragged:                               
1191                         handleTabletEvent(eventPtr);
1192                 case NSMouseMoved:
1193                         {
1194                                 NSPoint mousePos = [event locationInWindow];
1195                                 pushEvent(new GHOST_EventCursor([event timestamp], GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1196                                 break;
1197                         }
1198                         
1199                 case NSScrollWheel:
1200                         {
1201                                 GHOST_TInt32 delta;
1202                                 delta = [event deltaY] > 0 ? 1 : -1;
1203                                 pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
1204
1205                         }
1206                         break;
1207                         
1208                 default:
1209                         return GHOST_kFailure;
1210                         break;
1211                 }
1212         
1213         return GHOST_kSuccess;
1214 }
1215
1216
1217 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1218 {
1219         NSEvent *event = (NSEvent *)eventPtr;
1220         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1221         NSUInteger modifiers;
1222         NSString *characters;
1223         GHOST_TKey keyCode;
1224         unsigned char ascii;
1225
1226         /* Can happen, very rarely - seems to only be when command-H makes
1227          * the window go away and we still get an HKey up. 
1228          */
1229         if (!window) {
1230                 return GHOST_kFailure;
1231         }
1232         
1233         switch ([event type]) {
1234                 case NSKeyDown:
1235                 case NSKeyUp:
1236                         characters = [event characters];
1237                         if ([characters length]) { //Check for dead keys
1238                                 keyCode = convertKey([event keyCode],
1239                                                                          [[event charactersIgnoringModifiers] characterAtIndex:0]);
1240                                 ascii= convertRomanToLatin((char)[characters characterAtIndex:0]);
1241                         } else {
1242                                 keyCode = convertKey([event keyCode],0);
1243                                 ascii= 0;
1244                         }
1245                         
1246                         
1247                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1248                                 break; //Cmd-Q is directly handled by Cocoa
1249                         
1250                         if ([event type] == NSKeyDown) {
1251                                 pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyDown, window, keyCode, ascii) );
1252                                 //printf("\nKey pressed keyCode=%u ascii=%i %c",keyCode,ascii,ascii);
1253                         } else {
1254                                 pushEvent( new GHOST_EventKey([event timestamp], GHOST_kEventKeyUp, window, keyCode, ascii) );
1255                         }
1256                         break;
1257         
1258                 case NSFlagsChanged: 
1259                         modifiers = [event modifierFlags];
1260                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1261                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1262                         }
1263                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1264                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1265                         }
1266                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1267                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1268                         }
1269                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1270                                 pushEvent( new GHOST_EventKey([event timestamp], (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1271                         }
1272                         
1273                         m_modifierMask = modifiers;
1274                         break;
1275                         
1276                 default:
1277                         return GHOST_kFailure;
1278                         break;
1279         }
1280         
1281         return GHOST_kSuccess;
1282 }
1283
1284
1285 /* System wide mouse clicks are handled directly through systematic event forwarding to Cocoa
1286 bool GHOST_SystemCarbon::handleMouseDown(void *eventPtr)
1287 {
1288         NSEvent *event = (NSEvent *)eventPtr;
1289         WindowPtr                       window;
1290         short                           part;
1291         BitMap                          screenBits;
1292     bool                                handled = true;
1293     GHOST_WindowCarbon* ghostWindow;
1294     Point                               mousePos = {0 , 0};
1295         
1296         ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
1297         
1298         part = ::FindWindow(mousePos, &window);
1299         ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
1300         
1301         switch (part) {
1302                 case inMenuBar:
1303                         handleMenuCommand(::MenuSelect(mousePos));
1304                         break;
1305                         
1306                 case inDrag:
1307                         // *
1308                         // * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
1309                         // * events. By setting m_ignoreWindowSizedMessages these are suppressed.
1310                         // * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
1311                         // *
1312                         // even worse: scale window also generates a load of events, and nothing 
1313                          //  is handled (read: client's event proc called) until you release mouse (ton) 
1314                         
1315                         GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
1316                         m_ignoreWindowSizedMessages = true;
1317                         ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
1318                         m_ignoreWindowSizedMessages = false;
1319                         
1320                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, ghostWindow) );
1321
1322                         break;
1323                 
1324                 case inContent:
1325                         if (window != ::FrontWindow()) {
1326                                 ::SelectWindow(window);
1327                                 //
1328                                 // * We add a mouse down event on the newly actived window
1329                                 // *            
1330                                 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
1331                                 EventMouseButton button;
1332                                 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
1333                                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
1334                         } else {
1335                                 handled = false;
1336                         }
1337                         break;
1338                         
1339                 case inGoAway:
1340                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1341                         if (::TrackGoAway(window, mousePos))
1342                         {
1343                                 // todo: add option-close, because itÿs in the HIG
1344                                 // if (event.modifiers & optionKey) {
1345                                         // Close the clean documents, others will be confirmed one by one.
1346                                 //}
1347                                 // else {
1348                                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
1349                                 //}
1350                         }
1351                         break;
1352                         
1353                 case inGrow:
1354                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1355                         ::ResizeWindow(window, mousePos, NULL, NULL);
1356                         break;
1357                         
1358                 case inZoomIn:
1359                 case inZoomOut:
1360                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
1361                         if (::TrackBox(window, mousePos, part)) {
1362                                 int macState;
1363                                 
1364                                 macState = ghostWindow->getMac_windowState();
1365                                 if ( macState== 0)
1366                                         ::ZoomWindow(window, part, true);
1367                                 else 
1368                                         if (macState == 2) { // always ok
1369                                                         ::ZoomWindow(window, part, true);
1370                                                         ghostWindow->setMac_windowState(1);
1371                                         } else { // need to force size again
1372                                         //      GHOST_TUns32 scr_x,scr_y; //unused
1373                                                 Rect outAvailableRect;
1374                                                 
1375                                                 ghostWindow->setMac_windowState(2);
1376                                                 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
1377                                                 
1378                                                 //this->getMainDisplayDimensions(scr_x,scr_y);
1379                                                 ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
1380                                                 ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
1381                                         }
1382                                 
1383                         }
1384                         break;
1385
1386                 default:
1387                         handled = false;
1388                         break;
1389         }
1390         
1391         return handled;
1392 }
1393
1394
1395 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
1396 {
1397         short           menuID;
1398         short           menuItem;
1399         UInt32          command;
1400         bool            handled;
1401         OSErr           err;
1402         
1403         menuID = HiWord(menuResult);
1404         menuItem = LoWord(menuResult);
1405
1406         err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
1407
1408         handled = false;
1409         
1410         if (err || command == 0) {
1411         }
1412         else {
1413                 switch(command) {
1414                 }
1415         }
1416
1417         ::HiliteMenu(0);
1418     return handled;
1419 }*/
1420
1421
1422
1423 #pragma mark Clipboard get/set
1424
1425 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1426 {
1427         GHOST_TUns8 * temp_buff;
1428         size_t pastedTextSize;  
1429         
1430         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1431         
1432         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1433         
1434         if (pasteBoard == nil) {
1435                 [pool drain];
1436                 return NULL;
1437         }
1438         
1439         NSArray *supportedTypes =
1440                 [NSArray arrayWithObjects: @"public.utf8-plain-text", nil];
1441         
1442         NSString *bestType = [[NSPasteboard generalPasteboard]
1443                                                   availableTypeFromArray:supportedTypes];
1444         
1445         if (bestType == nil) {
1446                 [pool drain];
1447                 return NULL;
1448         }
1449         
1450         NSString * textPasted = [pasteBoard stringForType:@"public.utf8-plain-text"];
1451
1452         if (textPasted == nil) {
1453                 [pool drain];
1454                 return NULL;
1455         }
1456         
1457         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1458         
1459         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1460
1461         if (temp_buff == NULL) {
1462                 [pool drain];
1463                 return NULL;
1464         }
1465         
1466         strncpy((char*)temp_buff, [textPasted UTF8String], pastedTextSize);
1467         
1468         temp_buff[pastedTextSize] = '\0';
1469         
1470         [pool drain];
1471
1472         if(temp_buff) {
1473                 return temp_buff;
1474         } else {
1475                 return NULL;
1476         }
1477 }
1478
1479 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1480 {
1481         NSString *textToCopy;
1482         
1483         if(selection) {return;} // for copying the selection, used on X11
1484
1485         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1486                 
1487         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1488         
1489         if (pasteBoard == nil) {
1490                 [pool drain];
1491                 return;
1492         }
1493         
1494         NSArray *supportedTypes = [NSArray arrayWithObject:@"public.utf8-plain-text"];
1495         
1496         [pasteBoard declareTypes:supportedTypes owner:nil];
1497         
1498         textToCopy = [NSString stringWithUTF8String:buffer];
1499         
1500         [pasteBoard setString:textToCopy forType:@"public.utf8-plain-text"];
1501         
1502         [pool drain];
1503 }
1504
1505 #pragma mark Carbon stuff to remove
1506
1507 #ifdef WITH_CARBON
1508
1509
1510 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1511 {
1512         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1513         
1514         return noErr;
1515 }
1516
1517 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1518 {
1519         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1520         AEDescList docs;
1521         SInt32 ndocs;
1522         OSErr err;
1523         
1524         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
1525         if (err != noErr)  return err;
1526         
1527         err = AECountItems(&docs, &ndocs);
1528         if (err==noErr) {
1529                 int i;
1530                 
1531                 for (i=0; i<ndocs; i++) {
1532                         FSSpec fss;
1533                         AEKeyword kwd;
1534                         DescType actType;
1535                         Size actSize;
1536                         
1537                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
1538                         if (err!=noErr)
1539                                 break;
1540                         
1541                         if (i==0) {
1542                                 FSRef fsref;
1543                                 
1544                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
1545                                         break;
1546                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
1547                                         break;
1548                                 
1549                                 g_hasFirstFile = true;
1550                         }
1551                 }
1552         }
1553         
1554         AEDisposeDesc(&docs);
1555         
1556         return err;
1557 }
1558
1559 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1560 {
1561         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1562         
1563         return noErr;
1564 }
1565
1566 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
1567 {
1568         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
1569         
1570         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
1571         
1572         return noErr;
1573 }
1574 #endif