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