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