]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CIrrDeviceOSX.mm
SDL: Support clipboard
[irrlicht.git] / source / Irrlicht / CIrrDeviceOSX.mm
1 // Copyright (C) 2005-2006 Etienne Petitjean
2 // Copyright (C) 2007-2012 Christian Stehno
3 // Copyright (C) 2013-2015 Patryk Nadrowski
4 // This file is part of the "Irrlicht Engine".
5 // For conditions of distribution and use, see copyright notice in Irrlicht.h
6
7
8 #ifdef _IRR_COMPILE_WITH_OSX_DEVICE_
9
10 #import <Cocoa/Cocoa.h>
11 #import <OpenGL/gl.h>
12
13 #include "CIrrDeviceOSX.h"
14
15 #include "IEventReceiver.h"
16 #include "os.h"
17 #include "CTimer.h"
18 #include "irrString.h"
19 #include "Keycodes.h"
20 #include <stdio.h>
21 #include <sys/utsname.h>
22 #include "COSOperator.h"
23 #include "CColorConverter.h"
24 #include "irrlicht.h"
25 #include <algorithm>
26
27 #include <wchar.h>
28 #include <time.h>
29
30 #include "CNSOGLManager.h"
31
32 #if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
33
34 #include <IOKit/IOKitLib.h>
35 #include <IOKit/IOCFPlugIn.h>
36 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
37 #include <IOKit/hid/IOHIDLib.h>
38 #include <IOKit/hid/IOHIDKeys.h>
39
40 struct JoystickComponent
41 {
42         IOHIDElementCookie cookie; // unique value which identifies element, will NOT change
43         long min; // reported min value possible
44         long max; // reported max value possible
45
46         long minRead; //min read value
47         long maxRead; //max read value
48
49         JoystickComponent() : min(0), minRead(0), max(0), maxRead(0)
50         {
51         }
52 };
53
54 struct JoystickInfo
55 {
56         irr::core::array <JoystickComponent> axisComp;
57         irr::core::array <JoystickComponent> buttonComp;
58         irr::core::array <JoystickComponent> hatComp;
59
60         int hats;
61         int axes;
62         int buttons;
63         int numActiveJoysticks;
64
65         irr::SEvent persistentData;
66
67         IOHIDDeviceInterface ** interface;
68         bool removed;
69         char joystickName[256];
70         long usage; // usage page from IOUSBHID Parser.h which defines general usage
71         long usagePage; // usage within above page from IOUSBHID Parser.h which defines specific usage
72
73         JoystickInfo() : hats(0), axes(0), buttons(0), interface(0), removed(false), usage(0), usagePage(0), numActiveJoysticks(0)
74         {
75                 interface = NULL;
76                 memset(joystickName, '\0', 256);
77                 axisComp.clear();
78                 buttonComp.clear();
79                 hatComp.clear();
80
81                 persistentData.EventType = irr::EET_JOYSTICK_INPUT_EVENT;
82                 persistentData.JoystickEvent.POV = 65535;
83                 persistentData.JoystickEvent.ButtonStates = 0;
84         }
85 };
86 irr::core::array<JoystickInfo> ActiveJoysticks;
87
88 //helper functions for init joystick
89 static IOReturn closeJoystickDevice (JoystickInfo* joyInfo)
90 {
91         IOReturn result = kIOReturnSuccess;
92         if (joyInfo && joyInfo->interface)
93         {
94                 /* close the interface */
95                 result = (*(joyInfo->interface))->close (joyInfo->interface);
96                 if (kIOReturnNotOpen == result)
97                 {
98                         /* do nothing as device was not opened, thus can't be closed */
99                 }
100                 else if (kIOReturnSuccess != result)
101                         irr::os::Printer::log("IOHIDDeviceInterface failed to close", irr::ELL_ERROR);
102                 /* release the interface */
103                 result = (*(joyInfo->interface))->Release (joyInfo->interface);
104                 if (kIOReturnSuccess != result)
105                         irr::os::Printer::log("IOHIDDeviceInterface failed to release", irr::ELL_ERROR);
106                 joyInfo->interface = NULL;
107         }
108         return result;
109 }
110
111 static void addComponentInfo (CFTypeRef refElement, JoystickComponent *pComponent, int numActiveJoysticks)
112 {
113         long number;
114         CFTypeRef refType;
115
116         refType = CFDictionaryGetValue ((CFDictionaryRef)refElement, CFSTR(kIOHIDElementCookieKey));
117         if (refType && CFNumberGetValue ((CFNumberRef)refType, kCFNumberLongType, &number))
118                 pComponent->cookie = (IOHIDElementCookie) number;
119         refType = CFDictionaryGetValue ((CFDictionaryRef)refElement, CFSTR(kIOHIDElementMinKey));
120         if (refType && CFNumberGetValue ((CFNumberRef)refType, kCFNumberLongType, &number))
121                 pComponent->minRead = pComponent->min = number;
122         refType = CFDictionaryGetValue ((CFDictionaryRef)refElement, CFSTR(kIOHIDElementMaxKey));
123         if (refType && CFNumberGetValue ((CFNumberRef)refType, kCFNumberLongType, &number))
124                 pComponent->maxRead = pComponent->max = number;
125 }
126
127 static void getJoystickComponentArrayHandler (const void * value, void * parameter);
128
129 static void addJoystickComponent (CFTypeRef refElement, JoystickInfo* joyInfo)
130 {
131         long elementType, usagePage, usage;
132         CFTypeRef refElementType = CFDictionaryGetValue ((CFDictionaryRef)refElement, CFSTR(kIOHIDElementTypeKey));
133         CFTypeRef refUsagePage = CFDictionaryGetValue ((CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsagePageKey));
134         CFTypeRef refUsage = CFDictionaryGetValue ((CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsageKey));
135
136         if ((refElementType) && (CFNumberGetValue ((CFNumberRef)refElementType, kCFNumberLongType, &elementType)))
137         {
138                 /* look at types of interest */
139                 if ((elementType == kIOHIDElementTypeInput_Misc) || (elementType == kIOHIDElementTypeInput_Button) ||
140                         (elementType == kIOHIDElementTypeInput_Axis))
141                 {
142                         if (refUsagePage && CFNumberGetValue ((CFNumberRef)refUsagePage, kCFNumberLongType, &usagePage) &&
143                                 refUsage && CFNumberGetValue ((CFNumberRef)refUsage, kCFNumberLongType, &usage))
144                         {
145                                 switch (usagePage) /* only interested in kHIDPage_GenericDesktop and kHIDPage_Button */
146                                 {
147                                         case kHIDPage_GenericDesktop:
148                                         {
149                                                 switch (usage) /* look at usage to determine function */
150                                                 {
151                                                         case kHIDUsage_GD_X:
152                                                         case kHIDUsage_GD_Y:
153                                                         case kHIDUsage_GD_Z:
154                                                         case kHIDUsage_GD_Rx:
155                                                         case kHIDUsage_GD_Ry:
156                                                         case kHIDUsage_GD_Rz:
157                                                         case kHIDUsage_GD_Slider:
158                                                         case kHIDUsage_GD_Dial:
159                                                         case kHIDUsage_GD_Wheel:
160                                                         {
161                                                                 joyInfo->axes++;
162                                                                 JoystickComponent newComponent;
163                                                                 addComponentInfo(refElement, &newComponent, joyInfo->numActiveJoysticks);
164                                                                 joyInfo->axisComp.push_back(newComponent);
165                                                         }
166                                                         break;
167                                                         case kHIDUsage_GD_Hatswitch:
168                                                         {
169                                                                 joyInfo->hats++;
170                                                                 JoystickComponent newComponent;
171                                                                 addComponentInfo(refElement, &newComponent, joyInfo->numActiveJoysticks);
172                                                                 joyInfo->hatComp.push_back(newComponent);
173                                                         }
174                                                         break;
175                                                 }
176                                         }
177                                                 break;
178                                         case kHIDPage_Button:
179                                         {
180                                                 joyInfo->buttons++;
181                                                 JoystickComponent newComponent;
182                                                 addComponentInfo(refElement, &newComponent, joyInfo->numActiveJoysticks);
183                                                 joyInfo->buttonComp.push_back(newComponent);
184                                         }
185                                                 break;
186                                         default:
187                                                 break;
188                                 }
189                         }
190                 }
191                 else if (kIOHIDElementTypeCollection == elementType)
192                 {
193                         //get elements
194                         CFTypeRef refElementTop = CFDictionaryGetValue ((CFMutableDictionaryRef) refElement, CFSTR(kIOHIDElementKey));
195                         if (refElementTop)
196                         {
197                                 CFTypeID type = CFGetTypeID (refElementTop);
198                                 if (type == CFArrayGetTypeID())
199                                 {
200                                         CFRange range = {0, CFArrayGetCount ((CFArrayRef)refElementTop)};
201                                         CFArrayApplyFunction ((CFArrayRef)refElementTop, range, getJoystickComponentArrayHandler, joyInfo);
202                                 }
203                         }
204                 }
205         }
206 }
207
208 static void getJoystickComponentArrayHandler (const void * value, void * parameter)
209 {
210         if (CFGetTypeID (value) == CFDictionaryGetTypeID ())
211                 addJoystickComponent ((CFTypeRef) value, (JoystickInfo *) parameter);
212 }
213
214 static void joystickTopLevelElementHandler (const void * value, void * parameter)
215 {
216         CFTypeRef refCF = 0;
217         if (CFGetTypeID (value) != CFDictionaryGetTypeID ())
218                 return;
219         refCF = CFDictionaryGetValue ((CFDictionaryRef)value, CFSTR(kIOHIDElementUsagePageKey));
220         if (!CFNumberGetValue ((CFNumberRef)refCF, kCFNumberLongType, &((JoystickInfo *) parameter)->usagePage))
221                 irr::os::Printer::log("CFNumberGetValue error retrieving JoystickInfo->usagePage", irr::ELL_ERROR);
222         refCF = CFDictionaryGetValue ((CFDictionaryRef)value, CFSTR(kIOHIDElementUsageKey));
223         if (!CFNumberGetValue ((CFNumberRef)refCF, kCFNumberLongType, &((JoystickInfo *) parameter)->usage))
224                 irr::os::Printer::log("CFNumberGetValue error retrieving JoystickInfo->usage", irr::ELL_ERROR);
225 }
226
227 static void getJoystickDeviceInfo (io_object_t hidDevice, CFMutableDictionaryRef hidProperties, JoystickInfo *joyInfo)
228 {
229         CFMutableDictionaryRef usbProperties = 0;
230         io_registry_entry_t parent1, parent2;
231
232         /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
233         * get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
234         */
235         if ((KERN_SUCCESS == IORegistryEntryGetParentEntry (hidDevice, kIOServicePlane, &parent1)) &&
236                 (KERN_SUCCESS == IORegistryEntryGetParentEntry (parent1, kIOServicePlane, &parent2)) &&
237                 (KERN_SUCCESS == IORegistryEntryCreateCFProperties (parent2, &usbProperties, kCFAllocatorDefault, kNilOptions)))
238         {
239                 if (usbProperties)
240                 {
241                         CFTypeRef refCF = 0;
242                         /* get device info
243                         * try hid dictionary first, if fail then go to usb dictionary
244                         */
245
246                         /* get joystickName name */
247                         refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDProductKey));
248                         if (!refCF)
249                                 refCF = CFDictionaryGetValue (usbProperties, CFSTR("USB Product Name"));
250                         if (refCF)
251                         {
252                                 if (!CFStringGetCString ((CFStringRef)refCF, joyInfo->joystickName, 256, CFStringGetSystemEncoding ()))
253                                         irr::os::Printer::log("CFStringGetCString error getting joyInfo->joystickName", irr::ELL_ERROR);
254                         }
255
256                         /* get usage page and usage */
257                         refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey));
258                         if (refCF)
259                         {
260                                 if (!CFNumberGetValue ((CFNumberRef)refCF, kCFNumberLongType, &joyInfo->usagePage))
261                                         irr::os::Printer::log("CFNumberGetValue error getting joyInfo->usagePage", irr::ELL_ERROR);
262                                 refCF = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
263                                 if (refCF)
264                                         if (!CFNumberGetValue ((CFNumberRef)refCF, kCFNumberLongType, &joyInfo->usage))
265                                                 irr::os::Printer::log("CFNumberGetValue error getting joyInfo->usage", irr::ELL_ERROR);
266                         }
267
268                         if (NULL == refCF) /* get top level element HID usage page or usage */
269                         {
270                                 /* use top level element instead */
271                                 CFTypeRef refCFTopElement = 0;
272                                 refCFTopElement = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey));
273                                 {
274                                         /* refCFTopElement points to an array of element dictionaries */
275                                         CFRange range = {0, CFArrayGetCount ((CFArrayRef)refCFTopElement)};
276                                         CFArrayApplyFunction ((CFArrayRef)refCFTopElement, range, joystickTopLevelElementHandler, joyInfo);
277                                 }
278                         }
279
280                         CFRelease (usbProperties);
281                 }
282                 else
283                         irr::os::Printer::log("IORegistryEntryCreateCFProperties failed to create usbProperties", irr::ELL_ERROR);
284
285                 if (kIOReturnSuccess != IOObjectRelease (parent2))
286                         irr::os::Printer::log("IOObjectRelease failed to release parent2", irr::ELL_ERROR);
287                 if (kIOReturnSuccess != IOObjectRelease (parent1))
288                         irr::os::Printer::log("IOObjectRelease failed to release parent1", irr::ELL_ERROR);
289         }
290 }
291
292 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
293
294 // Contents from Events.h from Carbon/HIToolbox but we need it with Cocoa too
295 // and for some reason no Cocoa equivalent of these constants seems provided.
296 // So I'm doing like everyone else and using copy-and-paste.
297
298 /*
299  *  Summary:
300  *      Virtual keycodes
301  *
302  *  Discussion:
303  *      These constants are the virtual keycodes defined originally in
304  *      Inside Mac Volume V, pg. V-191. They identify physical keys on a
305  *      keyboard. Those constants with "ANSI" in the name are labeled
306  *      according to the key position on an ANSI-standard US keyboard.
307  *      For example, kVK_ANSI_A indicates the virtual keycode for the key
308  *      with the letter 'A' in the US keyboard layout. Other keyboard
309  *      layouts may have the 'A' key label on a different physical key;
310  *      in this case, pressing 'A' will generate a different virtual
311  *      keycode.
312  */
313 enum {
314         kVK_ANSI_A              = 0x00,
315         kVK_ANSI_S              = 0x01,
316         kVK_ANSI_D              = 0x02,
317         kVK_ANSI_F              = 0x03,
318         kVK_ANSI_H              = 0x04,
319         kVK_ANSI_G              = 0x05,
320         kVK_ANSI_Z              = 0x06,
321         kVK_ANSI_X              = 0x07,
322         kVK_ANSI_C              = 0x08,
323         kVK_ANSI_V              = 0x09,
324         kVK_ANSI_B              = 0x0B,
325         kVK_ANSI_Q              = 0x0C,
326         kVK_ANSI_W              = 0x0D,
327         kVK_ANSI_E              = 0x0E,
328         kVK_ANSI_R              = 0x0F,
329         kVK_ANSI_Y              = 0x10,
330         kVK_ANSI_T              = 0x11,
331         kVK_ANSI_1              = 0x12,
332         kVK_ANSI_2              = 0x13,
333         kVK_ANSI_3              = 0x14,
334         kVK_ANSI_4              = 0x15,
335         kVK_ANSI_6              = 0x16,
336         kVK_ANSI_5              = 0x17,
337         kVK_ANSI_Equal          = 0x18,
338         kVK_ANSI_9              = 0x19,
339         kVK_ANSI_7              = 0x1A,
340         kVK_ANSI_Minus          = 0x1B,
341         kVK_ANSI_8              = 0x1C,
342         kVK_ANSI_0              = 0x1D,
343         kVK_ANSI_RightBracket   = 0x1E,
344         kVK_ANSI_O              = 0x1F,
345         kVK_ANSI_U              = 0x20,
346         kVK_ANSI_LeftBracket    = 0x21,
347         kVK_ANSI_I              = 0x22,
348         kVK_ANSI_P              = 0x23,
349         kVK_ANSI_L              = 0x25,
350         kVK_ANSI_J              = 0x26,
351         kVK_ANSI_Quote          = 0x27,
352         kVK_ANSI_K              = 0x28,
353         kVK_ANSI_Semicolon      = 0x29,
354         kVK_ANSI_Backslash      = 0x2A,
355         kVK_ANSI_Comma          = 0x2B,
356         kVK_ANSI_Slash          = 0x2C,
357         kVK_ANSI_N              = 0x2D,
358         kVK_ANSI_M              = 0x2E,
359         kVK_ANSI_Period         = 0x2F,
360         kVK_ANSI_Grave          = 0x32,
361         kVK_ANSI_KeypadDecimal  = 0x41,
362         kVK_ANSI_KeypadMultiply = 0x43,
363         kVK_ANSI_KeypadPlus     = 0x45,
364         kVK_ANSI_KeypadClear    = 0x47,
365         kVK_ANSI_KeypadDivide   = 0x4B,
366         kVK_ANSI_KeypadEnter    = 0x4C,
367         kVK_ANSI_KeypadMinus    = 0x4E,
368         kVK_ANSI_KeypadEquals   = 0x51,
369         kVK_ANSI_Keypad0        = 0x52,
370         kVK_ANSI_Keypad1        = 0x53,
371         kVK_ANSI_Keypad2        = 0x54,
372         kVK_ANSI_Keypad3        = 0x55,
373         kVK_ANSI_Keypad4        = 0x56,
374         kVK_ANSI_Keypad5        = 0x57,
375         kVK_ANSI_Keypad6        = 0x58,
376         kVK_ANSI_Keypad7        = 0x59,
377         kVK_ANSI_Keypad8        = 0x5B,
378         kVK_ANSI_Keypad9        = 0x5C
379 };
380
381 /* keycodes for keys that are independent of keyboard layout*/
382 enum {
383         kVK_Return              = 0x24,
384         kVK_Tab                 = 0x30,
385         kVK_Space               = 0x31,
386         kVK_Delete              = 0x33,
387         kVK_Escape              = 0x35,
388         kVK_Command             = 0x37,
389         kVK_Shift               = 0x38,
390         kVK_CapsLock            = 0x39,
391         kVK_Option              = 0x3A,
392         kVK_Control             = 0x3B,
393         kVK_RightShift          = 0x3C,
394         kVK_RightOption         = 0x3D,
395         kVK_RightControl        = 0x3E,
396         kVK_Function            = 0x3F,
397         kVK_F17                 = 0x40,
398         kVK_VolumeUp            = 0x48,
399         kVK_VolumeDown          = 0x49,
400         kVK_Mute                = 0x4A,
401         kVK_F18                 = 0x4F,
402         kVK_F19                 = 0x50,
403         kVK_F20                 = 0x5A,
404         kVK_F5                  = 0x60,
405         kVK_F6                  = 0x61,
406         kVK_F7                  = 0x62,
407         kVK_F3                  = 0x63,
408         kVK_F8                  = 0x64,
409         kVK_F9                  = 0x65,
410         kVK_F11                 = 0x67,
411         kVK_F13                 = 0x69,
412         kVK_F16                 = 0x6A,
413         kVK_F14                 = 0x6B,
414         kVK_F10                 = 0x6D,
415         kVK_F12                 = 0x6F,
416         kVK_F15                 = 0x71,
417         kVK_Help                = 0x72,
418         kVK_Home                = 0x73,
419         kVK_PageUp              = 0x74,
420         kVK_ForwardDelete       = 0x75,
421         kVK_F4                  = 0x76,
422         kVK_End                 = 0x77,
423         kVK_F2                  = 0x78,
424         kVK_PageDown            = 0x79,
425         kVK_F1                  = 0x7A,
426         kVK_LeftArrow           = 0x7B,
427         kVK_RightArrow          = 0x7C,
428         kVK_DownArrow           = 0x7D,
429         kVK_UpArrow             = 0x7E
430 };
431
432 //------------------------------------------------------------------------------------------
433 Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key)
434 {
435         // get a boolean from the dictionary
436         Boolean value = false;
437         CFBooleanRef boolRef;
438         boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key);
439         if (boolRef != NULL)
440                 value = CFBooleanGetValue(boolRef);
441         return value;
442 }
443 //------------------------------------------------------------------------------------------
444 long GetDictionaryLong(CFDictionaryRef theDict, const void* key)
445 {
446         // get a long from the dictionary
447         long value = 0;
448         CFNumberRef numRef;
449         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
450         if (numRef != NULL)
451                 CFNumberGetValue(numRef, kCFNumberLongType, &value);
452         return value;
453 }
454
455 namespace irr
456 {
457         namespace video
458         {
459                 IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& param, io::IFileSystem* io, IContextManager* contextManager);
460         }
461 } // end namespace irr
462
463 static bool firstLaunch = true;
464
465 @implementation CIrrDelegateOSX
466 {
467     irr::CIrrDeviceMacOSX* Device;
468     bool Quit;
469 }
470
471 - (id)initWithDevice:(irr::CIrrDeviceMacOSX*)device
472 {
473     self = [super init];
474     
475     if (self)
476         Device = device;
477     
478     Quit = false;
479     
480     return (self);
481 }
482
483 - (void)applicationDidFinishLaunching:(NSNotification*)notification
484 {
485     Quit = false;
486 }
487
488 - (void)orderFrontStandardAboutPanel:(id)sender
489 {
490     [NSApp orderFrontStandardAboutPanel:sender];
491 }
492
493 - (void)unhideAllApplications:(id)sender
494 {
495     [NSApp unhideAllApplications:sender];
496 }
497
498 - (void)hide:(id)sender
499 {
500     [NSApp hide:sender];
501 }
502
503 - (void)hideOtherApplications:(id)sender
504 {
505     [NSApp hideOtherApplications:sender];
506 }
507
508 - (void)terminate:(id)sender
509 {
510     Quit = true;
511 }
512
513 - (void)windowWillClose:(id)sender
514 {
515     Device->setWindow(nil);
516     Quit = true;
517 }
518
519 - (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize
520 {
521     if (Device->isResizable())
522         return proposedFrameSize;
523     else
524         return [window frame].size;
525 }
526
527 - (void)windowDidResize:(NSNotification *)aNotification
528 {
529     NSWindow    *window;
530     NSRect              frame;
531     
532     window = [aNotification object];
533     frame = [window frame];
534     Device->setResize((int)frame.size.width,(int)frame.size.height);
535 }
536
537 - (BOOL)isQuit
538 {
539     return (Quit);
540 }
541
542 @end
543
544 namespace irr
545 {
546 //! constructor
547 CIrrDeviceMacOSX::CIrrDeviceMacOSX(const SIrrlichtCreationParameters& param)
548         : CIrrDeviceStub(param), Window(NULL), Display(NULL),
549         DeviceWidth(0), DeviceHeight(0),
550         ScreenWidth(0), ScreenHeight(0), MouseButtonStates(0),
551         IsActive(true), IsFullscreen(false), IsShiftDown(false), IsControlDown(false), IsResizable(false)
552 {
553         struct utsname name;
554
555 #ifdef _DEBUG
556         setDebugName("CIrrDeviceMacOSX");
557 #endif
558
559         if (firstLaunch)
560         {
561                 firstLaunch = false;
562
563                 if (!CreationParams.WindowId)
564                 {
565                         [[NSAutoreleasePool alloc] init];
566                         [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
567                         [NSApp setDelegate:(id<NSApplicationDelegate>)[[[CIrrDelegateOSX alloc] initWithDevice:this] autorelease]];
568
569             // Create menu
570
571             NSString* bundleName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
572             if (bundleName == nil)
573                 bundleName = @"Irrlicht";
574
575             NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@"MainMenu"] autorelease];
576             NSMenu* menu = [[[NSMenu alloc] initWithTitle:bundleName] autorelease];
577             NSMenuItem* menuItem = [mainMenu addItemWithTitle:bundleName action:nil keyEquivalent:@""];
578             [mainMenu setSubmenu:menu forItem:menuItem];
579             menuItem = [menu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@"q"];
580             [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
581
582             [NSApp setMainMenu:mainMenu];
583
584             [NSApp finishLaunching];
585                 }
586
587                 NSString *path = [[NSBundle mainBundle] bundlePath];
588                 if (path != nil) {
589                         path = [path stringByAppendingString:@"/Contents/Resources"];
590                         chdir([path fileSystemRepresentation]);
591                         [path release];
592                 }
593         }
594
595         uname(&name);
596         Operator = new COSOperator(name.version);
597         os::Printer::log(name.version,ELL_INFORMATION);
598
599         initKeycodes();
600
601         bool success = true;
602
603         if (CreationParams.DriverType != video::EDT_NULL)
604                 success = createWindow();
605
606         // in case of failure, one can check VideoDriver for initialization
607         if (!success)
608                 return;
609
610         setResizable(false);
611         CursorControl = new CCursorControl(CreationParams.WindowSize, this);
612
613         createDriver();
614         createGUIAndScene();
615 }
616
617 CIrrDeviceMacOSX::~CIrrDeviceMacOSX()
618 {
619         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
620         closeDevice();
621 #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
622         for (u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
623         {
624                 if (ActiveJoysticks[joystick].interface)
625                         closeJoystickDevice(&ActiveJoysticks[joystick]);
626         }
627 #endif
628 }
629
630 void CIrrDeviceMacOSX::closeDevice()
631 {
632         if (Window != nil)
633         {
634                 [Window setIsVisible:FALSE];
635                 [Window setReleasedWhenClosed:TRUE];
636                 [Window release];
637                 Window = nil;
638         }
639
640         IsFullscreen = false;
641         IsActive = false;
642 }
643
644 bool CIrrDeviceMacOSX::createWindow()
645 {
646     CGDisplayErr error;
647     bool result = false;
648     Display = CGMainDisplayID();
649     
650     CGRect displayRect;
651     CGDisplayModeRef displaymode, olddisplaymode;
652     
653     ScreenWidth = (int)CGDisplayPixelsWide(Display);
654     ScreenHeight = (int)CGDisplayPixelsHigh(Display);
655     
656     const NSBackingStoreType type = (CreationParams.DriverType == video::EDT_OPENGL) ? NSBackingStoreBuffered : NSBackingStoreNonretained;
657     
658     // TODO: fullscreen
659     //if (!CreationParams.Fullscreen)
660     {
661         if (!CreationParams.WindowId) //create another window when WindowId is null
662         {
663             int x = (CreationParams.WindowPosition.X > 0) ? CreationParams.WindowPosition.X : 0;
664             int y = (CreationParams.WindowPosition.Y > 0) ? CreationParams.WindowPosition.Y : 0;
665             
666             if (CreationParams.WindowPosition.Y > -1)
667             {
668                 int screenHeight = [[[NSScreen screens] objectAtIndex:0] frame].size.height;
669                 y = screenHeight - y - CreationParams.WindowSize.Height;
670             }
671             
672             Window = [[NSWindow alloc] initWithContentRect:NSMakeRect(x, y, CreationParams.WindowSize.Width,CreationParams.WindowSize.Height) styleMask:NSTitledWindowMask+NSClosableWindowMask+NSResizableWindowMask backing:type defer:FALSE];
673
674             if (CreationParams.WindowPosition.X == -1 && CreationParams.WindowPosition.Y == -1)
675                 [Window center];
676         }
677         
678         DeviceWidth = CreationParams.WindowSize.Width;
679         DeviceHeight = CreationParams.WindowSize.Height;
680         
681         result = true;
682     }
683     
684     if (result)
685     {
686         if (Window)
687         {
688             [Window setDelegate:(id<NSWindowDelegate>)[NSApp delegate]];
689             [Window setAcceptsMouseMovedEvents:TRUE];
690             [Window setIsVisible:TRUE];
691             [Window makeKeyAndOrderFront:nil];
692         }
693     }
694     
695     return result;
696 }
697
698 void CIrrDeviceMacOSX::setResize(int width, int height)
699 {
700         // set new window size
701         DeviceWidth = width;
702         DeviceHeight = height;
703
704 #if defined(_IRR_COMPILE_WITH_OPENGL_)
705         // update the size of the opengl rendering context
706         if (CreationParams.DriverType == video::EDT_OPENGL)
707     {
708         NSOpenGLContext* Context = (NSOpenGLContext*)ContextManager->getContext().OpenGLOSX.Context;
709         
710         if (Context)
711             [Context update];
712     }
713 #endif
714
715         // resize the driver to the inner pane size
716         if (Window)
717         {
718                 NSRect driverFrame = [Window contentRectForFrameRect:[Window frame]];
719                 getVideoDriver()->OnResize(core::dimension2d<u32>( (s32)driverFrame.size.width, (s32)driverFrame.size.height));
720                 DeviceWidth = (s32)driverFrame.size.width;
721                 DeviceHeight = (s32)driverFrame.size.height;
722         }
723         else
724                 getVideoDriver()->OnResize(core::dimension2d<u32>( (s32)width, (s32)height));
725 }
726
727
728 void CIrrDeviceMacOSX::createDriver()
729 {
730         switch (CreationParams.DriverType)
731         {
732                 case video::EDT_OPENGL:
733 #ifdef _IRR_COMPILE_WITH_OPENGL_
734             {
735                 video::SExposedVideoData data;
736                 data.OpenGLOSX.Window = Window;
737                 ContextManager = new video::CNSOGLManager();
738                 ContextManager->initialize(CreationParams, data);
739                 VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, ContextManager);
740                 if (!VideoDriver)
741                 {
742                     os::Printer::log("Could not create OpenGL driver.", ELL_ERROR);
743                 }
744                 
745                                 if (Window) 
746                                 {
747                                         [[Window contentView] setWantsBestResolutionOpenGLSurface:NO];
748                                         [(NSOpenGLContext*)ContextManager->getContext().OpenGLOSX.Context setView:[Window contentView]];
749                                 }
750                                 else 
751                                 {
752                                         [(NSView*)CreationParams.WindowId setWantsBestResolutionOpenGLSurface:NO];
753                                         [(NSOpenGLContext*)ContextManager->getContext().OpenGLOSX.Context setView:(NSView*)CreationParams.WindowId];
754                                 }
755             }
756 #else
757                         os::Printer::log("No OpenGL support compiled in.", ELL_ERROR);
758 #endif
759                         break;
760
761                 case video::DEPRECATED_EDT_DIRECT3D8_NO_LONGER_EXISTS:
762                 case video::EDT_DIRECT3D9:
763                 case video::EDT_OGLES1:
764                 case video::EDT_OGLES2:
765                         os::Printer::log("This driver is not available in OSX. Try OpenGL or Software renderer.", ELL_ERROR);
766                         break;
767
768                 case video::EDT_NULL:
769                         VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
770                         break;
771
772                 default:
773                         os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
774                         break;
775         }
776 }
777
778 bool CIrrDeviceMacOSX::run()
779 {
780         NSAutoreleasePool* Pool = [[NSAutoreleasePool alloc] init];
781
782         NSEvent *event;
783         irr::SEvent     ievent;
784
785         os::Timer::tick();
786         storeMouseLocation();
787
788         event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
789         if (event != nil)
790         {
791                 bzero(&ievent,sizeof(ievent));
792
793                 switch([(NSEvent *)event type])
794                 {
795                         case NSKeyDown:
796                                 postKeyEvent(event,ievent,true);
797                                 break;
798
799                         case NSKeyUp:
800                                 postKeyEvent(event,ievent,false);
801                                 break;
802
803                         case NSFlagsChanged:
804                                 ievent.EventType = irr::EET_KEY_INPUT_EVENT;
805                                 ievent.KeyInput.Shift = ([(NSEvent *)event modifierFlags] & NSShiftKeyMask) != 0;
806                                 ievent.KeyInput.Control = ([(NSEvent *)event modifierFlags] & NSControlKeyMask) != 0;
807
808                                 if (IsShiftDown != ievent.KeyInput.Shift)
809                                 {
810                                         ievent.KeyInput.Char = irr::KEY_SHIFT;
811                                         ievent.KeyInput.Key = irr::KEY_SHIFT;
812                                         ievent.KeyInput.PressedDown = ievent.KeyInput.Shift;
813
814                                         IsShiftDown = ievent.KeyInput.Shift;
815
816                                         postEventFromUser(ievent);
817                                 }
818
819                                 if (IsControlDown != ievent.KeyInput.Control)
820                                 {
821                                         ievent.KeyInput.Char = irr::KEY_CONTROL;
822                                         ievent.KeyInput.Key = irr::KEY_CONTROL;
823                                         ievent.KeyInput.PressedDown = ievent.KeyInput.Control;
824
825                                         IsControlDown = ievent.KeyInput.Control;
826
827                                         postEventFromUser(ievent);
828                                 }
829
830                                 [NSApp sendEvent:event];
831                                 break;
832
833                         case NSLeftMouseDown:
834                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
835                                 ievent.MouseInput.Event = irr::EMIE_LMOUSE_PRESSED_DOWN;
836                                 MouseButtonStates |= irr::EMBSM_LEFT;
837                                 ievent.MouseInput.ButtonStates = MouseButtonStates;
838                                 postMouseEvent(event,ievent);
839                                 break;
840
841                         case NSLeftMouseUp:
842                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
843                                 MouseButtonStates &= !irr::EMBSM_LEFT;
844                                 ievent.MouseInput.ButtonStates = MouseButtonStates;
845                                 ievent.MouseInput.Event = irr::EMIE_LMOUSE_LEFT_UP;
846                                 postMouseEvent(event,ievent);
847                                 break;
848
849                         case NSOtherMouseDown:
850                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
851                                 ievent.MouseInput.Event = irr::EMIE_MMOUSE_PRESSED_DOWN;
852                                 MouseButtonStates |= irr::EMBSM_MIDDLE;
853                                 ievent.MouseInput.ButtonStates = MouseButtonStates;
854                                 postMouseEvent(event,ievent);
855                                 break;
856
857                         case NSOtherMouseUp:
858                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
859                                 MouseButtonStates &= !irr::EMBSM_MIDDLE;
860                                 ievent.MouseInput.ButtonStates = MouseButtonStates;
861                                 ievent.MouseInput.Event = irr::EMIE_MMOUSE_LEFT_UP;
862                                 postMouseEvent(event,ievent);
863                                 break;
864
865                         case NSMouseMoved:
866                         case NSLeftMouseDragged:
867                         case NSRightMouseDragged:
868                         case NSOtherMouseDragged:
869                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
870                                 ievent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
871                                 ievent.MouseInput.ButtonStates = MouseButtonStates;
872                                 postMouseEvent(event,ievent);
873                                 break;
874
875                         case NSRightMouseDown:
876                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
877                                 ievent.MouseInput.Event = irr::EMIE_RMOUSE_PRESSED_DOWN;
878                                 MouseButtonStates |= irr::EMBSM_RIGHT;
879                                 ievent.MouseInput.ButtonStates = MouseButtonStates;
880                                 postMouseEvent(event,ievent);
881                                 break;
882
883                         case NSRightMouseUp:
884                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
885                                 ievent.MouseInput.Event = irr::EMIE_RMOUSE_LEFT_UP;
886                                 MouseButtonStates &= !irr::EMBSM_RIGHT;
887                                 ievent.MouseInput.ButtonStates = MouseButtonStates;
888                                 postMouseEvent(event,ievent);
889                                 break;
890
891                         case NSScrollWheel:
892                                 ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
893                                 ievent.MouseInput.Event = irr::EMIE_MOUSE_WHEEL;
894                                 ievent.MouseInput.Wheel = [(NSEvent *)event deltaY];
895                                 if (ievent.MouseInput.Wheel < 1.0f)
896                                         ievent.MouseInput.Wheel *= 10.0f;
897                                 else
898                                         ievent.MouseInput.Wheel *= 5.0f;
899                                 postMouseEvent(event,ievent);
900                                 break;
901
902                         default:
903                                 [NSApp sendEvent:event];
904                                 break;
905                 }
906         }
907
908         pollJoysticks();
909
910         [Pool release];
911
912         return (![[NSApp delegate] isQuit] && IsActive);
913 }
914
915
916 //! Pause the current process for the minimum time allowed only to allow other processes to execute
917 void CIrrDeviceMacOSX::yield()
918 {
919         struct timespec ts = {0,0};
920         nanosleep(&ts, NULL);
921 }
922
923
924 //! Pause execution and let other processes to run for a specified amount of time.
925 void CIrrDeviceMacOSX::sleep(u32 timeMs, bool pauseTimer=false)
926 {
927         bool wasStopped = Timer ? Timer->isStopped() : true;
928
929         struct timespec ts;
930         ts.tv_sec = (time_t) (timeMs / 1000);
931         ts.tv_nsec = (long) (timeMs % 1000) * 1000000;
932
933         if (pauseTimer && !wasStopped)
934                 Timer->stop();
935
936         nanosleep(&ts, NULL);
937
938         if (pauseTimer && !wasStopped)
939                 Timer->start();
940 }
941
942
943 void CIrrDeviceMacOSX::setWindowCaption(const wchar_t* text)
944 {
945         if (Window != NULL)
946         {
947                 if ( text )
948                 {
949                         size_t numBytes = wcslen(text) * sizeof(wchar_t);
950
951 #ifdef __BIG_ENDIAN__
952                         NSStringEncoding encode = sizeof(wchar_t) == 4 ? NSUTF32BigEndianStringEncoding : NSUTF16BigEndianStringEncoding;
953 #else
954                         NSStringEncoding encode = sizeof(wchar_t) == 4 ? NSUTF32LittleEndianStringEncoding : NSUTF16LittleEndianStringEncoding;
955 #endif
956                         NSString* name = [[NSString alloc] initWithBytes:text length:numBytes encoding:encode];
957                         if ( name )
958                         {
959                                 [Window setTitle:name];
960                                 [name release];
961                         }
962                 }
963                 else
964                 {
965                         [Window setTitle:@""];
966                 }
967         }
968 }
969
970
971 bool CIrrDeviceMacOSX::isWindowActive() const
972 {
973         return (IsActive);
974 }
975
976
977 bool CIrrDeviceMacOSX::isWindowFocused() const
978 {
979         if (Window != NULL)
980                 return [Window isKeyWindow];
981         return false;
982 }
983
984
985 bool CIrrDeviceMacOSX::isWindowMinimized() const
986 {
987         if (Window != NULL)
988                 return [Window isMiniaturized];
989         return false;
990 }
991
992
993 void CIrrDeviceMacOSX::postKeyEvent(void *event,irr::SEvent &ievent,bool pressed)
994 {
995         NSString *str;
996         std::map<int,int>::const_iterator iter;
997         unsigned int c,mkey,mchar;
998         const unsigned char *cStr;
999         BOOL skipCommand;
1000
1001         str = [(NSEvent *)event characters];
1002         if ((str != nil) && ([str length] > 0))
1003         {
1004                 mkey = mchar = 0;
1005                 skipCommand = false;
1006                 c = [str characterAtIndex:0];
1007                 mchar = c;
1008
1009                 iter = KeyCodes.find([(NSEvent *)event keyCode]);
1010                 if (iter != KeyCodes.end())
1011                         mkey = (*iter).second;
1012                 else if ((iter = KeyCodes.find(c))  != KeyCodes.end())
1013                         mkey = (*iter).second;
1014                 else
1015                 {
1016                         // workaround for period character
1017                         if (c == 0x2E)
1018                         {
1019                                 mkey = irr::KEY_PERIOD;
1020                                 mchar = '.';
1021                         }
1022                         else
1023                         {
1024                                 cStr = (unsigned char *)[str cStringUsingEncoding:NSWindowsCP1252StringEncoding];
1025                                 if (cStr != NULL && strlen((char*)cStr) > 0)
1026                                 {
1027                                         mchar = cStr[0];
1028                                         mkey = toupper(mchar);
1029                                         if ([(NSEvent *)event modifierFlags] & NSCommandKeyMask)
1030                                         {
1031                                                 if (mkey == 'C' || mkey == 'V' || mkey == 'X')
1032                                                 {
1033                                                         mchar = 0;
1034                                                         skipCommand = true;
1035                                                 }
1036                                         }
1037                                 }
1038                         }
1039                 }
1040
1041                 ievent.EventType = irr::EET_KEY_INPUT_EVENT;
1042                 ievent.KeyInput.Key = (irr::EKEY_CODE)mkey;
1043                 ievent.KeyInput.PressedDown = pressed;
1044                 ievent.KeyInput.Shift = ([(NSEvent *)event modifierFlags] & NSShiftKeyMask) != 0;
1045                 ievent.KeyInput.Control = ([(NSEvent *)event modifierFlags] & NSControlKeyMask) != 0;
1046                 ievent.KeyInput.Char = mchar;
1047
1048                 if (skipCommand)
1049                         ievent.KeyInput.Control = true;
1050                 else if ([(NSEvent *)event modifierFlags] & NSCommandKeyMask)
1051                         [NSApp sendEvent:(NSEvent *)event];
1052
1053                 postEventFromUser(ievent);
1054         }
1055 }
1056
1057
1058 void CIrrDeviceMacOSX::postMouseEvent(void *event,irr::SEvent &ievent)
1059 {
1060         bool post = true;
1061
1062         if (Window != NULL)
1063         {
1064                 ievent.MouseInput.X = (int)[(NSEvent *)event locationInWindow].x;
1065                 ievent.MouseInput.Y = DeviceHeight - (int)[(NSEvent *)event locationInWindow].y;
1066
1067                 if (ievent.MouseInput.Y < 0)
1068                         post = false;
1069         }
1070         else
1071         {
1072                 CGEventRef ourEvent = CGEventCreate(NULL);
1073                 CGPoint point = CGEventGetLocation(ourEvent);
1074                 CFRelease(ourEvent);
1075
1076                 ievent.MouseInput.X = (int)point.x;
1077                 ievent.MouseInput.Y = (int)point.y;
1078
1079                 if (ievent.MouseInput.Y < 0)
1080                         post = false;
1081         }
1082
1083         if (post)
1084         {
1085                 ievent.MouseInput.Shift = ([(NSEvent *)event modifierFlags] & NSShiftKeyMask) != 0;
1086                 ievent.MouseInput.Control = ([(NSEvent *)event modifierFlags] & NSControlKeyMask) != 0;
1087                 
1088                 postEventFromUser(ievent);
1089         }
1090
1091         [NSApp sendEvent:(NSEvent *)event];
1092 }
1093
1094
1095 void CIrrDeviceMacOSX::storeMouseLocation()
1096 {
1097         int x,y;
1098
1099         if (Window != NULL)
1100         {
1101                 NSPoint p;
1102                 p = [NSEvent mouseLocation];
1103                 p = [Window convertScreenToBase:p];
1104                 x = (int)p.x;
1105                 y = DeviceHeight - (int)p.y;
1106         }
1107         else
1108         {
1109                 // Do we still need this?
1110                 CGEventRef ourEvent = CGEventCreate(NULL);
1111                 CGPoint point = CGEventGetLocation(ourEvent);
1112                 CFRelease(ourEvent);
1113
1114                 x = (int)point.x;
1115                 y = (int)point.y;
1116
1117                 const core::position2di& curr = ((CCursorControl *)CursorControl)->getPosition(true);
1118                 if (curr.X != x || curr.Y != y)
1119                 {
1120                         irr::SEvent ievent;
1121                         ievent.EventType = irr::EET_MOUSE_INPUT_EVENT;
1122                         ievent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
1123                         ievent.MouseInput.X = x;
1124                         ievent.MouseInput.Y = y;
1125                         postEventFromUser(ievent);
1126                 }
1127         }
1128
1129         ((CCursorControl *)CursorControl)->updateInternalCursorPosition(x,y);
1130 }
1131
1132
1133 void CIrrDeviceMacOSX::setMouseLocation(int x,int y)
1134 {
1135         NSPoint p;
1136         CGPoint c;
1137
1138         if (Window != NULL)
1139         {
1140                 // Irrlicht window exists
1141                 p.x = (float) x;
1142                 p.y = (float) (DeviceHeight - y);
1143                 p = [Window convertBaseToScreen:p];
1144                 p.y = ScreenHeight - p.y;
1145         }
1146         else
1147         {
1148                 p.x = (float) x;
1149                 p.y = (float) y + (ScreenHeight - DeviceHeight);
1150         }
1151
1152         c.x = p.x;
1153         c.y = p.y;
1154
1155         CGWarpMouseCursorPosition(c);
1156         CGAssociateMouseAndMouseCursorPosition(YES);
1157 }
1158
1159
1160 void CIrrDeviceMacOSX::setCursorVisible(bool visible)
1161 {
1162         if (visible)
1163                 CGDisplayShowCursor(CGMainDisplayID());
1164         else
1165                 CGDisplayHideCursor(CGMainDisplayID());
1166 }
1167     
1168     
1169 void CIrrDeviceMacOSX::setWindow(NSWindow* window)
1170 {
1171     Window = window;
1172 }
1173
1174
1175 void CIrrDeviceMacOSX::initKeycodes()
1176 {
1177         KeyCodes[kVK_UpArrow] = irr::KEY_UP;
1178         KeyCodes[kVK_DownArrow] = irr::KEY_DOWN;
1179         KeyCodes[kVK_LeftArrow] = irr::KEY_LEFT;
1180         KeyCodes[kVK_RightArrow] = irr::KEY_RIGHT;
1181         KeyCodes[kVK_F1]        = irr::KEY_F1;
1182         KeyCodes[kVK_F2]        = irr::KEY_F2;
1183         KeyCodes[kVK_F3]        = irr::KEY_F3;
1184         KeyCodes[kVK_F4]        = irr::KEY_F4;
1185         KeyCodes[kVK_F5]        = irr::KEY_F5;
1186         KeyCodes[kVK_F6]        = irr::KEY_F6;
1187         KeyCodes[kVK_F7]        = irr::KEY_F7;
1188         KeyCodes[kVK_F8]        = irr::KEY_F8;
1189         KeyCodes[kVK_F9]        = irr::KEY_F9;
1190         KeyCodes[kVK_F10]       = irr::KEY_F10;
1191         KeyCodes[kVK_F11]       = irr::KEY_F11;
1192         KeyCodes[kVK_F12]       = irr::KEY_F12;
1193         KeyCodes[kVK_F13]       = irr::KEY_F13;
1194         KeyCodes[kVK_F14]       = irr::KEY_F14;
1195         KeyCodes[kVK_F15]       = irr::KEY_F15;
1196         KeyCodes[kVK_F16]       = irr::KEY_F16;
1197         KeyCodes[kVK_F17]       = irr::KEY_F17;
1198         KeyCodes[kVK_F18]       = irr::KEY_F18;
1199         KeyCodes[kVK_F19]       = irr::KEY_F19;
1200         KeyCodes[kVK_F20]       = irr::KEY_F20;
1201         KeyCodes[kVK_Home]      = irr::KEY_HOME;
1202         KeyCodes[kVK_End]       = irr::KEY_END;
1203         KeyCodes[NSInsertFunctionKey] = irr::KEY_INSERT;
1204         KeyCodes[kVK_ForwardDelete] = irr::KEY_DELETE;
1205         KeyCodes[kVK_Help] = irr::KEY_HELP;
1206         KeyCodes[NSSelectFunctionKey] = irr::KEY_SELECT;
1207         KeyCodes[NSPrintFunctionKey] = irr::KEY_PRINT;
1208         KeyCodes[NSExecuteFunctionKey] = irr::KEY_EXECUT;
1209         KeyCodes[NSPrintScreenFunctionKey] = irr::KEY_SNAPSHOT;
1210         KeyCodes[NSPauseFunctionKey] = irr::KEY_PAUSE;
1211         KeyCodes[NSScrollLockFunctionKey] = irr::KEY_SCROLL;
1212         KeyCodes[kVK_Delete] = irr::KEY_BACK;
1213         KeyCodes[kVK_Tab] = irr::KEY_TAB;
1214         KeyCodes[kVK_Return] = irr::KEY_RETURN;
1215         KeyCodes[kVK_Escape] = irr::KEY_ESCAPE;
1216         KeyCodes[kVK_Control] = irr::KEY_CONTROL;
1217         KeyCodes[kVK_RightControl] = irr::KEY_RCONTROL;
1218         KeyCodes[kVK_Command] = irr::KEY_MENU;
1219         KeyCodes[kVK_Shift] = irr::KEY_SHIFT;
1220         KeyCodes[kVK_RightShift] = irr::KEY_RSHIFT;
1221         KeyCodes[kVK_Space] = irr::KEY_SPACE;
1222
1223         KeyCodes[kVK_ANSI_A] = irr::KEY_KEY_A;
1224         KeyCodes[kVK_ANSI_B] = irr::KEY_KEY_B;
1225         KeyCodes[kVK_ANSI_C] = irr::KEY_KEY_C;
1226         KeyCodes[kVK_ANSI_D] = irr::KEY_KEY_D;
1227         KeyCodes[kVK_ANSI_E] = irr::KEY_KEY_E;
1228         KeyCodes[kVK_ANSI_F] = irr::KEY_KEY_F;
1229         KeyCodes[kVK_ANSI_G] = irr::KEY_KEY_G;
1230         KeyCodes[kVK_ANSI_H] = irr::KEY_KEY_H;
1231         KeyCodes[kVK_ANSI_I] = irr::KEY_KEY_I;
1232         KeyCodes[kVK_ANSI_J] = irr::KEY_KEY_J;
1233         KeyCodes[kVK_ANSI_K] = irr::KEY_KEY_K;
1234         KeyCodes[kVK_ANSI_L] = irr::KEY_KEY_L;
1235         KeyCodes[kVK_ANSI_M] = irr::KEY_KEY_M;
1236         KeyCodes[kVK_ANSI_N] = irr::KEY_KEY_N;
1237         KeyCodes[kVK_ANSI_O] = irr::KEY_KEY_O;
1238         KeyCodes[kVK_ANSI_P] = irr::KEY_KEY_P;
1239         KeyCodes[kVK_ANSI_Q] = irr::KEY_KEY_Q;
1240         KeyCodes[kVK_ANSI_R] = irr::KEY_KEY_R;
1241         KeyCodes[kVK_ANSI_S] = irr::KEY_KEY_S;
1242         KeyCodes[kVK_ANSI_T] = irr::KEY_KEY_T;
1243         KeyCodes[kVK_ANSI_U] = irr::KEY_KEY_U;
1244         KeyCodes[kVK_ANSI_V] = irr::KEY_KEY_V;
1245         KeyCodes[kVK_ANSI_W] = irr::KEY_KEY_W;
1246         KeyCodes[kVK_ANSI_X] = irr::KEY_KEY_X;
1247         KeyCodes[kVK_ANSI_X] = irr::KEY_KEY_X;
1248         KeyCodes[kVK_ANSI_Y] = irr::KEY_KEY_Y;
1249         KeyCodes[kVK_ANSI_Z] = irr::KEY_KEY_Z;
1250
1251         KeyCodes[kVK_ANSI_0] = irr::KEY_KEY_0;
1252         KeyCodes[kVK_ANSI_1] = irr::KEY_KEY_1;
1253         KeyCodes[kVK_ANSI_2] = irr::KEY_KEY_2;
1254         KeyCodes[kVK_ANSI_3] = irr::KEY_KEY_3;
1255         KeyCodes[kVK_ANSI_4] = irr::KEY_KEY_4;
1256         KeyCodes[kVK_ANSI_5] = irr::KEY_KEY_5;
1257         KeyCodes[kVK_ANSI_6] = irr::KEY_KEY_6;
1258         KeyCodes[kVK_ANSI_7] = irr::KEY_KEY_7;
1259         KeyCodes[kVK_ANSI_8] = irr::KEY_KEY_8;
1260         KeyCodes[kVK_ANSI_9] = irr::KEY_KEY_9;
1261
1262         KeyCodes[kVK_ANSI_Slash] = irr::KEY_DIVIDE;
1263         KeyCodes[kVK_ANSI_Comma] = irr::KEY_COMMA;
1264         KeyCodes[kVK_ANSI_Period] = irr::KEY_PERIOD;
1265         KeyCodes[kVK_PageUp] = irr::KEY_PRIOR;
1266         KeyCodes[kVK_PageDown] = irr::KEY_NEXT;
1267
1268         KeyCodes[kVK_ANSI_Keypad0] = irr::KEY_NUMPAD0;
1269         KeyCodes[kVK_ANSI_Keypad1] = irr::KEY_NUMPAD1;
1270         KeyCodes[kVK_ANSI_Keypad2] = irr::KEY_NUMPAD2;
1271         KeyCodes[kVK_ANSI_Keypad3] = irr::KEY_NUMPAD3;
1272         KeyCodes[kVK_ANSI_Keypad4] = irr::KEY_NUMPAD4;
1273         KeyCodes[kVK_ANSI_Keypad5] = irr::KEY_NUMPAD5;
1274         KeyCodes[kVK_ANSI_Keypad6] = irr::KEY_NUMPAD6;
1275         KeyCodes[kVK_ANSI_Keypad7] = irr::KEY_NUMPAD7;
1276         KeyCodes[kVK_ANSI_Keypad8] = irr::KEY_NUMPAD8;
1277         KeyCodes[kVK_ANSI_Keypad9] = irr::KEY_NUMPAD9;
1278
1279         KeyCodes[kVK_ANSI_KeypadDecimal] = irr::KEY_DECIMAL;
1280         KeyCodes[kVK_ANSI_KeypadMultiply] = irr::KEY_MULTIPLY;
1281         KeyCodes[kVK_ANSI_KeypadPlus] = irr::KEY_PLUS;
1282         KeyCodes[kVK_ANSI_KeypadClear] = irr::KEY_OEM_CLEAR;
1283         KeyCodes[kVK_ANSI_KeypadDivide] = irr::KEY_DIVIDE;
1284         KeyCodes[kVK_ANSI_KeypadEnter] = irr::KEY_RETURN;
1285         KeyCodes[kVK_ANSI_KeypadMinus] = irr::KEY_SUBTRACT;
1286
1287         KeyCodes[kVK_ANSI_LeftBracket] = irr::KEY_OEM_4;
1288         KeyCodes[kVK_ANSI_Backslash] = irr::KEY_OEM_5;
1289         KeyCodes[kVK_ANSI_RightBracket] = irr::KEY_OEM_6;
1290 }
1291
1292
1293 //! Sets if the window should be resizable in windowed mode.
1294 void CIrrDeviceMacOSX::setResizable(bool resize)
1295 {
1296         IsResizable = resize;
1297 #if 0
1298         if (resize)
1299                 [Window setStyleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask];
1300         else
1301                 [Window setStyleMask:NSTitledWindowMask|NSClosableWindowMask];
1302 #endif
1303 }
1304
1305
1306 bool CIrrDeviceMacOSX::isResizable() const
1307 {
1308         return IsResizable;
1309 }
1310
1311
1312 void CIrrDeviceMacOSX::minimizeWindow()
1313 {
1314         if (Window != NULL)
1315                 [Window miniaturize:[NSApp self]];
1316 }
1317
1318
1319 //! Maximizes the window if possible.
1320 void CIrrDeviceMacOSX::maximizeWindow()
1321 {
1322         // todo: implement
1323 }
1324
1325
1326 //! get the window to normal size if possible.
1327 void CIrrDeviceMacOSX::restoreWindow()
1328 {
1329         [Window deminiaturize:[NSApp self]];
1330 }
1331     
1332 //! Get the position of this window on screen
1333 core::position2di CIrrDeviceMacOSX::getWindowPosition()
1334 {
1335         NSRect rect = [Window frame];
1336         int screenHeight = [[[NSScreen screens] objectAtIndex:0] frame].size.height;
1337         return core::position2di(rect.origin.x, screenHeight - rect.origin.y - rect.size.height);
1338 }
1339
1340
1341 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
1342 static void joystickRemovalCallback(void * target,
1343                 IOReturn result, void * refcon, void * sender)
1344 {
1345         JoystickInfo *joy = (JoystickInfo *) refcon;
1346         joy->removed = 1;
1347 }
1348 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
1349
1350
1351 bool CIrrDeviceMacOSX::activateJoysticks(core::array<SJoystickInfo> & joystickInfo)
1352 {
1353 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
1354         ActiveJoysticks.clear();
1355         joystickInfo.clear();
1356
1357         io_object_t hidObject = 0;
1358         io_iterator_t hidIterator = 0;
1359         IOReturn result = kIOReturnSuccess;
1360         mach_port_t masterPort = 0;
1361         CFMutableDictionaryRef hidDictionaryRef = NULL;
1362
1363         result = IOMasterPort (bootstrap_port, &masterPort);
1364         if (kIOReturnSuccess != result)
1365         {
1366                 os::Printer::log("initialiseJoysticks IOMasterPort failed", ELL_ERROR);
1367                 return false;
1368         }
1369
1370         hidDictionaryRef = IOServiceMatching (kIOHIDDeviceKey);
1371         if (!hidDictionaryRef)
1372         {
1373                 os::Printer::log("initialiseJoysticks IOServiceMatching failed", ELL_ERROR);
1374                 return false;
1375         }
1376         result = IOServiceGetMatchingServices (masterPort, hidDictionaryRef, &hidIterator);
1377
1378         if (kIOReturnSuccess != result)
1379         {
1380                 os::Printer::log("initialiseJoysticks IOServiceGetMatchingServices failed", ELL_ERROR);
1381                 return false;
1382         }
1383
1384         //no joysticks just return
1385         if (!hidIterator)
1386                 return false;
1387
1388         u32 jindex = 0u;
1389         while ((hidObject = IOIteratorNext (hidIterator)))
1390         {
1391                 JoystickInfo info;
1392
1393                 // get dictionary for HID properties
1394                 CFMutableDictionaryRef hidProperties = 0;
1395
1396                 kern_return_t kern_result = IORegistryEntryCreateCFProperties (hidObject, &hidProperties, kCFAllocatorDefault, kNilOptions);
1397                 if ((kern_result == KERN_SUCCESS) && hidProperties)
1398                 {
1399                         HRESULT plugInResult = S_OK;
1400                         SInt32 score = 0;
1401                         IOCFPlugInInterface ** ppPlugInInterface = NULL;
1402                         result = IOCreatePlugInInterfaceForService (hidObject, kIOHIDDeviceUserClientTypeID,
1403                                                                                                         kIOCFPlugInInterfaceID, &ppPlugInInterface, &score);
1404                         if (kIOReturnSuccess == result)
1405                         {
1406                                 plugInResult = (*ppPlugInInterface)->QueryInterface (ppPlugInInterface,
1407                                                                         CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), (void **) &(info.interface));
1408                                 if (plugInResult != S_OK)
1409                                         os::Printer::log("initialiseJoysticks query HID class device interface failed", ELL_ERROR);
1410                                 (*ppPlugInInterface)->Release(ppPlugInInterface);
1411                         }
1412                         else
1413                                 continue;
1414
1415                         if (info.interface != NULL)
1416                         {
1417                                 result = (*(info.interface))->open (info.interface, 0);
1418                                 if (result == kIOReturnSuccess)
1419                                 {
1420                                         (*(info.interface))->setRemovalCallback (info.interface, joystickRemovalCallback, &info, &info);
1421                                         getJoystickDeviceInfo(hidObject, hidProperties, &info);
1422
1423                                         // get elements
1424                                         CFTypeRef refElementTop = CFDictionaryGetValue (hidProperties, CFSTR(kIOHIDElementKey));
1425                                         if (refElementTop)
1426                                         {
1427                                                 CFTypeID type = CFGetTypeID (refElementTop);
1428                                                 if (type == CFArrayGetTypeID())
1429                                                 {
1430                                                         CFRange range = {0, CFArrayGetCount ((CFArrayRef)refElementTop)};
1431                                                         info.numActiveJoysticks = ActiveJoysticks.size();
1432                                                         CFArrayApplyFunction ((CFArrayRef)refElementTop, range, getJoystickComponentArrayHandler, &info);
1433                                                 }
1434                                         }
1435                                 }
1436                                 else
1437                                 {
1438                                         CFRelease (hidProperties);
1439                                         os::Printer::log("initialiseJoysticks Open interface failed", ELL_ERROR);
1440                                         continue;
1441                                 }
1442
1443                                 CFRelease (hidProperties);
1444
1445                                 result = IOObjectRelease (hidObject);
1446
1447                                 if ( (info.usagePage != kHIDPage_GenericDesktop) ||
1448                                         ((info.usage != kHIDUsage_GD_Joystick &&
1449                                         info.usage != kHIDUsage_GD_GamePad &&
1450                                         info.usage != kHIDUsage_GD_MultiAxisController)) )
1451                                 {
1452                                         closeJoystickDevice (&info);
1453                                         continue;
1454                                 }
1455
1456                                 for (u32 i = 0; i < 6; ++i)
1457                                         info.persistentData.JoystickEvent.Axis[i] = 0;
1458
1459                                 ActiveJoysticks.push_back(info);
1460
1461                                 SJoystickInfo returnInfo;
1462                                 returnInfo.Joystick = jindex;
1463                                 returnInfo.Axes = info.axes;
1464                                 //returnInfo.Hats = info.hats;
1465                                 returnInfo.Buttons = info.buttons;
1466                                 returnInfo.Name = info.joystickName;
1467                                 returnInfo.PovHat = SJoystickInfo::POV_HAT_UNKNOWN;
1468                                 ++ jindex;
1469
1470                                 //if (info.hatComp.size())
1471                                 //      returnInfo.PovHat = SJoystickInfo::POV_HAT_PRESENT;
1472                                 //else
1473                                 //      returnInfo.PovHat = SJoystickInfo::POV_HAT_ABSENT;
1474
1475                                 joystickInfo.push_back(returnInfo);
1476                         }
1477
1478                 }
1479                 else
1480                 {
1481                         continue;
1482                 }
1483         }
1484         result = IOObjectRelease (hidIterator);
1485
1486         return true;
1487 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
1488
1489         return false;
1490 }
1491
1492 void CIrrDeviceMacOSX::pollJoysticks()
1493 {
1494 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
1495         if(0 == ActiveJoysticks.size())
1496                 return;
1497
1498         u32 joystick;
1499         for (joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
1500         {
1501                 if (ActiveJoysticks[joystick].removed)
1502                         continue;
1503
1504                 bool found = false;
1505                 ActiveJoysticks[joystick].persistentData.JoystickEvent.Joystick = joystick;
1506
1507                 if (ActiveJoysticks[joystick].interface)
1508                 {
1509                         for (u32 n = 0; n < ActiveJoysticks[joystick].axisComp.size(); n++)
1510                         {
1511                                 IOReturn result = kIOReturnSuccess;
1512                                 IOHIDEventStruct hidEvent;
1513                                 hidEvent.value = 0;
1514                                 result = (*(ActiveJoysticks[joystick].interface))->getElementValue(ActiveJoysticks[joystick].interface, ActiveJoysticks[joystick].axisComp[n].cookie, &hidEvent);
1515                                 if (kIOReturnSuccess == result)
1516                                 {
1517                                         const f32 min = -32768.0f;
1518                                         const f32 max = 32767.0f;
1519                                         const f32 deviceScale = max - min;
1520                                         const f32 readScale = (f32)ActiveJoysticks[joystick].axisComp[n].maxRead - (f32)ActiveJoysticks[joystick].axisComp[n].minRead;
1521
1522                                         if (hidEvent.value < ActiveJoysticks[joystick].axisComp[n].minRead)
1523                                                 ActiveJoysticks[joystick].axisComp[n].minRead = hidEvent.value;
1524                                         if (hidEvent.value > ActiveJoysticks[joystick].axisComp[n].maxRead)
1525                                                 ActiveJoysticks[joystick].axisComp[n].maxRead = hidEvent.value;
1526
1527                                         if (readScale != 0.0f)
1528                                                 hidEvent.value = (int)(((f32)((f32)hidEvent.value - (f32)ActiveJoysticks[joystick].axisComp[n].minRead) * deviceScale / readScale) + min);
1529
1530                                         if (ActiveJoysticks[joystick].persistentData.JoystickEvent.Axis[n] != (s16)hidEvent.value)
1531                                                 found = true;
1532                                         ActiveJoysticks[joystick].persistentData.JoystickEvent.Axis[n] = (s16)hidEvent.value;
1533                                 }
1534                         }//axis check
1535
1536                         for (u32 n = 0; n < ActiveJoysticks[joystick].buttonComp.size(); n++)
1537                         {
1538                                 IOReturn result = kIOReturnSuccess;
1539                                 IOHIDEventStruct hidEvent;
1540                                 hidEvent.value = 0;
1541                                 result = (*(ActiveJoysticks[joystick].interface))->getElementValue(ActiveJoysticks[joystick].interface, ActiveJoysticks[joystick].buttonComp[n].cookie, &hidEvent);
1542                                 if (kIOReturnSuccess == result)
1543                                 {
1544                                         if (hidEvent.value && !((ActiveJoysticks[joystick].persistentData.JoystickEvent.ButtonStates & (1 << n)) ? true : false) )
1545                                                         found = true;
1546                                         else if (!hidEvent.value && ((ActiveJoysticks[joystick].persistentData.JoystickEvent.ButtonStates & (1 << n)) ? true : false))
1547                                                         found = true;
1548
1549                                         if (hidEvent.value)
1550                                                         ActiveJoysticks[joystick].persistentData.JoystickEvent.ButtonStates |= (1 << n);
1551                                         else
1552                                                         ActiveJoysticks[joystick].persistentData.JoystickEvent.ButtonStates &= ~(1 << n);
1553                                 }
1554                         }//button check
1555                         //still ToDo..will be done soon :)
1556 /*
1557                         for (u32 n = 0; n < ActiveJoysticks[joystick].hatComp.size(); n++)
1558                         {
1559                                 IOReturn result = kIOReturnSuccess;
1560                                 IOHIDEventStruct hidEvent;
1561                                 hidEvent.value = 0;
1562                                 result = (*(ActiveJoysticks[joystick].interface))->getElementValue(ActiveJoysticks[joystick].interface, ActiveJoysticks[joystick].hatComp[n].cookie, &hidEvent);
1563                                 if (kIOReturnSuccess == result)
1564                                 {
1565                                         if (ActiveJoysticks[joystick].persistentData.JoystickEvent.POV != hidEvent.value)
1566                                                 found = true;
1567                                         ActiveJoysticks[joystick].persistentData.JoystickEvent.POV = hidEvent.value;
1568                                 }
1569                         }//hat check
1570 */
1571                 }
1572
1573                 if (found)
1574                         postEventFromUser(ActiveJoysticks[joystick].persistentData);
1575         }
1576 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
1577 }
1578
1579 } // end namespace
1580
1581 #endif // _IRR_COMPILE_WITH_OSX_DEVICE_
1582