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