merge with trunk at r27259 and commit of a patch by anthony jones to fix msvc (though...
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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_EventTrackpad.h"
45 #include "GHOST_EventDragnDrop.h"
46 #include "GHOST_EventString.h"
47 \r
48 #include "GHOST_TimerManager.h"\r
49 #include "GHOST_TimerTask.h"\r
50 #include "GHOST_WindowManager.h"\r
51 #include "GHOST_WindowCocoa.h"\r
52 #include "GHOST_NDOFManager.h"\r
53 #include "AssertMacros.h"\r
54 \r
55 #pragma mark KeyMap, mouse converters\r
56 \r
57 \r
58 /* Keycodes from Carbon include file */\r
59 /*  \r
60  *  Summary:\r
61  *    Virtual keycodes\r
62  *  \r
63  *  Discussion:\r
64  *    These constants are the virtual keycodes defined originally in\r
65  *    Inside Mac Volume V, pg. V-191. They identify physical keys on a\r
66  *    keyboard. Those constants with "ANSI" in the name are labeled\r
67  *    according to the key position on an ANSI-standard US keyboard.\r
68  *    For example, kVK_ANSI_A indicates the virtual keycode for the key\r
69  *    with the letter 'A' in the US keyboard layout. Other keyboard\r
70  *    layouts may have the 'A' key label on a different physical key;\r
71  *    in this case, pressing 'A' will generate a different virtual\r
72  *    keycode.\r
73  */\r
74 enum {\r
75         kVK_ANSI_A                    = 0x00,\r
76         kVK_ANSI_S                    = 0x01,\r
77         kVK_ANSI_D                    = 0x02,\r
78         kVK_ANSI_F                    = 0x03,\r
79         kVK_ANSI_H                    = 0x04,\r
80         kVK_ANSI_G                    = 0x05,\r
81         kVK_ANSI_Z                    = 0x06,\r
82         kVK_ANSI_X                    = 0x07,\r
83         kVK_ANSI_C                    = 0x08,\r
84         kVK_ANSI_V                    = 0x09,\r
85         kVK_ANSI_B                    = 0x0B,\r
86         kVK_ANSI_Q                    = 0x0C,\r
87         kVK_ANSI_W                    = 0x0D,\r
88         kVK_ANSI_E                    = 0x0E,\r
89         kVK_ANSI_R                    = 0x0F,\r
90         kVK_ANSI_Y                    = 0x10,\r
91         kVK_ANSI_T                    = 0x11,\r
92         kVK_ANSI_1                    = 0x12,\r
93         kVK_ANSI_2                    = 0x13,\r
94         kVK_ANSI_3                    = 0x14,\r
95         kVK_ANSI_4                    = 0x15,\r
96         kVK_ANSI_6                    = 0x16,\r
97         kVK_ANSI_5                    = 0x17,\r
98         kVK_ANSI_Equal                = 0x18,\r
99         kVK_ANSI_9                    = 0x19,\r
100         kVK_ANSI_7                    = 0x1A,\r
101         kVK_ANSI_Minus                = 0x1B,\r
102         kVK_ANSI_8                    = 0x1C,\r
103         kVK_ANSI_0                    = 0x1D,\r
104         kVK_ANSI_RightBracket         = 0x1E,\r
105         kVK_ANSI_O                    = 0x1F,\r
106         kVK_ANSI_U                    = 0x20,\r
107         kVK_ANSI_LeftBracket          = 0x21,\r
108         kVK_ANSI_I                    = 0x22,\r
109         kVK_ANSI_P                    = 0x23,\r
110         kVK_ANSI_L                    = 0x25,\r
111         kVK_ANSI_J                    = 0x26,\r
112         kVK_ANSI_Quote                = 0x27,\r
113         kVK_ANSI_K                    = 0x28,\r
114         kVK_ANSI_Semicolon            = 0x29,\r
115         kVK_ANSI_Backslash            = 0x2A,\r
116         kVK_ANSI_Comma                = 0x2B,\r
117         kVK_ANSI_Slash                = 0x2C,\r
118         kVK_ANSI_N                    = 0x2D,\r
119         kVK_ANSI_M                    = 0x2E,\r
120         kVK_ANSI_Period               = 0x2F,\r
121         kVK_ANSI_Grave                = 0x32,\r
122         kVK_ANSI_KeypadDecimal        = 0x41,\r
123         kVK_ANSI_KeypadMultiply       = 0x43,\r
124         kVK_ANSI_KeypadPlus           = 0x45,\r
125         kVK_ANSI_KeypadClear          = 0x47,\r
126         kVK_ANSI_KeypadDivide         = 0x4B,\r
127         kVK_ANSI_KeypadEnter          = 0x4C,\r
128         kVK_ANSI_KeypadMinus          = 0x4E,\r
129         kVK_ANSI_KeypadEquals         = 0x51,\r
130         kVK_ANSI_Keypad0              = 0x52,\r
131         kVK_ANSI_Keypad1              = 0x53,\r
132         kVK_ANSI_Keypad2              = 0x54,\r
133         kVK_ANSI_Keypad3              = 0x55,\r
134         kVK_ANSI_Keypad4              = 0x56,\r
135         kVK_ANSI_Keypad5              = 0x57,\r
136         kVK_ANSI_Keypad6              = 0x58,\r
137         kVK_ANSI_Keypad7              = 0x59,\r
138         kVK_ANSI_Keypad8              = 0x5B,\r
139         kVK_ANSI_Keypad9              = 0x5C\r
140 };\r
141 \r
142 /* keycodes for keys that are independent of keyboard layout*/\r
143 enum {\r
144         kVK_Return                    = 0x24,\r
145         kVK_Tab                       = 0x30,\r
146         kVK_Space                     = 0x31,\r
147         kVK_Delete                    = 0x33,\r
148         kVK_Escape                    = 0x35,\r
149         kVK_Command                   = 0x37,\r
150         kVK_Shift                     = 0x38,\r
151         kVK_CapsLock                  = 0x39,\r
152         kVK_Option                    = 0x3A,\r
153         kVK_Control                   = 0x3B,\r
154         kVK_RightShift                = 0x3C,\r
155         kVK_RightOption               = 0x3D,\r
156         kVK_RightControl              = 0x3E,\r
157         kVK_Function                  = 0x3F,\r
158         kVK_F17                       = 0x40,\r
159         kVK_VolumeUp                  = 0x48,\r
160         kVK_VolumeDown                = 0x49,\r
161         kVK_Mute                      = 0x4A,\r
162         kVK_F18                       = 0x4F,\r
163         kVK_F19                       = 0x50,\r
164         kVK_F20                       = 0x5A,\r
165         kVK_F5                        = 0x60,\r
166         kVK_F6                        = 0x61,\r
167         kVK_F7                        = 0x62,\r
168         kVK_F3                        = 0x63,\r
169         kVK_F8                        = 0x64,\r
170         kVK_F9                        = 0x65,\r
171         kVK_F11                       = 0x67,\r
172         kVK_F13                       = 0x69,\r
173         kVK_F16                       = 0x6A,\r
174         kVK_F14                       = 0x6B,\r
175         kVK_F10                       = 0x6D,\r
176         kVK_F12                       = 0x6F,\r
177         kVK_F15                       = 0x71,\r
178         kVK_Help                      = 0x72,\r
179         kVK_Home                      = 0x73,\r
180         kVK_PageUp                    = 0x74,\r
181         kVK_ForwardDelete             = 0x75,\r
182         kVK_F4                        = 0x76,\r
183         kVK_End                       = 0x77,\r
184         kVK_F2                        = 0x78,\r
185         kVK_PageDown                  = 0x79,\r
186         kVK_F1                        = 0x7A,\r
187         kVK_LeftArrow                 = 0x7B,\r
188         kVK_RightArrow                = 0x7C,\r
189         kVK_DownArrow                 = 0x7D,\r
190         kVK_UpArrow                   = 0x7E\r
191 };\r
192 \r
193 /* ISO keyboards only*/\r
194 enum {\r
195         kVK_ISO_Section               = 0x0A\r
196 };\r
197 \r
198 /* JIS keyboards only*/\r
199 enum {\r
200         kVK_JIS_Yen                   = 0x5D,\r
201         kVK_JIS_Underscore            = 0x5E,\r
202         kVK_JIS_KeypadComma           = 0x5F,\r
203         kVK_JIS_Eisu                  = 0x66,\r
204         kVK_JIS_Kana                  = 0x68\r
205 };\r
206 \r
207 \r
208 static GHOST_TButtonMask convertButton(int button)\r
209 {\r
210         switch (button) {\r
211                 case 0:\r
212                         return GHOST_kButtonMaskLeft;\r
213                 case 1:\r
214                         return GHOST_kButtonMaskRight;\r
215                 case 2:\r
216                         return GHOST_kButtonMaskMiddle;\r
217                 case 3:\r
218                         return GHOST_kButtonMaskButton4;\r
219                 case 4:\r
220                         return GHOST_kButtonMaskButton5;\r
221                 default:\r
222                         return GHOST_kButtonMaskLeft;\r
223         }\r
224 }\r
225 \r
226 /**\r
227  * Converts Mac rawkey codes (same for Cocoa & Carbon)\r
228  * into GHOST key codes\r
229  * @param rawCode The raw physical key code\r
230  * @param recvChar the character ignoring modifiers (except for shift)\r
231  * @return Ghost key code\r
232  */\r
233 static GHOST_TKey convertKey(int rawCode, unichar recvChar) \r
234 {       \r
235         \r
236         //printf("\nrecvchar %c 0x%x",recvChar,recvChar);\r
237         switch (rawCode) {\r
238                 /*Physical keycodes not used due to map changes in int'l keyboards\r
239                 case kVK_ANSI_A:        return GHOST_kKeyA;\r
240                 case kVK_ANSI_B:        return GHOST_kKeyB;\r
241                 case kVK_ANSI_C:        return GHOST_kKeyC;\r
242                 case kVK_ANSI_D:        return GHOST_kKeyD;\r
243                 case kVK_ANSI_E:        return GHOST_kKeyE;\r
244                 case kVK_ANSI_F:        return GHOST_kKeyF;\r
245                 case kVK_ANSI_G:        return GHOST_kKeyG;\r
246                 case kVK_ANSI_H:        return GHOST_kKeyH;\r
247                 case kVK_ANSI_I:        return GHOST_kKeyI;\r
248                 case kVK_ANSI_J:        return GHOST_kKeyJ;\r
249                 case kVK_ANSI_K:        return GHOST_kKeyK;\r
250                 case kVK_ANSI_L:        return GHOST_kKeyL;\r
251                 case kVK_ANSI_M:        return GHOST_kKeyM;\r
252                 case kVK_ANSI_N:        return GHOST_kKeyN;\r
253                 case kVK_ANSI_O:        return GHOST_kKeyO;\r
254                 case kVK_ANSI_P:        return GHOST_kKeyP;\r
255                 case kVK_ANSI_Q:        return GHOST_kKeyQ;\r
256                 case kVK_ANSI_R:        return GHOST_kKeyR;\r
257                 case kVK_ANSI_S:        return GHOST_kKeyS;\r
258                 case kVK_ANSI_T:        return GHOST_kKeyT;\r
259                 case kVK_ANSI_U:        return GHOST_kKeyU;\r
260                 case kVK_ANSI_V:        return GHOST_kKeyV;\r
261                 case kVK_ANSI_W:        return GHOST_kKeyW;\r
262                 case kVK_ANSI_X:        return GHOST_kKeyX;\r
263                 case kVK_ANSI_Y:        return GHOST_kKeyY;\r
264                 case kVK_ANSI_Z:        return GHOST_kKeyZ;*/\r
265                 \r
266                 /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/\r
267                 case kVK_ISO_Section: return    GHOST_kKeyUnknown;\r
268                 case kVK_ANSI_1:        return GHOST_kKey1;\r
269                 case kVK_ANSI_2:        return GHOST_kKey2;\r
270                 case kVK_ANSI_3:        return GHOST_kKey3;\r
271                 case kVK_ANSI_4:        return GHOST_kKey4;\r
272                 case kVK_ANSI_5:        return GHOST_kKey5;\r
273                 case kVK_ANSI_6:        return GHOST_kKey6;\r
274                 case kVK_ANSI_7:        return GHOST_kKey7;\r
275                 case kVK_ANSI_8:        return GHOST_kKey8;\r
276                 case kVK_ANSI_9:        return GHOST_kKey9;\r
277                 case kVK_ANSI_0:        return GHOST_kKey0;\r
278         \r
279                 case kVK_ANSI_Keypad0:                  return GHOST_kKeyNumpad0;\r
280                 case kVK_ANSI_Keypad1:                  return GHOST_kKeyNumpad1;\r
281                 case kVK_ANSI_Keypad2:                  return GHOST_kKeyNumpad2;\r
282                 case kVK_ANSI_Keypad3:                  return GHOST_kKeyNumpad3;\r
283                 case kVK_ANSI_Keypad4:                  return GHOST_kKeyNumpad4;\r
284                 case kVK_ANSI_Keypad5:                  return GHOST_kKeyNumpad5;\r
285                 case kVK_ANSI_Keypad6:                  return GHOST_kKeyNumpad6;\r
286                 case kVK_ANSI_Keypad7:                  return GHOST_kKeyNumpad7;\r
287                 case kVK_ANSI_Keypad8:                  return GHOST_kKeyNumpad8;\r
288                 case kVK_ANSI_Keypad9:                  return GHOST_kKeyNumpad9;\r
289                 case kVK_ANSI_KeypadDecimal:    return GHOST_kKeyNumpadPeriod;\r
290                 case kVK_ANSI_KeypadEnter:              return GHOST_kKeyNumpadEnter;\r
291                 case kVK_ANSI_KeypadPlus:               return GHOST_kKeyNumpadPlus;\r
292                 case kVK_ANSI_KeypadMinus:              return GHOST_kKeyNumpadMinus;\r
293                 case kVK_ANSI_KeypadMultiply:   return GHOST_kKeyNumpadAsterisk;\r
294                 case kVK_ANSI_KeypadDivide:     return GHOST_kKeyNumpadSlash;\r
295                 case kVK_ANSI_KeypadClear:              return GHOST_kKeyUnknown;\r
296 \r
297                 case kVK_F1:                            return GHOST_kKeyF1;\r
298                 case kVK_F2:                            return GHOST_kKeyF2;\r
299                 case kVK_F3:                            return GHOST_kKeyF3;\r
300                 case kVK_F4:                            return GHOST_kKeyF4;\r
301                 case kVK_F5:                            return GHOST_kKeyF5;\r
302                 case kVK_F6:                            return GHOST_kKeyF6;\r
303                 case kVK_F7:                            return GHOST_kKeyF7;\r
304                 case kVK_F8:                            return GHOST_kKeyF8;\r
305                 case kVK_F9:                            return GHOST_kKeyF9;\r
306                 case kVK_F10:                           return GHOST_kKeyF10;\r
307                 case kVK_F11:                           return GHOST_kKeyF11;\r
308                 case kVK_F12:                           return GHOST_kKeyF12;\r
309                 case kVK_F13:                           return GHOST_kKeyF13;\r
310                 case kVK_F14:                           return GHOST_kKeyF14;\r
311                 case kVK_F15:                           return GHOST_kKeyF15;\r
312                 case kVK_F16:                           return GHOST_kKeyF16;\r
313                 case kVK_F17:                           return GHOST_kKeyF17;\r
314                 case kVK_F18:                           return GHOST_kKeyF18;\r
315                 case kVK_F19:                           return GHOST_kKeyF19;\r
316                 case kVK_F20:                           return GHOST_kKeyF20;\r
317                         \r
318                 case kVK_UpArrow:                       return GHOST_kKeyUpArrow;\r
319                 case kVK_DownArrow:                     return GHOST_kKeyDownArrow;\r
320                 case kVK_LeftArrow:                     return GHOST_kKeyLeftArrow;\r
321                 case kVK_RightArrow:            return GHOST_kKeyRightArrow;\r
322                         \r
323                 case kVK_Return:                        return GHOST_kKeyEnter;\r
324                 case kVK_Delete:                        return GHOST_kKeyBackSpace;\r
325                 case kVK_ForwardDelete:         return GHOST_kKeyDelete;\r
326                 case kVK_Escape:                        return GHOST_kKeyEsc;\r
327                 case kVK_Tab:                           return GHOST_kKeyTab;\r
328                 case kVK_Space:                         return GHOST_kKeySpace;\r
329                         \r
330                 case kVK_Home:                          return GHOST_kKeyHome;\r
331                 case kVK_End:                           return GHOST_kKeyEnd;\r
332                 case kVK_PageUp:                        return GHOST_kKeyUpPage;\r
333                 case kVK_PageDown:                      return GHOST_kKeyDownPage;\r
334                         \r
335                 /*case kVK_ANSI_Minus:          return GHOST_kKeyMinus;\r
336                 case kVK_ANSI_Equal:            return GHOST_kKeyEqual;\r
337                 case kVK_ANSI_Comma:            return GHOST_kKeyComma;\r
338                 case kVK_ANSI_Period:           return GHOST_kKeyPeriod;\r
339                 case kVK_ANSI_Slash:            return GHOST_kKeySlash;\r
340                 case kVK_ANSI_Semicolon:        return GHOST_kKeySemicolon;\r
341                 case kVK_ANSI_Quote:            return GHOST_kKeyQuote;\r
342                 case kVK_ANSI_Backslash:        return GHOST_kKeyBackslash;\r
343                 case kVK_ANSI_LeftBracket:      return GHOST_kKeyLeftBracket;\r
344                 case kVK_ANSI_RightBracket:     return GHOST_kKeyRightBracket;\r
345                 case kVK_ANSI_Grave:            return GHOST_kKeyAccentGrave;*/\r
346                         \r
347                 case kVK_VolumeUp:\r
348                 case kVK_VolumeDown:\r
349                 case kVK_Mute:\r
350                         return GHOST_kKeyUnknown;\r
351                         \r
352                 default:\r
353                         /*Then detect on character value for "remappable" keys in int'l keyboards*/\r
354                         if ((recvChar >= 'A') && (recvChar <= 'Z')) {\r
355                                 return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA);\r
356                         } else if ((recvChar >= 'a') && (recvChar <= 'z')) {\r
357                                 return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA);\r
358                         } else\r
359                         switch (recvChar) {\r
360                                 case '-':       return GHOST_kKeyMinus;\r
361                                 case '=':       return GHOST_kKeyEqual;\r
362                                 case ',':       return GHOST_kKeyComma;\r
363                                 case '.':       return GHOST_kKeyPeriod;\r
364                                 case '/':       return GHOST_kKeySlash;\r
365                                 case ';':       return GHOST_kKeySemicolon;\r
366                                 case '\'':      return GHOST_kKeyQuote;\r
367                                 case '\\':      return GHOST_kKeyBackslash;\r
368                                 case '[':       return GHOST_kKeyLeftBracket;\r
369                                 case ']':       return GHOST_kKeyRightBracket;\r
370                                 case '`':       return GHOST_kKeyAccentGrave;\r
371                                 default:\r
372                                         return GHOST_kKeyUnknown;\r
373                         }\r
374         }\r
375         return GHOST_kKeyUnknown;\r
376 }\r
377 \r
378 \r
379 #pragma mark defines for 10.6 api not documented in 10.5
380 #ifndef MAC_OS_X_VERSION_10_6
381 enum {
382         /* The following event types are available on some hardware on 10.5.2 and later */
383         NSEventTypeGesture          = 29,
384         NSEventTypeMagnify          = 30,
385         NSEventTypeSwipe            = 31,
386         NSEventTypeRotate           = 18,
387         NSEventTypeBeginGesture     = 19,
388         NSEventTypeEndGesture       = 20
389 };
390
391 @interface NSEvent(GestureEvents)
392 /* This message is valid for events of type NSEventTypeMagnify, on 10.5.2 or later */
393 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
394 - (float)magnification;       // change in magnification.
395 #else
396 - (CGFloat)magnification;       // change in magnification.
397 #endif
398 @end 
399
400 #endif
401
402
403 #pragma mark Utility functions
404
405 #define FIRSTFILEBUFLG 512\r
406 static bool g_hasFirstFile = false;\r
407 static char g_firstFileBuf[512];\r
408 \r
409 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true\r
410 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { \r
411         if (g_hasFirstFile) {\r
412                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);\r
413                 buf[FIRSTFILEBUFLG - 1] = '\0';\r
414                 return 1;\r
415         } else {\r
416                 return 0; \r
417         }\r
418 }\r
419 \r
420 #if defined(WITH_QUICKTIME) && !defined(USE_QTKIT)
421 //Need to place this quicktime function in an ObjC file
422 //It is used to avoid memory leak when raising the quicktime "compression settings" standard dialog
423 extern "C" {
424         struct bContext;
425         struct wmOperator;
426         extern int fromcocoa_request_qtcodec_settings(bContext *C, wmOperator *op);
427 \r
428
429 int cocoa_request_qtcodec_settings(bContext *C, wmOperator *op)
430 {
431         int result;
432         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
433         
434         result = fromcocoa_request_qtcodec_settings(C, op);
435         
436         [pool drain];
437         return result;
438 }
439 };
440 #endif
441
442
443 #pragma mark Cocoa objects\r
444 \r
445 /**\r
446  * CocoaAppDelegate\r
447  * ObjC object to capture applicationShouldTerminate, and send quit event\r
448  **/\r
449 @interface CocoaAppDelegate : NSObject {\r
450         GHOST_SystemCocoa *systemCocoa;\r
451 }\r
452 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
453 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
454 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;\r
455 - (void)applicationWillTerminate:(NSNotification *)aNotification;\r
456 - (void)applicationWillBecomeActive:(NSNotification *)aNotification;
457 @end\r
458 \r
459 @implementation CocoaAppDelegate : NSObject\r
460 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa\r
461 {\r
462         systemCocoa = sysCocoa;\r
463 }\r
464 \r
465 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
466 {
467         return systemCocoa->handleOpenDocumentRequest(filename);
468 }
469
470 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender\r
471 {\r
472         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled\r
473         //Note that Cmd+Q is already handled by keyhandler\r
474     if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)\r
475                 return NSTerminateCancel;//NSTerminateNow;\r
476         else\r
477                 return NSTerminateCancel;\r
478 }\r
479 \r
480 // To avoid cancelling a log off process, we must use Cocoa termination process\r
481 // And this function is the only chance to perform clean up\r
482 // So WM_exit needs to be called directly, as the event loop will never run before termination\r
483 - (void)applicationWillTerminate:(NSNotification *)aNotification\r
484 {\r
485         /*G.afbreek = 0; //Let Cocoa perform the termination at the end\r
486         WM_exit(C);*/\r
487 }\r
488
489 - (void)applicationWillBecomeActive:(NSNotification *)aNotification
490 {
491         systemCocoa->handleApplicationBecomeActiveEvent();
492 }
493 @end\r
494 \r
495 \r
496 \r
497 #pragma mark initialization/finalization\r
498 \r
499 \r
500 GHOST_SystemCocoa::GHOST_SystemCocoa()\r
501 {\r
502         int mib[2];
503         struct timeval boottime;
504         size_t len;
505         char *rstring = NULL;
506         
507         m_modifierMask =0;\r
508         m_pressedMouseButtons =0;\r
509         m_isGestureInProgress = false;
510         m_cursorDelta_x=0;
511         m_cursorDelta_y=0;
512         m_outsideLoopEventProcessed = false;
513         m_needDelayedApplicationBecomeActiveEventProcessing = false;
514         m_displayManager = new GHOST_DisplayManagerCocoa ();\r
515         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");\r
516         m_displayManager->initialize();\r
517 \r
518         //NSEvent timeStamp is given in system uptime, state start date is boot time\r
519         mib[0] = CTL_KERN;\r
520         mib[1] = KERN_BOOTTIME;\r
521         len = sizeof(struct timeval);\r
522         \r
523         sysctl(mib, 2, &boottime, &len, NULL, 0);\r
524         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));\r
525         \r
526         //Detect multitouch trackpad
527         mib[0] = CTL_HW;
528         mib[1] = HW_MODEL;
529         sysctl( mib, 2, NULL, &len, NULL, 0 );
530         rstring = (char*)malloc( len );
531         sysctl( mib, 2, rstring, &len, NULL, 0 );
532         
533         //Hack on MacBook revision, as multitouch avail. function missing
534         if (strstr(rstring,"MacBookAir") ||
535                 (strstr(rstring,"MacBook") && (rstring[strlen(rstring)-3]>='5') && (rstring[strlen(rstring)-3]<='9')))
536                 m_hasMultiTouchTrackpad = true;
537         else m_hasMultiTouchTrackpad = false;
538         
539         free( rstring );
540         rstring = NULL;
541         
542         m_ignoreWindowSizedMessages = false;\r
543 }\r
544 \r
545 GHOST_SystemCocoa::~GHOST_SystemCocoa()\r
546 {\r
547 }\r
548 \r
549 \r
550 GHOST_TSuccess GHOST_SystemCocoa::init()\r
551 {\r
552         \r
553     GHOST_TSuccess success = GHOST_System::init();\r
554     if (success) {\r
555                 //ProcessSerialNumber psn;\r
556                 \r
557                 //Carbon stuff to move window & menu to foreground\r
558                 /*if (!GetCurrentProcess(&psn)) {\r
559                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);\r
560                         SetFrontProcess(&psn);\r
561                 }*/\r
562                 \r
563                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r
564                 if (NSApp == nil) {\r
565                         [NSApplication sharedApplication];\r
566                         \r
567                         if ([NSApp mainMenu] == nil) {\r
568                                 NSMenu *mainMenubar = [[NSMenu alloc] init];\r
569                                 NSMenuItem *menuItem;\r
570                                 NSMenu *windowMenu;\r
571                                 NSMenu *appMenu;\r
572                                 \r
573                                 //Create the application menu\r
574                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];\r
575                                 \r
576                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];\r
577                                 [appMenu addItem:[NSMenuItem separatorItem]];\r
578                                 \r
579                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];\r
580                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];\r
581                                  \r
582                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];\r
583                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];\r
584                                 \r
585                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];\r
586                                 \r
587                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];\r
588                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];\r
589                                 \r
590                                 menuItem = [[NSMenuItem alloc] init];\r
591                                 [menuItem setSubmenu:appMenu];\r
592                                 \r
593                                 [mainMenubar addItem:menuItem];\r
594                                 [menuItem release];\r
595                                 [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5\r
596                                 [appMenu release];\r
597                                 \r
598                                 //Create the window menu\r
599                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];\r
600                                 \r
601                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];\r
602                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];\r
603                                 \r
604                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];\r
605                                 \r
606                                 menuItem = [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
607                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
608                                 
609                                 menuItem = [[NSMenuItem alloc] init];\r
610                                 [menuItem setSubmenu:windowMenu];\r
611                                 \r
612                                 [mainMenubar addItem:menuItem];\r
613                                 [menuItem release];\r
614                                 \r
615                                 [NSApp setMainMenu:mainMenubar];\r
616                                 [NSApp setWindowsMenu:windowMenu];\r
617                                 [windowMenu release];\r
618                         }\r
619                 }\r
620                 if ([NSApp delegate] == nil) {\r
621                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];\r
622                         [appDelegate setSystemCocoa:this];\r
623                         [NSApp setDelegate:appDelegate];\r
624                 }\r
625                 
626                 [NSApp finishLaunching];
627                 
628                 [pool drain];\r
629     }\r
630     return success;\r
631 }\r
632 \r
633 \r
634 #pragma mark window management\r
635 \r
636 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const\r
637 {\r
638         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])\r
639         struct timeval currentTime;
640         \r
641         gettimeofday(&currentTime, NULL);
642         \r
643         //Return timestamp of system uptime
644         
645         return ((currentTime.tv_sec*1000)+(currentTime.tv_usec/1000)-m_start_time);
646 }\r
647 \r
648 \r
649 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const\r
650 {\r
651         //Note that OS X supports monitor hot plug\r
652         // We do not support multiple monitors at the moment\r
653         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r
654 \r
655         GHOST_TUns8 count = [[NSScreen screens] count];\r
656 \r
657         [pool drain];\r
658         return count;\r
659 }\r
660 \r
661 \r
662 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const\r
663 {\r
664         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r
665         //Get visible frame, that is frame excluding dock and top menu bar\r
666         NSRect frame = [[NSScreen mainScreen] visibleFrame];\r
667         \r
668         //Returns max window contents (excluding title bar...)\r
669         NSRect contentRect = [NSWindow contentRectForFrameRect:frame\r
670                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];\r
671         \r
672         width = contentRect.size.width;\r
673         height = contentRect.size.height;\r
674 \r
675         [pool drain];\r
676 }\r
677 \r
678 \r
679 GHOST_IWindow* GHOST_SystemCocoa::createWindow(\r
680         const STR_String& title, \r
681         GHOST_TInt32 left,\r
682         GHOST_TInt32 top,\r
683         GHOST_TUns32 width,\r
684         GHOST_TUns32 height,\r
685         GHOST_TWindowState state,\r
686         GHOST_TDrawingContextType type,\r
687         bool stereoVisual,\r
688         const GHOST_TUns16 numOfAASamples,
689         const GHOST_TEmbedderWindowID parentWindow\r
690 )\r
691 {\r
692     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r
693         GHOST_IWindow* window = 0;\r
694         \r
695         //Get the available rect for including window contents\r
696         NSRect frame = [[NSScreen mainScreen] visibleFrame];\r
697         NSRect contentRect = [NSWindow contentRectForFrameRect:frame\r
698                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];\r
699         \r
700         //Ensures window top left is inside this available rect\r
701         left = left > contentRect.origin.x ? left : contentRect.origin.x;\r
702         top = top > contentRect.origin.y ? top : contentRect.origin.y;\r
703         \r
704         window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
705 \r
706     if (window) {\r
707         if (window->getValid()) {\r
708             // Store the pointer to the window \r
709             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");\r
710             m_windowManager->addWindow(window);\r
711             m_windowManager->setActiveWindow(window);\r
712                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)\r
713             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));\r
714                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));\r
715 \r
716         }\r
717         else {\r
718                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");\r
719             delete window;\r
720             window = 0;\r
721         }\r
722     }\r
723         else {\r
724                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");\r
725         }\r
726         [pool drain];\r
727     return window;\r
728 }\r
729 \r
730 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)\r
731 {       \r
732         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();\r
733 \r
734         *window = currentWindow;\r
735         \r
736         return currentWindow->setState(GHOST_kWindowStateFullScreen);\r
737 }\r
738 \r
739 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)\r
740 {       \r
741         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();\r
742         \r
743         return currentWindow->setState(GHOST_kWindowStateNormal);\r
744 }\r
745 \r
746 \r
747         \r
748 /**
749  * @note : returns coordinates in Cocoa screen coordinates
750  */
751 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const\r
752 {\r
753     NSPoint mouseLoc = [NSEvent mouseLocation];\r
754         \r
755     // Returns the mouse location in screen coordinates\r
756     x = (GHOST_TInt32)mouseLoc.x;\r
757     y = (GHOST_TInt32)mouseLoc.y;\r
758     return GHOST_kSuccess;\r
759 }\r
760 \r
761 /**
762  * @note : expect Cocoa screen coordinates
763  */
764 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const\r
765 {\r
766         float xf=(float)x, yf=(float)y;\r
767         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
768         NSScreen *windowScreen = window->getScreen();
769         NSRect screenRect = [windowScreen frame];
770         \r
771         //Set position relative to current screen
772         xf -= screenRect.origin.x;
773         yf -= screenRect.origin.y;
774         
775         //Quartz Display Services uses the old coordinates (top left origin)\r
776         yf = screenRect.size.height -yf;
777 \r
778         CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
779
780     return GHOST_kSuccess;\r
781 }\r
782 \r
783 \r
784 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const\r
785 {\r
786         keys.set(GHOST_kModifierKeyCommand, (m_modifierMask & NSCommandKeyMask) ? true : false);
787         keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSAlternateKeyMask) ? true : false);
788         keys.set(GHOST_kModifierKeyLeftShift, (m_modifierMask & NSShiftKeyMask) ? true : false);
789         keys.set(GHOST_kModifierKeyLeftControl, (m_modifierMask & NSControlKeyMask) ? true : false);
790         \r
791     return GHOST_kSuccess;\r
792 }\r
793 \r
794 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const\r
795 {\r
796         buttons.clear();\r
797     buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);\r
798         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);\r
799         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);\r
800         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);\r
801         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);\r
802     return GHOST_kSuccess;\r
803 }\r
804 \r
805 \r
806 \r
807 #pragma mark Event handlers\r
808 \r
809 /**\r
810  * The event queue polling function\r
811  */\r
812 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)\r
813 {\r
814         bool anyProcessed = false;\r
815         NSEvent *event;\r
816         \r
817         //      SetMouseCoalescingEnabled(false, NULL);\r
818         //TODO : implement timer ??\r
819         \r
820         /*do {\r
821                 GHOST_TimerManager* timerMgr = getTimerManager();\r
822                 \r
823                  if (waitForEvent) {\r
824                  GHOST_TUns64 next = timerMgr->nextFireTime();\r
825                  double timeOut;\r
826                  \r
827                  if (next == GHOST_kFireTimeNever) {\r
828                  timeOut = kEventDurationForever;\r
829                  } else {\r
830                  timeOut = (double)(next - getMilliSeconds())/1000.0;\r
831                  if (timeOut < 0.0)\r
832                  timeOut = 0.0;\r
833                  }\r
834                  \r
835                  ::ReceiveNextEvent(0, NULL, timeOut, false, &event);\r
836                  }\r
837                  \r
838                  if (timerMgr->fireTimers(getMilliSeconds())) {\r
839                  anyProcessed = true;\r
840                  }*/\r
841                 \r
842                 do {\r
843                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];\r
844                         event = [NSApp nextEventMatchingMask:NSAnyEventMask\r
845                                                                            untilDate:[NSDate distantPast]\r
846                                                                                   inMode:NSDefaultRunLoopMode\r
847                                                                                  dequeue:YES];\r
848                         if (event==nil) {\r
849                                 [pool drain];\r
850                                 break;\r
851                         }\r
852                         \r
853                         anyProcessed = true;\r
854                         \r
855                         switch ([event type]) {\r
856                                 case NSKeyDown:\r
857                                 case NSKeyUp:\r
858                                 case NSFlagsChanged:\r
859                                         handleKeyEvent(event);\r
860                                         \r
861                                         /* Support system-wide keyboard shortcuts, like Expos√©, ...) =>included in always NSApp sendEvent */
862                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {\r
863                                          [NSApp sendEvent:event];\r
864                                          }*/\r
865                                         break;\r
866                                         \r
867                                 case NSLeftMouseDown:\r
868                                 case NSLeftMouseUp:\r
869                                 case NSRightMouseDown:\r
870                                 case NSRightMouseUp:\r
871                                 case NSMouseMoved:\r
872                                 case NSLeftMouseDragged:\r
873                                 case NSRightMouseDragged:\r
874                                 case NSScrollWheel:\r
875                                 case NSOtherMouseDown:\r
876                                 case NSOtherMouseUp:\r
877                                 case NSOtherMouseDragged:
878                                 case NSEventTypeMagnify:
879                                 case NSEventTypeRotate:
880                                 case NSEventTypeBeginGesture:
881                                 case NSEventTypeEndGesture:
882                                         handleMouseEvent(event);\r
883                                         break;\r
884                                         \r
885                                 case NSTabletPoint:\r
886                                 case NSTabletProximity:\r
887                                         handleTabletEvent(event,[event type]);\r
888                                         break;\r
889                                         \r
890                                         /* Trackpad features, fired only from OS X 10.5.2
891                                          case NSEventTypeGesture:\r
892                                          case NSEventTypeSwipe:\r
893                                          break; */\r
894                                         \r
895                                         /*Unused events\r
896                                          NSMouseEntered       = 8,\r
897                                          NSMouseExited        = 9,\r
898                                          NSAppKitDefined      = 13,\r
899                                          NSSystemDefined      = 14,\r
900                                          NSApplicationDefined = 15,\r
901                                          NSPeriodic           = 16,\r
902                                          NSCursorUpdate       = 17,*/\r
903                                         \r
904                                 default:\r
905                                         break;\r
906                         }\r
907                         //Resend event to NSApp to ensure Mac wide events are handled\r
908                         [NSApp sendEvent:event];\r
909                         [pool drain];\r
910                 } while (event!= nil);          \r
911         //} while (waitForEvent && !anyProcessed); Needed only for timer implementation\r
912         \r
913         if (m_needDelayedApplicationBecomeActiveEventProcessing) handleApplicationBecomeActiveEvent();
914         
915         if (m_outsideLoopEventProcessed) {
916                 m_outsideLoopEventProcessed = false;
917                 return true;
918         }
919         \r
920     return anyProcessed;
921 }
922
923 //Note: called from NSApplication delegate
924 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
925 {
926         //Update the modifiers key mask, as its status may have changed when the application was not active
927         //(that is when update events are sent to another application)
928         unsigned int modifiers;
929         GHOST_IWindow* window = m_windowManager->getActiveWindow();
930         
931         if (!window) {
932                 m_needDelayedApplicationBecomeActiveEventProcessing = true;
933                 return GHOST_kFailure;
934         }
935         else m_needDelayedApplicationBecomeActiveEventProcessing = false;
936
937         modifiers = [[[NSApplication sharedApplication] currentEvent] modifierFlags];
938         \r
939         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
940                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
941         }
942         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
943                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
944         }
945         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
946                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
947         }
948         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
949                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
950         }
951         
952         m_modifierMask = modifiers;
953         
954         m_outsideLoopEventProcessed = true;
955         return GHOST_kSuccess;
956 }\r
957 \r
958 //Note: called from NSWindow delegate\r
959 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)\r
960 {\r
961         if (!validWindow(window)) {\r
962                 return GHOST_kFailure;\r
963         }\r
964                 switch(eventType) \r
965                 {\r
966                         case GHOST_kEventWindowClose:\r
967                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );\r
968                                 break;\r
969                         case GHOST_kEventWindowActivate:\r
970                                 m_windowManager->setActiveWindow(window);\r
971                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());\r
972                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );\r
973                                 break;\r
974                         case GHOST_kEventWindowDeactivate:\r
975                                 m_windowManager->setWindowInactive(window);\r
976                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );\r
977                                 break;\r
978                         case GHOST_kEventWindowUpdate:\r
979                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );\r
980                                 break;\r
981                         case GHOST_kEventWindowMove:
982                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) );
983                                 break;
984                         case GHOST_kEventWindowSize:\r
985                                 if (!m_ignoreWindowSizedMessages)\r
986                                 {\r
987                                         window->updateDrawingContext();\r
988                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );\r
989                                 }\r
990                                 break;\r
991                         default:\r
992                                 return GHOST_kFailure;\r
993                                 break;\r
994                 }\r
995         
996         m_outsideLoopEventProcessed = true;
997         return GHOST_kSuccess;\r
998 }\r
999 \r
1000 //Note: called from NSWindow subclass
1001 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,
1002                                                                    GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data)
1003 {
1004         if (!validWindow(window)) {
1005                 return GHOST_kFailure;
1006         }
1007         switch(eventType) 
1008         {
1009                 case GHOST_kEventDraggingEntered:
1010                 case GHOST_kEventDraggingUpdated:
1011                 case GHOST_kEventDraggingExited:
1012                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL));
1013                         break;
1014                         
1015                 case GHOST_kEventDraggingDropDone:
1016                 {
1017                         GHOST_TUns8 * temp_buff;
1018                         GHOST_TStringArray *strArray;
1019                         NSArray *droppedArray;
1020                         size_t pastedTextSize;  
1021                         NSString *droppedStr;
1022                         GHOST_TEventDataPtr eventData;
1023                         int i;
1024
1025                         if (!data) return GHOST_kFailure;
1026                         
1027                         switch (draggedObjectType) {
1028                                 case GHOST_kDragnDropTypeFilenames:
1029                                         droppedArray = (NSArray*)data;
1030                                         
1031                                         strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray));
1032                                         if (!strArray) return GHOST_kFailure;
1033                                         
1034                                         strArray->count = [droppedArray count];
1035                                         if (strArray->count == 0) return GHOST_kFailure;
1036                                         
1037                                         strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*));
1038                                         
1039                                         for (i=0;i<strArray->count;i++)
1040                                         {
1041                                                 droppedStr = [droppedArray objectAtIndex:i];
1042                                                 
1043                                                 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1044                                                 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1045                                         
1046                                                 if (!temp_buff) {
1047                                                         strArray->count = i;
1048                                                         break;
1049                                                 }
1050                                         
1051                                                 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1052                                                 temp_buff[pastedTextSize] = '\0';
1053                                                 
1054                                                 strArray->strings[i] = temp_buff;
1055                                         }
1056
1057                                         eventData = (GHOST_TEventDataPtr) strArray;     
1058                                         break;
1059                                         
1060                                 case GHOST_kDragnDropTypeString:
1061                                         droppedStr = (NSString*)data;
1062                                         pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1063                                         
1064                                         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1065                                         
1066                                         if (temp_buff == NULL) {
1067                                                 return GHOST_kFailure;
1068                                         }
1069                                         
1070                                         strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1071                                         
1072                                         temp_buff[pastedTextSize] = '\0';
1073                                         
1074                                         eventData = (GHOST_TEventDataPtr) temp_buff;
1075                                         break;
1076                                 
1077                                 case GHOST_kDragnDropTypeBitmap:
1078                                 {
1079                                         NSImage *droppedImg = (NSImage*)data;
1080                                         NSSize imgSize = [droppedImg size];
1081                                         ImBuf *ibuf = NULL;
1082                                         GHOST_TUns8 *rasterRGB = NULL;
1083                                         GHOST_TUns8 *rasterRGBA = NULL;
1084                                         GHOST_TUns8 *toIBuf = NULL;
1085                                         int x, y, to_i, from_i;
1086                                         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
1087                                         NSEnumerator *enumerator;
1088                                         NSImageRep *representation;
1089                                         
1090                                         ibuf = IMB_allocImBuf (imgSize.width , imgSize.height, 32, IB_rect, 0);
1091                                         if (!ibuf) {
1092                                                 [droppedImg release];
1093                                                 return GHOST_kFailure;
1094                                         }
1095                                         
1096                                         /*Get the bitmap of the image*/
1097                                         enumerator = [[droppedImg representations] objectEnumerator];
1098                                         while ((representation = [enumerator nextObject])) {
1099                                                 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
1100                                                         bitmapImage = (NSBitmapImageRep *)representation;
1101                                                         break;
1102                                                 }
1103                                         }
1104                                         if (bitmapImage == nil) return GHOST_kFailure;
1105                                         
1106                                         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
1107                                                 && ![bitmapImage isPlanar]) {
1108                                                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
1109                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1110                                                 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData];
1111                                                 for (y = 0; y < imgSize.height; y++) {
1112                                                         to_i = (imgSize.height-y-1)*imgSize.width;
1113                                                         from_i = y*imgSize.width;
1114                                                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width);
1115                                                 }
1116                                         }
1117                                         else {
1118                                                 /* Tell cocoa image resolution is same as current system one */
1119                                                 [bitmapImage setSize:imgSize];
1120                                                 
1121                                                 /* Convert the image in a RGBA 32bit format */
1122                                                 /* As Core Graphics does not support contextes with non premutliplied alpha,
1123                                                  we need to get alpha key values in a separate batch */
1124                                                 
1125                                                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
1126                                                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1127                                                                                                                                                                                  pixelsWide:imgSize.width 
1128                                                                                                                                                                                  pixelsHigh:imgSize.height
1129                                                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
1130                                                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
1131                                                                                                                                                                            bitmapFormat:(NSBitmapFormat)0
1132                                                                                                                                                                                 bytesPerRow:4*imgSize.width
1133                                                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
1134                                                 
1135                                                 [NSGraphicsContext saveGraphicsState];
1136                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
1137                                                 [bitmapImage draw];
1138                                                 [NSGraphicsContext restoreGraphicsState];
1139                                                 
1140                                                 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData];
1141                                                 if (rasterRGB == NULL) {
1142                                                         [bitmapImage release];
1143                                                         [blBitmapFormatImageRGB release];
1144                                                         [droppedImg release];
1145                                                         return GHOST_kFailure;
1146                                                 }
1147                                                 
1148                                                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
1149                                                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1150                                                                                                                                                                                   pixelsWide:imgSize.width
1151                                                                                                                                                                                   pixelsHigh:imgSize.height
1152                                                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
1153                                                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
1154                                                                                                                                                                                 bitmapFormat:(NSBitmapFormat)0
1155                                                                                                                                                                                  bytesPerRow:4*imgSize.width
1156                                                                                                                                                                                 bitsPerPixel:32/* RGBA */];
1157                                                 
1158                                                 [NSGraphicsContext saveGraphicsState];
1159                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
1160                                                 [bitmapImage draw];
1161                                                 [NSGraphicsContext restoreGraphicsState];
1162                                                 
1163                                                 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData];
1164                                                 if (rasterRGBA == NULL) {
1165                                                         [bitmapImage release];
1166                                                         [blBitmapFormatImageRGB release];
1167                                                         [blBitmapFormatImageRGBA release];
1168                                                         [droppedImg release];
1169                                                         return GHOST_kFailure;
1170                                                 }
1171                                                 
1172                                                 /*Copy the image to ibuf, flipping it vertically*/
1173                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1174                                                 for (y = 0; y < imgSize.height; y++) {
1175                                                         for (x = 0; x < imgSize.width; x++) {
1176                                                                 to_i = (imgSize.height-y-1)*imgSize.width + x;
1177                                                                 from_i = y*imgSize.width + x;
1178                                                                 
1179                                                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
1180                                                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
1181                                                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
1182                                                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
1183                                                         }
1184                                                 }
1185                                                 
1186                                                 [blBitmapFormatImageRGB release];
1187                                                 [blBitmapFormatImageRGBA release];
1188                                                 [droppedImg release];
1189                                         }
1190                                         
1191                                         eventData = (GHOST_TEventDataPtr) ibuf;
1192                                 }
1193                                         break;
1194                                         
1195                                 default:
1196                                         return GHOST_kFailure;
1197                                         break;
1198                         }
1199                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData));
1200                 }
1201                         break;
1202                 default:
1203                         return GHOST_kFailure;
1204         }
1205         m_outsideLoopEventProcessed = true;
1206         return GHOST_kSuccess;
1207 }
1208
1209
1210 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()\r
1211 {\r
1212         GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1213         
1214         //Discard quit event if we are in cursor grab sequence
1215         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1216                 return GHOST_kExitCancel;
1217         
1218         //Check open windows if some changes are not saved\r
1219         if (m_windowManager->getAnyModifiedState())\r
1220         {\r
1221                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?",
1222                                                                                  @"Cancel", @"Quit Anyway", nil);
1223                 if (shouldQuit == NSAlertAlternateReturn)\r
1224                 {\r
1225                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );\r
1226                         return GHOST_kExitNow;\r
1227                 } else {
1228                         //Give back focus to the blender window if user selected cancel quit
1229                         NSArray *windowsList = [NSApp orderedWindows];
1230                         if ([windowsList count]) {
1231                                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1232                         }
1233                 }\r
1234
1235         }\r
1236         else {\r
1237                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );\r
1238                 m_outsideLoopEventProcessed = true;
1239                 return GHOST_kExitNow;\r
1240         }\r
1241         \r
1242         return GHOST_kExitCancel;\r
1243 }\r
1244 \r
1245 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
1246 {
1247         NSString *filepath = (NSString*)filepathStr;
1248         int confirmOpen = NSAlertAlternateReturn;
1249         NSArray *windowsList;
1250         char * temp_buff;
1251         size_t filenameTextSize;        
1252         GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow();
1253         
1254         if (!window) {
1255                 return NO;
1256         }       
1257         
1258         //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised
1259         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1260                 return GHOST_kExitCancel;
1261
1262         //Check open windows if some changes are not saved
1263         if (m_windowManager->getAnyModifiedState())
1264         {
1265                 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]],
1266                                                                                  @"Current document has not been saved.\nDo you really want to proceed?",
1267                                                                                  @"Cancel", @"Open", nil);
1268         }
1269 \r
1270         //Give back focus to the blender window
1271         windowsList = [NSApp orderedWindows];
1272         if ([windowsList count]) {
1273                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1274         }
1275
1276         if (confirmOpen == NSAlertAlternateReturn)
1277         {
1278                 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1279                 
1280                 temp_buff = (char*) malloc(filenameTextSize+1); 
1281                 
1282                 if (temp_buff == NULL) {
1283                         return GHOST_kFailure;
1284                 }
1285                 
1286                 strncpy(temp_buff, [filepath cStringUsingEncoding:NSISOLatin1StringEncoding], filenameTextSize);
1287                 
1288                 temp_buff[filenameTextSize] = '\0';
1289
1290                 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff));
1291
1292                 return YES;
1293         }
1294         else return NO;
1295 }
1296
1297 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)\r
1298 {\r
1299         NSEvent *event = (NSEvent *)eventPtr;\r
1300         GHOST_IWindow* window;
1301         
1302         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1303         if (!window) {
1304                 //printf("\nW failure for event 0x%x",[event type]);
1305                 return GHOST_kFailure;
1306         }
1307         
1308         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();\r
1309         \r
1310         switch (eventType) {\r
1311                 case NSTabletPoint:\r
1312                         ct.Pressure = [event pressure];
1313                         ct.Xtilt = [event tilt].x;\r
1314                         ct.Ytilt = [event tilt].y;\r
1315                         break;\r
1316                 \r
1317                 case NSTabletProximity:\r
1318                         ct.Pressure = 0;\r
1319                         ct.Xtilt = 0;\r
1320                         ct.Ytilt = 0;\r
1321                         if ([event isEnteringProximity])\r
1322                         {\r
1323                                 //pointer is entering tablet area proximity\r
1324                                 switch ([event pointingDeviceType]) {\r
1325                                         case NSPenPointingDevice:\r
1326                                                 ct.Active = GHOST_kTabletModeStylus;\r
1327                                                 break;\r
1328                                         case NSEraserPointingDevice:\r
1329                                                 ct.Active = GHOST_kTabletModeEraser;\r
1330                                                 break;\r
1331                                         case NSCursorPointingDevice:\r
1332                                         case NSUnknownPointingDevice:\r
1333                                         default:\r
1334                                                 ct.Active = GHOST_kTabletModeNone;\r
1335                                                 break;\r
1336                                 }\r
1337                         } else {\r
1338                                 // pointer is leaving - return to mouse\r
1339                                 ct.Active = GHOST_kTabletModeNone;\r
1340                         }\r
1341                         break;\r
1342                 \r
1343                 default:\r
1344                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");\r
1345                         return GHOST_kFailure;\r
1346                         break;\r
1347         }\r
1348         return GHOST_kSuccess;\r
1349 }\r
1350 \r
1351 \r
1352 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)\r
1353 {\r
1354         NSEvent *event = (NSEvent *)eventPtr;\r
1355     GHOST_Window* window;
1356         \r
1357         window = (GHOST_Window*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1358         if (!window) {\r
1359                 //printf("\nW failure for event 0x%x",[event type]);
1360                 return GHOST_kFailure;\r
1361         }\r
1362         \r
1363         switch ([event type])\r
1364     {\r
1365                 case NSLeftMouseDown:\r
1366                 case NSRightMouseDown:\r
1367                 case NSOtherMouseDown:\r
1368                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1369                         //Handle tablet events combined with mouse events\r
1370                         switch ([event subtype]) {\r
1371                                 case NX_SUBTYPE_TABLET_POINT:\r
1372                                         handleTabletEvent(eventPtr, NSTabletPoint);\r
1373                                         break;\r
1374                                 case NX_SUBTYPE_TABLET_PROXIMITY:\r
1375                                         handleTabletEvent(eventPtr, NSTabletProximity);\r
1376                                         break;\r
1377                                 default:\r
1378                                         //No tablet event included : do nothing\r
1379                                         break;\r
1380                         }\r
1381                         break;\r
1382                                                 \r
1383                 case NSLeftMouseUp:\r
1384                 case NSRightMouseUp:\r
1385                 case NSOtherMouseUp:\r
1386                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1387                         //Handle tablet events combined with mouse events\r
1388                         switch ([event subtype]) {\r
1389                                 case NX_SUBTYPE_TABLET_POINT:\r
1390                                         handleTabletEvent(eventPtr, NSTabletPoint);\r
1391                                         break;\r
1392                                 case NX_SUBTYPE_TABLET_PROXIMITY:\r
1393                                         handleTabletEvent(eventPtr, NSTabletProximity);\r
1394                                         break;\r
1395                                 default:\r
1396                                         //No tablet event included : do nothing\r
1397                                         break;\r
1398                         }\r
1399                         break;\r
1400                         \r
1401                 case NSLeftMouseDragged:\r
1402                 case NSRightMouseDragged:\r
1403                 case NSOtherMouseDragged:                               \r
1404                         //Handle tablet events combined with mouse events\r
1405                         switch ([event subtype]) {\r
1406                                 case NX_SUBTYPE_TABLET_POINT:\r
1407                                         handleTabletEvent(eventPtr, NSTabletPoint);\r
1408                                         break;\r
1409                                 case NX_SUBTYPE_TABLET_PROXIMITY:\r
1410                                         handleTabletEvent(eventPtr, NSTabletProximity);\r
1411                                         break;\r
1412                                 default:\r
1413                                         //No tablet event included : do nothing\r
1414                                         break;\r
1415                         }\r
1416                         
1417                 case NSMouseMoved:\r
1418                                 switch (window->getCursorGrabMode()) {
1419                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1420                                         {
1421                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
1422                                                 
1423                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1424                                                 
1425                                                 window->getCursorGrabAccum(x_accum, y_accum);
1426                                                 x_accum += [event deltaX];
1427                                                 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1428                                                 window->setCursorGrabAccum(x_accum, y_accum);
1429                                                 
1430                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
1431                                         }
1432                                                 break;
1433                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1434                                         {
1435                                                 NSPoint mousePos = [event locationInWindow];
1436                                                 GHOST_TInt32 x_mouse= mousePos.x;
1437                                                 GHOST_TInt32 y_mouse= mousePos.y;
1438                                                 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur;
1439                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1440                                                 
1441                                                 /* fallback to window bounds */
1442                                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
1443                                                         window->getClientBounds(bounds);
1444                                                 
1445                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1446                                                 window->getClientBounds(windowBounds);
1447                                                 window->screenToClient(bounds.m_l,bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1448                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1449                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1450                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1451                                                 
1452                                                 //Update accumulation counts
1453                                                 window->getCursorGrabAccum(x_accum, y_accum);
1454                                                 x_accum += [event deltaX]-m_cursorDelta_x;
1455                                                 y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1456                                                 window->setCursorGrabAccum(x_accum, y_accum);
1457                                                 
1458                                                 
1459                                                 //Warp mouse cursor if needed
1460                                                 x_mouse += [event deltaX]-m_cursorDelta_x;
1461                                                 y_mouse += -[event deltaY]-m_cursorDelta_y;
1462                                                 correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
1463                                                 
1464                                                 //Compensate for mouse moved event taking cursor position set into account
1465                                                 m_cursorDelta_x = x_mouse-mousePos.x;
1466                                                 m_cursorDelta_y = y_mouse-mousePos.y;
1467                                                 
1468                                                 //Set new cursor position
1469                                                 window->clientToScreen(x_mouse, y_mouse, x_cur, y_cur);
1470                                                 setCursorPosition(x_cur, y_cur); /* wrap */
1471                                                 
1472                                                 //Post event
1473                                                 window->getCursorGrabInitPos(x_cur, y_cur);
1474                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_cur + x_accum, y_cur + y_accum));
1475                                         }
1476                                                 break;
1477                                         default:
1478                                         {
1479                                                 //Normal cursor operation: send mouse position in window
1480                                                 NSPoint mousePos = [event locationInWindow];
1481                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1482                                                 m_cursorDelta_x=0;
1483                                                 m_cursorDelta_y=0; //Mouse motion occured between two cursor warps, so we can reset the delta counter
1484                                         }
1485                                                 break;
1486                                 }\r
1487                                 break;\r
1488                         \r
1489                 case NSScrollWheel:\r
1490                         {\r
1491                                 /* Send trackpad event if inside a trackpad gesture, send wheel event otherwise */
1492                                 if (!m_hasMultiTouchTrackpad || !m_isGestureInProgress) {
1493                                         GHOST_TInt32 delta;
1494                                         
1495                                         double deltaF = [event deltaY];
1496                                         if (deltaF == 0.0) break; //discard trackpad delta=0 events
1497                                         
1498                                         delta = deltaF > 0.0 ? 1 : -1;
1499                                         pushEvent(new GHOST_EventWheel([event timestamp]*1000, window, delta));
1500                                 }
1501                                 else {
1502                                         NSPoint mousePos = [event locationInWindow];
1503                                         double dx = [event deltaX];
1504                                         double dy = -[event deltaY];
1505                                         
1506                                         const double deltaMax = 50.0;
1507                                         
1508                                         if ((dx == 0) && (dy == 0)) break;
1509                                         
1510                                         /* Quadratic acceleration */
1511                                         dx = dx*(fabs(dx)+0.5);
1512                                         if (dx<0.0) dx-=0.5; else dx+=0.5;
1513                                         if (dx< -deltaMax) dx= -deltaMax; else if (dx>deltaMax) dx=deltaMax;
1514                                         
1515                                         dy = dy*(fabs(dy)+0.5);
1516                                         if (dy<0.0) dy-=0.5; else dy+=0.5;
1517                                         if (dy< -deltaMax) dy= -deltaMax; else if (dy>deltaMax) dy=deltaMax;
1518
1519                                         pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventScroll, mousePos.x, mousePos.y, dx, dy));
1520                                 }
1521                         }\r
1522                         break;\r
1523                         \r
1524                 case NSEventTypeMagnify:
1525                         {
1526                                 NSPoint mousePos = [event locationInWindow];
1527                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventMagnify, mousePos.x, mousePos.y,
1528                                                                                                   [event magnification]*250.0 + 0.1, 0));
1529                         }
1530                         break;
1531
1532                 case NSEventTypeRotate:
1533                         {
1534                                 NSPoint mousePos = [event locationInWindow];
1535                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventRotate, mousePos.x, mousePos.y,
1536                                                                                                   -[event rotation] * 5.0, 0));
1537                         }
1538                 case NSEventTypeBeginGesture:
1539                         m_isGestureInProgress = true;
1540                         break;
1541                 case NSEventTypeEndGesture:
1542                         m_isGestureInProgress = false;
1543                         break;
1544                 default:\r
1545                         return GHOST_kFailure;\r
1546                         break;\r
1547                 }\r
1548         \r
1549         return GHOST_kSuccess;\r
1550 }\r
1551 \r
1552 \r
1553 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)\r
1554 {\r
1555         NSEvent *event = (NSEvent *)eventPtr;\r
1556         GHOST_IWindow* window;
1557         unsigned int modifiers;\r
1558         NSString *characters;\r
1559         NSData *convertedCharacters;
1560         GHOST_TKey keyCode;\r
1561         unsigned char ascii;\r
1562         NSString* charsIgnoringModifiers;
1563 \r
1564         window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1565         if (!window) {\r
1566                 //printf("\nW failure for event 0x%x",[event type]);
1567                 return GHOST_kFailure;\r
1568         }\r
1569         \r
1570         switch ([event type]) {\r
1571                 case NSKeyDown:\r
1572                 case NSKeyUp:\r
1573                         charsIgnoringModifiers = [event charactersIgnoringModifiers];
1574                         if ([charsIgnoringModifiers length]>0)
1575                                 keyCode = convertKey([event keyCode],\r
1576                                                                          [charsIgnoringModifiers characterAtIndex:0]);
1577                         else
1578                                 keyCode = convertKey([event keyCode],0);\r
1579
1580                                 
1581                         characters = [event characters];
1582                         if ([characters length]>0) { //Check for dead keys
1583                                 //Convert characters to iso latin 1 encoding
1584                                 convertedCharacters = [characters dataUsingEncoding:NSISOLatin1StringEncoding];
1585                                 if ([convertedCharacters length]>0)
1586                                         ascii =((char*)[convertedCharacters bytes])[0];
1587                                 else
1588                                         ascii = 0; //Character not available in iso latin 1 encoding
1589                         }\r
1590                         else
1591                                 ascii= 0;
1592                         \r
1593                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))\r
1594                                 break; //Cmd-Q is directly handled by Cocoa\r
1595 \r
1596                         if ([event type] == NSKeyDown) {\r
1597                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii) );
1598                                 //printf("\nKey pressed keyCode=%u ascii=%i %c",keyCode,ascii,ascii);\r
1599                         } else {\r
1600                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii) );
1601                         }\r
1602                         break;\r
1603         \r
1604                 case NSFlagsChanged: \r
1605                         modifiers = [event modifierFlags];\r
1606                         
1607                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {\r
1608                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1609                         }\r
1610                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {\r
1611                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1612                         }\r
1613                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {\r
1614                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1615                         }\r
1616                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {\r
1617                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1618                         }\r
1619                         \r
1620                         m_modifierMask = modifiers;\r
1621                         break;\r
1622                         \r
1623                 default:\r
1624                         return GHOST_kFailure;\r
1625                         break;\r
1626         }\r
1627         \r
1628         return GHOST_kSuccess;\r
1629 }\r
1630 \r
1631 \r
1632 \r
1633 #pragma mark Clipboard get/set\r
1634 \r
1635 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const\r
1636 {\r
1637         GHOST_TUns8 * temp_buff;\r
1638         size_t pastedTextSize;  \r
1639         \r
1640         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r
1641         \r
1642         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];\r
1643         \r
1644         if (pasteBoard == nil) {\r
1645                 [pool drain];\r
1646                 return NULL;\r
1647         }\r
1648         \r
1649         NSArray *supportedTypes =\r
1650                 [NSArray arrayWithObjects: NSStringPboardType, nil];
1651         \r
1652         NSString *bestType = [[NSPasteboard generalPasteboard]\r
1653                                                   availableTypeFromArray:supportedTypes];\r
1654         \r
1655         if (bestType == nil) {\r
1656                 [pool drain];\r
1657                 return NULL;\r
1658         }\r
1659         \r
1660         NSString * textPasted = [pasteBoard stringForType:NSStringPboardType];
1661 \r
1662         if (textPasted == nil) {\r
1663                 [pool drain];\r
1664                 return NULL;\r
1665         }\r
1666         \r
1667         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1668         \r
1669         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); \r
1670 \r
1671         if (temp_buff == NULL) {\r
1672                 [pool drain];\r
1673                 return NULL;\r
1674         }\r
1675         \r
1676         strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1677         \r
1678         temp_buff[pastedTextSize] = '\0';\r
1679         \r
1680         [pool drain];\r
1681 \r
1682         if(temp_buff) {\r
1683                 return temp_buff;\r
1684         } else {\r
1685                 return NULL;\r
1686         }\r
1687 }\r
1688 \r
1689 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const\r
1690 {\r
1691         NSString *textToCopy;\r
1692         \r
1693         if(selection) {return;} // for copying the selection, used on X11\r
1694 \r
1695         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];\r
1696                 \r
1697         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];\r
1698         \r
1699         if (pasteBoard == nil) {\r
1700                 [pool drain];\r
1701                 return;\r
1702         }\r
1703         \r
1704         NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
1705         \r
1706         [pasteBoard declareTypes:supportedTypes owner:nil];\r
1707         \r
1708         textToCopy = [NSString stringWithCString:buffer encoding:NSISOLatin1StringEncoding];
1709         \r
1710         [pasteBoard setString:textToCopy forType:NSStringPboardType];
1711         \r
1712         [pool drain];\r
1713 }\r
1714
1715 #pragma mark Base directories retrieval
1716
1717 const GHOST_TUns8* GHOST_SystemCocoa::getSystemDir() const
1718 {
1719         static GHOST_TUns8 tempPath[512] = "";
1720         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1721         NSFileManager *fileManager;
1722         NSString *basePath;
1723         NSArray *paths;
1724         
1725         paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSLocalDomainMask, YES);
1726         
1727         if ([paths count] > 0)
1728                 basePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Blender"];
1729         else { //Fall back to standard unix path in case of issue
1730                 basePath = @"/usr/share/blender";
1731         }
1732         
1733         /* Ensure path exists, creates it if needed */
1734         fileManager = [NSFileManager defaultManager];
1735         if (![fileManager fileExistsAtPath:basePath isDirectory:NULL]) {
1736                 [fileManager createDirectoryAtPath:basePath attributes:nil];
1737         }
1738         
1739         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1740         
1741         [pool drain];
1742         return tempPath;
1743 }
1744
1745 const GHOST_TUns8* GHOST_SystemCocoa::getUserDir() const
1746 {
1747         static GHOST_TUns8 tempPath[512] = "";
1748         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1749         NSFileManager *fileManager;
1750         NSString *basePath;
1751         NSArray *paths;
1752
1753         paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
1754
1755         if ([paths count] > 0)
1756                 basePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Blender"];
1757         else { //Fall back to HOME in case of issue
1758                 basePath = [NSHomeDirectory() stringByAppendingPathComponent:@".blender"];
1759         }
1760         
1761         /* Ensure path exists, creates it if needed */
1762         fileManager = [NSFileManager defaultManager];
1763         if (![fileManager fileExistsAtPath:basePath isDirectory:NULL]) {
1764                 [fileManager createDirectoryAtPath:basePath attributes:nil];
1765         }
1766         
1767         strcpy((char*)tempPath, [basePath cStringUsingEncoding:NSASCIIStringEncoding]);
1768         
1769         [pool drain];
1770         return tempPath;
1771 }