PlatformMacOSX.mm 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424
  1. #ifdef __APPLE__
  2. #include "Base.h"
  3. #include "Platform.h"
  4. #include "FileSystem.h"
  5. #include "Game.h"
  6. #include "Form.h"
  7. #include "ScriptController.h"
  8. #include <unistd.h>
  9. #include <IOKit/hid/IOHIDLib.h>
  10. #import <Cocoa/Cocoa.h>
  11. #import <QuartzCore/CVDisplayLink.h>
  12. #import <OpenGL/OpenGL.h>
  13. #import <mach/mach_time.h>
  14. #import <Foundation/Foundation.h>
  15. #import <GameKit/GameKit.h>
  16. // These should probably be moved to a platform common file
  17. #define SONY_USB_VENDOR_ID 0x054c
  18. #define SONY_USB_PS3_PRODUCT_ID 0x0268
  19. #define MICROSOFT_VENDOR_ID 0x045e
  20. #define MICROSOFT_XBOX360_PRODUCT_ID 0x028e
  21. #define STEELSERIES_VENDOR_ID 0x1038
  22. #define STEELSERIES_FREE_PRODUCT_ID 0x1412
  23. #define FRUCTEL_VENDOR_ID 0x25B6
  24. #define FRUCTEL_GAMETEL_PRODUCT_ID 0x0001
  25. using namespace std;
  26. using namespace gameplay;
  27. @class View;
  28. @class HIDGamepad;
  29. int __argc = 0;
  30. char** __argv = 0;
  31. // Default to 720p
  32. static int __width = 1280;
  33. static int __height = 720;
  34. static double __timeStart;
  35. static double __timeAbsolute;
  36. static bool __vsync = WINDOW_VSYNC;
  37. static bool __hasMouse = false;
  38. static bool __leftMouseDown = false;
  39. static bool __rightMouseDown = false;
  40. static bool __otherMouseDown = false;
  41. static bool __shiftDown = false;
  42. static char* __title = NULL;
  43. static bool __fullscreen = false;
  44. static bool __resizable = false;
  45. static bool __mouseCaptured = false;
  46. static bool __mouseCapturedFirstPass = false;
  47. static CGPoint __mouseCapturePoint;
  48. static bool __multiSampling = false;
  49. static bool __cursorVisible = true;
  50. static View* __view = NULL;
  51. static NSMutableDictionary *__activeGamepads = NULL;
  52. static NSMutableArray *__gamepads = NULL;
  53. static IOHIDManagerRef __hidManagerRef = NULL;
  54. // Gamepad Helper Function
  55. HIDGamepad *gamepadForLocationID(NSNumber *locationID);
  56. HIDGamepad *gamepadForLocationIDValue(unsigned int locationIDValue);
  57. HIDGamepad *gamepadForGameHandle(int gameHandle);
  58. // IOHid Helper Functions
  59. CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage);
  60. CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
  61. int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key);
  62. // IOHid Callbacks
  63. static void hidDeviceDiscoveredCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
  64. static void hidDeviceRemovalCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
  65. static void hidDeviceValueAvailableCallback(void *inContext, IOReturn inResult, void *inSender);
  66. double getMachTimeInMilliseconds()
  67. {
  68. static const double kOneMillion = 1000 * 1000;
  69. static mach_timebase_info_data_t s_timebase_info;
  70. if (s_timebase_info.denom == 0)
  71. (void) mach_timebase_info(&s_timebase_info);
  72. // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
  73. GP_ASSERT(s_timebase_info.denom);
  74. return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
  75. }
  76. @interface HIDGamepadAxis : NSObject
  77. {
  78. IOHIDElementRef e;
  79. CFIndex v;
  80. CFIndex logMin;
  81. CFIndex logMax;
  82. }
  83. + gamepadAxisWithAxisElement:(IOHIDElementRef)element;
  84. - initWithAxisElement:(IOHIDElementRef)element;
  85. - (IOHIDElementRef)element;
  86. - (IOHIDElementCookie)cookie;
  87. - (uint32_t)usage;
  88. - (uint32_t)usagePage;
  89. - (CFIndex)logicalMinimum;
  90. - (CFIndex)logicalMaximum;
  91. - (float)calibratedValue;
  92. - (CFIndex)value;
  93. - (void)setValue:(CFIndex)value;
  94. @end
  95. @implementation HIDGamepadAxis
  96. + gamepadAxisWithAxisElement:(IOHIDElementRef)element
  97. {
  98. return [[[[self class] alloc] initWithAxisElement:element] autorelease];
  99. }
  100. - initWithAxisElement:(IOHIDElementRef)element
  101. {
  102. if((self = [super init]))
  103. {
  104. e = (IOHIDElementRef)CFRetain(element);
  105. }
  106. return self;
  107. }
  108. - (void)dealloc
  109. {
  110. CFRelease(e);
  111. [super dealloc];
  112. }
  113. - (IOHIDElementRef)element
  114. {
  115. return e;
  116. }
  117. - (IOHIDElementCookie)cookie
  118. {
  119. return IOHIDElementGetCookie(e);
  120. }
  121. - (bool)isHatSwitch {
  122. return (IOHIDElementGetUsage(e) == kHIDUsage_GD_Hatswitch);
  123. }
  124. - (uint32_t)usage
  125. {
  126. return IOHIDElementGetUsage(e);
  127. }
  128. - (uint32_t)usagePage
  129. {
  130. return IOHIDElementGetUsagePage(e);
  131. }
  132. - (CFIndex)logicalMinimum
  133. {
  134. return IOHIDElementGetLogicalMin(e);
  135. }
  136. - (CFIndex)logicalMaximum
  137. {
  138. return IOHIDElementGetLogicalMax(e);
  139. }
  140. - (float)calibratedValue
  141. {
  142. float cmax = 2.0f;
  143. float cmin = 0.0f;
  144. return ((((v - [self logicalMinimum]) * (cmax - cmin)) / ([self logicalMaximum] - [self logicalMinimum])) + cmin - 1.0f);
  145. }
  146. - (CFIndex)value
  147. {
  148. return v;
  149. }
  150. - (void)setValue:(CFIndex)value
  151. {
  152. v = value;
  153. }
  154. @end
  155. @interface HIDGamepadButton : NSObject
  156. {
  157. IOHIDElementRef e;
  158. IOHIDElementRef te;
  159. bool state;
  160. int triggerValue;
  161. }
  162. + gamepadButtonWithButtonElement:(IOHIDElementRef)element;
  163. - initWithButtonElement:(IOHIDElementRef)element;
  164. - (void)setTriggerElement:(IOHIDElementRef)element;
  165. - (IOHIDElementRef)element;
  166. - (IOHIDElementCookie)cookie;
  167. - (IOHIDElementRef)triggerElement;
  168. - (IOHIDElementCookie)triggerCookie;
  169. - (bool)isTriggerButton;
  170. - (uint32_t)usage;
  171. - (uint32_t)usagePage;
  172. - (int)stateValue;
  173. - (float)calibratedStateValue;
  174. - (void)setStateValue:(int)value;
  175. - (bool)state;
  176. - (void)setState:(bool)state;
  177. @end
  178. @implementation HIDGamepadButton
  179. + gamepadButtonWithButtonElement:(IOHIDElementRef)element
  180. {
  181. return [[[[self class] alloc] initWithButtonElement:element] autorelease];
  182. }
  183. - initWithButtonElement:(IOHIDElementRef)element
  184. {
  185. if((self = [super init]))
  186. {
  187. e = (IOHIDElementRef)CFRetain(element);
  188. te = NULL;
  189. state = false;
  190. }
  191. return self;
  192. }
  193. - (void)dealloc
  194. {
  195. CFRelease(e);
  196. if(te != NULL) CFRelease(te);
  197. [super dealloc];
  198. }
  199. - (void)setTriggerElement:(IOHIDElementRef)element {
  200. if(te)
  201. {
  202. CFRelease(te);
  203. te = NULL;
  204. }
  205. if(element)
  206. {
  207. te = (IOHIDElementRef)CFRetain(element);
  208. }
  209. }
  210. - (IOHIDElementRef)element
  211. {
  212. return e;
  213. }
  214. - (IOHIDElementCookie)cookie
  215. {
  216. return IOHIDElementGetCookie(e);
  217. }
  218. - (IOHIDElementRef)triggerElement
  219. {
  220. return te;
  221. }
  222. - (IOHIDElementCookie)triggerCookie
  223. {
  224. return IOHIDElementGetCookie(te);
  225. }
  226. - (bool)isTriggerButton
  227. {
  228. return (te != NULL);
  229. }
  230. - (uint32_t)usage
  231. {
  232. return IOHIDElementGetUsage(e);
  233. }
  234. - (uint32_t)usagePage
  235. {
  236. return IOHIDElementGetUsagePage(e);
  237. }
  238. - (void)setStateValue:(int)value {
  239. triggerValue = value;
  240. }
  241. - (int)stateValue
  242. {
  243. return triggerValue;
  244. }
  245. - (float)calibratedStateValue
  246. {
  247. return (float)triggerValue / 255.0f;
  248. }
  249. - (bool)state
  250. {
  251. return state;
  252. }
  253. - (void)setState:(bool)s
  254. {
  255. state = s;
  256. }
  257. @end
  258. @interface HIDGamepad : NSObject
  259. {
  260. IOHIDDeviceRef hidDeviceRef;
  261. IOHIDQueueRef queueRef;
  262. NSMutableArray* buttons;
  263. NSMutableArray* triggerButtons;
  264. NSMutableArray* axes;
  265. HIDGamepadAxis* hatSwitch;
  266. }
  267. @property (assign) IOHIDDeviceRef hidDeviceRef;
  268. @property (assign) IOHIDQueueRef queueRef;
  269. @property (retain) NSMutableArray* buttons;
  270. @property (retain) NSMutableArray* triggerButtons;
  271. @property (retain) NSMutableArray* axes;
  272. @property (retain) HIDGamepadAxis* hatSwitch;
  273. - initWithDevice:(IOHIDDeviceRef)rawDevice;
  274. - (IOHIDDeviceRef)rawDevice;
  275. - (NSNumber*)locationID;
  276. - (void)initializeGamepadElements;
  277. - (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie;
  278. - (bool)startListening;
  279. - (void)stopListening;
  280. - (NSString*)identifierName;
  281. - (NSString*)productName;
  282. - (NSString*)manufacturerName;
  283. - (NSString*)serialNumber;
  284. - (int)versionNumber;
  285. - (int)vendorID;
  286. - (int)productID;
  287. - (NSUInteger)numberOfAxes;
  288. - (NSUInteger)numberOfSticks;
  289. - (NSUInteger)numberOfButtons;
  290. - (NSUInteger)numberOfTriggerButtons;
  291. - (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index;
  292. - (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index;
  293. - (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index;
  294. - (HIDGamepadAxis*)getHatSwitch;
  295. @end
  296. @implementation HIDGamepad
  297. @synthesize hidDeviceRef;
  298. @synthesize queueRef;
  299. @synthesize buttons;
  300. @synthesize triggerButtons;
  301. @synthesize axes;
  302. @synthesize hatSwitch;
  303. - initWithDevice:(IOHIDDeviceRef)rawDevice
  304. {
  305. if((self = [super init]))
  306. {
  307. [self setButtons:[NSMutableArray array]];
  308. [self setTriggerButtons:[NSMutableArray array]];
  309. [self setAxes:[NSMutableArray array]];
  310. hatSwitch = NULL;
  311. CFRetain(rawDevice);
  312. IOHIDQueueRef queue = IOHIDQueueCreate(CFAllocatorGetDefault(), rawDevice, 10, kIOHIDOptionsTypeNone);
  313. [self setHidDeviceRef:rawDevice];
  314. [self setQueueRef:queue];
  315. [self initializeGamepadElements];
  316. [self startListening];
  317. }
  318. return self;
  319. }
  320. - (void)dealloc
  321. {
  322. [self stopListening];
  323. CFRelease([self rawDevice]);
  324. CFRelease([self queueRef]);
  325. [self setQueueRef:NULL];
  326. [self setHidDeviceRef:NULL];
  327. [self setButtons:NULL];
  328. [self setTriggerButtons:NULL];
  329. [self setAxes:NULL];
  330. if (hatSwitch != NULL)
  331. {
  332. [hatSwitch dealloc];
  333. }
  334. [super dealloc];
  335. }
  336. - (IOHIDDeviceRef)rawDevice
  337. {
  338. return [self hidDeviceRef];
  339. }
  340. - (NSNumber*)locationID
  341. {
  342. return (NSNumber*)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDLocationIDKey));
  343. }
  344. - (void)initializeGamepadElements
  345. {
  346. uint32_t vendorID = [self vendorID];
  347. uint32_t productID = [self productID];
  348. CFArrayRef elements = IOHIDDeviceCopyMatchingElements([self rawDevice], NULL, kIOHIDOptionsTypeNone);
  349. for(int i = 0; i < CFArrayGetCount(elements); i++)
  350. {
  351. IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
  352. IOHIDElementType type = IOHIDElementGetType(hidElement);
  353. if (type == kIOHIDElementTypeInput_Misc || type == kIOHIDElementTypeInput_Axis)
  354. {
  355. uint32_t pageUsage = IOHIDElementGetUsage(hidElement);
  356. IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
  357. switch(pageUsage)
  358. {
  359. case kHIDUsage_GD_X:
  360. case kHIDUsage_GD_Y:
  361. case kHIDUsage_GD_Rx:
  362. case kHIDUsage_GD_Ry:
  363. case kHIDUsage_GD_Z:
  364. case kHIDUsage_GD_Rz:
  365. {
  366. if (vendorID == MICROSOFT_VENDOR_ID &&
  367. productID == MICROSOFT_XBOX360_PRODUCT_ID &&
  368. (pageUsage == kHIDUsage_GD_Z || pageUsage == kHIDUsage_GD_Rz))
  369. {
  370. HIDGamepadButton* triggerButton = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
  371. [triggerButton setTriggerElement:hidElement];
  372. [[self triggerButtons] addObject:triggerButton];
  373. }
  374. else
  375. {
  376. HIDGamepadAxis* axis = [HIDGamepadAxis gamepadAxisWithAxisElement:hidElement];
  377. [[self axes] addObject:axis];
  378. }
  379. break;
  380. }
  381. case kHIDUsage_GD_Hatswitch:
  382. {
  383. HIDGamepadAxis* hat = [[HIDGamepadAxis alloc] initWithAxisElement:hidElement];
  384. [hat setValue: -1];
  385. hatSwitch = hat;
  386. }
  387. default:
  388. // Ignore the pointers
  389. // Note: Some of the pointers are for the 6-axis accelerometer in a PS3 controller
  390. // Note: L2/R2 triggers are at cookie 39 and 40 base 10 tied to 9 and 10 button elements
  391. break;
  392. }
  393. }
  394. if(type == kIOHIDElementTypeInput_Button)
  395. {
  396. HIDGamepadButton *button = [HIDGamepadButton gamepadButtonWithButtonElement:hidElement];
  397. [[self buttons] addObject:button];
  398. }
  399. }
  400. // Go back and get proprietary information (e.g. triggers) and associate with appropriate values
  401. // Example for other trigger buttons
  402. for(int i = 0; i < CFArrayGetCount(elements); i++)
  403. {
  404. IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
  405. IOHIDElementType type = IOHIDElementGetType(hidElement);
  406. IOHIDElementCookie cookie = IOHIDElementGetCookie(hidElement);
  407. // Gamepad specific code
  408. if(vendorID == SONY_USB_VENDOR_ID && productID == SONY_USB_PS3_PRODUCT_ID)
  409. {
  410. if((unsigned long)cookie == 39)
  411. {
  412. HIDGamepadButton *leftTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)9];
  413. if(leftTriggerButton)
  414. {
  415. [leftTriggerButton setTriggerElement:hidElement];
  416. [[self triggerButtons] addObject:leftTriggerButton];
  417. }
  418. }
  419. if((unsigned long)cookie == 40)
  420. {
  421. HIDGamepadButton *rightTriggerButton = [self buttonWithCookie:(IOHIDElementCookie)10];
  422. if(rightTriggerButton)
  423. {
  424. [rightTriggerButton setTriggerElement:hidElement];
  425. [[self triggerButtons] addObject:rightTriggerButton];
  426. }
  427. }
  428. }
  429. }
  430. }
  431. - (HIDGamepadButton*)buttonWithCookie:(IOHIDElementCookie)cookie {
  432. for(HIDGamepadButton *b in [self buttons]) {
  433. if([b cookie] == cookie)
  434. return b;
  435. }
  436. return NULL;
  437. }
  438. - (bool)startListening
  439. {
  440. IOReturn kr = IOHIDDeviceOpen([self hidDeviceRef], kIOHIDOptionsTypeNone);
  441. if(kr != 0) {
  442. return false;
  443. }
  444. IOHIDDeviceScheduleWithRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  445. IOHIDQueueStart([self queueRef]);
  446. IOHIDQueueRegisterValueAvailableCallback([self queueRef], hidDeviceValueAvailableCallback, self);
  447. CFArrayRef elements = (CFArrayRef)[self watchedElements];
  448. for(int i = 0; i < CFArrayGetCount(elements); i++)
  449. {
  450. IOHIDElementRef hidElement = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
  451. IOHIDQueueAddElement([self queueRef], hidElement);
  452. }
  453. IOHIDQueueScheduleWithRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  454. return true;
  455. }
  456. - (void)stopListening
  457. {
  458. IOHIDQueueUnscheduleFromRunLoop([self queueRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  459. IOHIDQueueStop([self queueRef]);
  460. IOHIDDeviceUnscheduleFromRunLoop([self hidDeviceRef], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  461. IOHIDDeviceClose([self hidDeviceRef], kIOHIDOptionsTypeNone);
  462. }
  463. - (NSString*)identifierName
  464. {
  465. NSString* idName = NULL;
  466. if(idName == NULL) idName = [self productName];
  467. if(idName == NULL) idName = [self manufacturerName];
  468. if(idName == NULL) idName = [self serialNumber];
  469. if(idName == NULL) idName = [NSString stringWithFormat:@"%d-%d", [self vendorID], [self productID]];
  470. return idName;
  471. }
  472. - (NSString*)productName
  473. {
  474. CFStringRef productName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDProductKey));
  475. if(productName == NULL || CFGetTypeID(productName) != CFStringGetTypeID())
  476. {
  477. return NULL;
  478. }
  479. return (NSString*)productName;
  480. }
  481. - (NSString*)manufacturerName
  482. {
  483. CFStringRef manufacturerName = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDManufacturerKey));
  484. if(manufacturerName == NULL || CFGetTypeID(manufacturerName) != CFStringGetTypeID())
  485. {
  486. return NULL;
  487. }
  488. return (NSString*)manufacturerName;
  489. }
  490. - (NSString*)serialNumber
  491. {
  492. CFStringRef serialNumber = (CFStringRef)IOHIDDeviceGetProperty([self rawDevice], CFSTR(kIOHIDSerialNumberKey));
  493. if(serialNumber == NULL || CFGetTypeID(serialNumber) != CFStringGetTypeID())
  494. {
  495. return NULL;
  496. }
  497. return (NSString*)serialNumber;
  498. }
  499. - (int)versionNumber
  500. {
  501. return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVersionNumberKey));
  502. }
  503. - (int)vendorID
  504. {
  505. return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDVendorIDKey));
  506. }
  507. - (int)productID
  508. {
  509. return IOHIDDeviceGetIntProperty([self rawDevice], CFSTR(kIOHIDProductIDKey));
  510. }
  511. - (NSUInteger)numberOfAxes
  512. {
  513. return [[self axes] count];
  514. }
  515. - (NSUInteger)numberOfSticks
  516. {
  517. return ([[self axes] count] / 2);
  518. }
  519. - (NSUInteger)numberOfButtons
  520. {
  521. return [[self buttons] count];
  522. }
  523. - (NSUInteger)numberOfTriggerButtons
  524. {
  525. return [[self triggerButtons] count];
  526. }
  527. - (HIDGamepadButton*)triggerButtonAtIndex:(NSUInteger)index
  528. {
  529. HIDGamepadButton *b = NULL;
  530. if(index < [[self triggerButtons] count])
  531. {
  532. b = [[self triggerButtons] objectAtIndex:index];
  533. }
  534. return b;
  535. }
  536. - (HIDGamepadAxis*)axisAtIndex:(NSUInteger)index
  537. {
  538. HIDGamepadAxis *a = NULL;
  539. if(index < [[self axes] count])
  540. {
  541. a = [[self axes] objectAtIndex:index];
  542. }
  543. return a;
  544. }
  545. - (HIDGamepadButton*)buttonAtIndex:(NSUInteger)index
  546. {
  547. HIDGamepadButton *b = NULL;
  548. if(index < [[self buttons] count])
  549. {
  550. b = [[self buttons] objectAtIndex:index];
  551. }
  552. return b;
  553. }
  554. - (HIDGamepadAxis*)getHatSwitch
  555. {
  556. if (hatSwitch != NULL)
  557. {
  558. return hatSwitch;
  559. }
  560. return NULL;
  561. }
  562. - (NSArray*)watchedElements
  563. {
  564. NSMutableArray *r = [NSMutableArray array];
  565. for(HIDGamepadButton *b in [self buttons])
  566. {
  567. [r addObject:(id)[b element]];
  568. }
  569. for(HIDGamepadAxis *a in [self axes])
  570. {
  571. [r addObject:(id)[a element]];
  572. }
  573. for(HIDGamepadButton* t in [self triggerButtons])
  574. {
  575. [r addObject:(id)[t triggerElement]];
  576. }
  577. if (hatSwitch)
  578. {
  579. [r addObject:(id)[hatSwitch element]];
  580. }
  581. return [NSArray arrayWithArray:r];
  582. }
  583. - (void)hidValueAvailable:(IOHIDValueRef)value
  584. {
  585. IOHIDElementRef element = IOHIDValueGetElement(value);
  586. IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
  587. if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
  588. CFIndex integerValue = IOHIDValueGetIntegerValue(value);
  589. for(HIDGamepadAxis *a in [self axes])
  590. {
  591. if([a cookie] == cookie)
  592. {
  593. [a setValue:integerValue];
  594. }
  595. }
  596. for(HIDGamepadButton *b in [self buttons])
  597. {
  598. if([b cookie] == cookie)
  599. {
  600. [b setState:(bool)integerValue];
  601. break;
  602. }
  603. }
  604. for(HIDGamepadButton *b in [self triggerButtons])
  605. {
  606. if([b triggerCookie] == cookie)
  607. {
  608. [b setStateValue:integerValue];
  609. break;
  610. }
  611. }
  612. if (hatSwitch && [hatSwitch cookie] == cookie)
  613. {
  614. [hatSwitch setValue:integerValue];
  615. }
  616. }
  617. @end
  618. @interface View : NSOpenGLView <NSWindowDelegate>
  619. {
  620. @public
  621. CVDisplayLinkRef displayLink;
  622. NSRecursiveLock* gameLock;
  623. @protected
  624. Game* _game;
  625. unsigned int _gestureEvents;
  626. }
  627. - (void) detectGamepads: (Game*) game;
  628. @end
  629. @implementation View
  630. -(void)windowWillClose:(NSNotification*)note
  631. {
  632. [gameLock lock];
  633. _game->exit();
  634. [gameLock unlock];
  635. [[NSApplication sharedApplication] terminate:self];
  636. }
  637. - (void)windowDidResize:(NSNotification*)notification
  638. {
  639. [gameLock lock];
  640. NSSize size = [ [ _window contentView ] frame ].size;
  641. gameplay::Platform::resizeEventInternal((unsigned int)size.width, (unsigned int)size.height);
  642. [gameLock unlock];
  643. }
  644. - (CVReturn) getFrameForTime:(const CVTimeStamp*)outputTime
  645. {
  646. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  647. [self update];
  648. [pool release];
  649. return kCVReturnSuccess;
  650. }
  651. - (void) detectGamepads: (Game*) game
  652. {
  653. // Locate any newly connected devices
  654. for(HIDGamepad* gamepad in __gamepads)
  655. {
  656. NSNumber* locationID = [gamepad locationID];
  657. if([__activeGamepads objectForKey:locationID] == NULL)
  658. {
  659. // Gameplay::add is friended to Platform, but we're not in Platform right now.
  660. Platform::gamepadEventConnectedInternal((unsigned int)[locationID intValue],
  661. [gamepad numberOfButtons],
  662. [gamepad numberOfSticks],
  663. [gamepad numberOfTriggerButtons],
  664. [gamepad vendorID],
  665. [gamepad productID],
  666. [[gamepad manufacturerName] cStringUsingEncoding:NSASCIIStringEncoding],
  667. [[gamepad productName] cStringUsingEncoding:NSASCIIStringEncoding]);
  668. [__activeGamepads setObject:locationID forKey:locationID];
  669. }
  670. }
  671. // Detect any disconnected gamepads
  672. NSMutableArray* deadGamepads = [NSMutableArray array];
  673. for(NSNumber* locationID in __activeGamepads)
  674. {
  675. HIDGamepad* gamepad = gamepadForLocationID(locationID);
  676. if(gamepad == NULL)
  677. {
  678. NSNumber* gameHandle = [__activeGamepads objectForKey:locationID];
  679. Platform::gamepadEventDisconnectedInternal((unsigned int)[locationID intValue]);
  680. [deadGamepads addObject:locationID];
  681. }
  682. }
  683. [__activeGamepads removeObjectsForKeys:deadGamepads];
  684. }
  685. -(void) update
  686. {
  687. [gameLock lock];
  688. [[self openGLContext] makeCurrentContext];
  689. CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
  690. if (_game)
  691. {
  692. [self detectGamepads: _game];
  693. _game->frame();
  694. }
  695. CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
  696. CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
  697. [gameLock unlock];
  698. }
  699. static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime,
  700. CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
  701. {
  702. CVReturn result = [(View*)displayLinkContext getFrameForTime:outputTime];
  703. return result;
  704. }
  705. - (id) initWithFrame: (NSRect) frame
  706. {
  707. _game = Game::getInstance();
  708. Properties* config = _game->getConfig()->getNamespace("window", true);
  709. int samples = config ? config->getInt("samples") : 0;
  710. if (samples < 0)
  711. samples = 0;
  712. // Note: Keep multisampling attributes at the start of the attribute lists since code below
  713. // assumes they are array elements 0 through 4.
  714. NSOpenGLPixelFormatAttribute windowedAttrs[] =
  715. {
  716. NSOpenGLPFAMultisample,
  717. NSOpenGLPFASampleBuffers, samples ? 1 : 0,
  718. NSOpenGLPFASamples, samples,
  719. NSOpenGLPFAAccelerated,
  720. NSOpenGLPFADoubleBuffer,
  721. NSOpenGLPFAColorSize, 32,
  722. NSOpenGLPFADepthSize, 24,
  723. NSOpenGLPFAAlphaSize, 8,
  724. NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
  725. 0
  726. };
  727. NSOpenGLPixelFormatAttribute fullscreenAttrs[] =
  728. {
  729. NSOpenGLPFAMultisample,
  730. NSOpenGLPFASampleBuffers, samples ? 1 : 0,
  731. NSOpenGLPFASamples, samples,
  732. NSOpenGLPFADoubleBuffer,
  733. NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
  734. NSOpenGLPFAFullScreen,
  735. NSOpenGLPFAColorSize, 32,
  736. NSOpenGLPFADepthSize, 24,
  737. NSOpenGLPFAAlphaSize, 8,
  738. NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
  739. 0
  740. };
  741. NSOpenGLPixelFormatAttribute* attrs = __fullscreen ? fullscreenAttrs : windowedAttrs;
  742. __multiSampling = samples > 0;
  743. // Try to choose a supported pixel format
  744. NSOpenGLPixelFormat* pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
  745. if (!pf)
  746. {
  747. bool valid = false;
  748. while (!pf && samples > 0)
  749. {
  750. samples /= 2;
  751. attrs[2] = samples ? 1 : 0;
  752. attrs[4] = samples;
  753. pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
  754. if (pf)
  755. {
  756. valid = true;
  757. break;
  758. }
  759. }
  760. __multiSampling = samples > 0;
  761. if (!valid)
  762. {
  763. NSLog(@"OpenGL pixel format not supported.");
  764. GP_ERROR("Failed to create a valid OpenGL pixel format.");
  765. return nil;
  766. }
  767. }
  768. if ((self = [super initWithFrame:frame pixelFormat:[pf autorelease]]))
  769. {
  770. gameLock = [[NSRecursiveLock alloc] init];
  771. __timeStart = getMachTimeInMilliseconds();
  772. }
  773. return self;
  774. }
  775. - (void) prepareOpenGL
  776. {
  777. [super prepareOpenGL];
  778. _game->run();
  779. if (__fullscreen)
  780. {
  781. [[self window] setLevel: NSMainMenuWindowLevel+1];
  782. [[self window] setHidesOnDeactivate:YES];
  783. }
  784. else
  785. {
  786. [[self window] setLevel: NSNormalWindowLevel];
  787. }
  788. [[self window] makeKeyAndOrderFront: self];
  789. [[self window] setTitle: [NSString stringWithUTF8String: __title ? __title : ""]];
  790. // Make all the OpenGL calls to setup rendering and build the necessary rendering objects
  791. [[self openGLContext] makeCurrentContext];
  792. // Synchronize buffer swaps with vertical refresh rate
  793. GLint swapInt = __vsync ? 1 : 0;
  794. [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
  795. // Create a display link capable of being used with all active displays
  796. CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
  797. // Set the renderer output callback function
  798. CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self);
  799. CGLContextObj cglContext = (CGLContextObj)[[self openGLContext] CGLContextObj];
  800. CGLPixelFormatObj cglPixelFormat = (CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj];
  801. CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
  802. GLint dim[2] = {__width, __height};
  803. CGLSetParameter(cglContext, kCGLCPSurfaceBackingSize, dim);
  804. CGLEnable(cglContext, kCGLCESurfaceBackingSize);
  805. // Activate the display link
  806. CVDisplayLinkStart(displayLink);
  807. }
  808. - (void) dealloc
  809. {
  810. [gameLock lock];
  811. // Release the display link
  812. CVDisplayLinkStop(displayLink);
  813. CVDisplayLinkRelease(displayLink);
  814. _game->exit();
  815. [gameLock unlock];
  816. [super dealloc];
  817. }
  818. - (void)resumeDisplayRenderer
  819. {
  820. [gameLock lock];
  821. CVDisplayLinkStop(displayLink);
  822. [gameLock unlock];
  823. }
  824. - (void)haltDisplayRenderer
  825. {
  826. [gameLock lock];
  827. CVDisplayLinkStop(displayLink);
  828. [gameLock unlock];
  829. }
  830. - (void) mouse: (Mouse::MouseEvent) mouseEvent orTouchEvent: (Touch::TouchEvent) touchEvent x: (float) x y: (float) y s: (int) s
  831. {
  832. [__view->gameLock lock];
  833. if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, s))
  834. {
  835. gameplay::Platform::touchEventInternal(touchEvent, x, y, 0);
  836. }
  837. [__view->gameLock unlock];
  838. }
  839. - (void) mouseDown: (NSEvent*) event
  840. {
  841. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  842. __leftMouseDown = true;
  843. [self mouse: Mouse::MOUSE_PRESS_LEFT_BUTTON orTouchEvent: Touch::TOUCH_PRESS x: point.x y: __height - point.y s: 0];
  844. }
  845. - (void) mouseUp: (NSEvent*) event
  846. {
  847. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  848. __leftMouseDown = false;
  849. [self mouse: Mouse::MOUSE_RELEASE_LEFT_BUTTON orTouchEvent: Touch::TOUCH_RELEASE x: point.x y: __height - point.y s: 0];
  850. }
  851. // helper function to handle mouse capture
  852. bool getMousePointForEvent(NSPoint& point, NSEvent* event)
  853. {
  854. if (__mouseCaptured)
  855. {
  856. if (__mouseCapturedFirstPass)
  857. {
  858. // Discard the first mouseMoved event following transition into capture
  859. // since it contains bogus x,y data.
  860. __mouseCapturedFirstPass = false;
  861. return false;
  862. }
  863. point.x = [event deltaX];
  864. point.y = [event deltaY];
  865. NSWindow* window = __view.window;
  866. NSRect rect = window.frame;
  867. CGPoint centerPoint;
  868. centerPoint.x = rect.origin.x + (rect.size.width / 2);
  869. centerPoint.y = rect.origin.y + (rect.size.height / 2);
  870. CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
  871. }
  872. else
  873. {
  874. point.y = __height - point.y;
  875. }
  876. return true;
  877. }
  878. - (void)mouseMoved:(NSEvent*) event
  879. {
  880. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  881. if (!getMousePointForEvent(point, event))
  882. {
  883. return;
  884. }
  885. [__view->gameLock lock];
  886. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, point.y, 0);
  887. [__view->gameLock unlock];
  888. }
  889. - (void) mouseDragged: (NSEvent*) event
  890. {
  891. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  892. if (__leftMouseDown)
  893. {
  894. if (__mouseCaptured)
  895. {
  896. if (!getMousePointForEvent(point, event))
  897. {
  898. return;
  899. }
  900. [__view->gameLock lock];
  901. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, point.y, 0);
  902. [__view->gameLock unlock];
  903. }
  904. else
  905. {
  906. [self mouse: Mouse::MOUSE_MOVE orTouchEvent: Touch::TOUCH_MOVE x: point.x y: __height - point.y s: 0];
  907. }
  908. }
  909. }
  910. - (void) rightMouseDown: (NSEvent*) event
  911. {
  912. __rightMouseDown = true;
  913. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  914. [__view->gameLock lock];
  915. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
  916. [__view->gameLock unlock];
  917. }
  918. - (void) rightMouseUp: (NSEvent*) event
  919. {
  920. __rightMouseDown = false;
  921. NSPoint point = [event locationInWindow];
  922. [__view->gameLock lock];
  923. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
  924. [__view->gameLock unlock];
  925. }
  926. - (void) rightMouseDragged: (NSEvent*) event
  927. {
  928. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  929. if (!getMousePointForEvent(point, event))
  930. {
  931. return;
  932. }
  933. // In right-mouse case, whether __rightMouseDown is true or false
  934. // this should not matter, mouse move is still occuring
  935. [__view->gameLock lock];
  936. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, point.y, 0);
  937. [__view->gameLock unlock];
  938. }
  939. - (void)otherMouseDown: (NSEvent*) event
  940. {
  941. __otherMouseDown = true;
  942. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  943. [__view->gameLock lock];
  944. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
  945. [__view->gameLock unlock];
  946. }
  947. - (void)otherMouseUp: (NSEvent*) event
  948. {
  949. __otherMouseDown = false;
  950. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  951. [__view->gameLock lock];
  952. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
  953. [__view->gameLock unlock];
  954. }
  955. - (void)otherMouseDragged: (NSEvent*) event
  956. {
  957. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  958. [__view->gameLock lock];
  959. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
  960. [__view->gameLock unlock];
  961. }
  962. - (void) mouseEntered: (NSEvent*)event
  963. {
  964. __hasMouse = true;
  965. }
  966. - (void)scrollWheel: (NSEvent*) event
  967. {
  968. NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
  969. [__view->gameLock lock];
  970. gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
  971. [__view->gameLock unlock];
  972. }
  973. - (void) mouseExited: (NSEvent*)event
  974. {
  975. __leftMouseDown = false;
  976. __rightMouseDown = false;
  977. __otherMouseDown = false;
  978. __hasMouse = false;
  979. }
  980. - (BOOL)acceptsFirstResponder
  981. {
  982. return YES;
  983. }
  984. int getKey(unsigned short keyCode, unsigned int modifierFlags)
  985. {
  986. __shiftDown = (modifierFlags & NSShiftKeyMask);
  987. unsigned int caps = (__shiftDown ? 1 : 0) ^ ((modifierFlags & NSAlphaShiftKeyMask) ? 1 : 0);
  988. switch(keyCode)
  989. {
  990. case 0x69:
  991. return Keyboard::KEY_PRINT;
  992. case 0x35:
  993. return Keyboard::KEY_ESCAPE;
  994. case 0x33:
  995. return Keyboard::KEY_BACKSPACE;
  996. case 0x30:
  997. return Keyboard::KEY_TAB;
  998. case 0x24:
  999. return Keyboard::KEY_RETURN;
  1000. case 0x72:
  1001. return Keyboard::KEY_INSERT;
  1002. case 0x73:
  1003. return Keyboard::KEY_HOME;
  1004. case 0x74:
  1005. return Keyboard::KEY_PG_UP;
  1006. case 0x79:
  1007. return Keyboard::KEY_PG_DOWN;
  1008. case 0x75:
  1009. return Keyboard::KEY_DELETE;
  1010. case 0x77:
  1011. return Keyboard::KEY_END;
  1012. case 0x7B:
  1013. return Keyboard::KEY_LEFT_ARROW;
  1014. case 0x7C:
  1015. return Keyboard::KEY_RIGHT_ARROW;
  1016. case 0x7E:
  1017. return Keyboard::KEY_UP_ARROW;
  1018. case 0x7D:
  1019. return Keyboard::KEY_DOWN_ARROW;
  1020. case 0x47:
  1021. return Keyboard::KEY_NUM_LOCK;
  1022. case 0x45:
  1023. return Keyboard::KEY_KP_PLUS;
  1024. case 0x4E:
  1025. return Keyboard::KEY_KP_MINUS;
  1026. case 0x43:
  1027. return Keyboard::KEY_KP_MULTIPLY;
  1028. case 0x4B:
  1029. return Keyboard::KEY_KP_DIVIDE;
  1030. case 0x59:
  1031. return Keyboard::KEY_KP_HOME;
  1032. case 0x5B:
  1033. return Keyboard::KEY_KP_UP;
  1034. case 0x5C:
  1035. return Keyboard::KEY_KP_PG_UP;
  1036. case 0x56:
  1037. return Keyboard::KEY_KP_LEFT;
  1038. case 0x57:
  1039. return Keyboard::KEY_KP_FIVE;
  1040. case 0x58:
  1041. return Keyboard::KEY_KP_RIGHT;
  1042. case 0x53:
  1043. return Keyboard::KEY_KP_END;
  1044. case 0x54:
  1045. return Keyboard::KEY_KP_DOWN;
  1046. case 0x55:
  1047. return Keyboard::KEY_KP_PG_DOWN;
  1048. case 0x52:
  1049. return Keyboard::KEY_KP_INSERT;
  1050. case 0x41:
  1051. return Keyboard::KEY_KP_DELETE;
  1052. case 0x7A:
  1053. return Keyboard::KEY_F1;
  1054. case 0x78:
  1055. return Keyboard::KEY_F2;
  1056. case 0x63:
  1057. return Keyboard::KEY_F3;
  1058. case 0x76:
  1059. return Keyboard::KEY_F4;
  1060. case 0x60:
  1061. return Keyboard::KEY_F5;
  1062. case 0x61:
  1063. return Keyboard::KEY_F6;
  1064. case 0x62:
  1065. return Keyboard::KEY_F7;
  1066. case 0x64:
  1067. return Keyboard::KEY_F8;
  1068. case 0x65:
  1069. return Keyboard::KEY_F9;
  1070. case 0x6D:
  1071. return Keyboard::KEY_F10;
  1072. // MACOS reserved:
  1073. // return Keyboard::KEY_F11;
  1074. // return Keyboard::KEY_F12;
  1075. // return Keyboard::KEY_PAUSE;
  1076. // return Keyboard::KEY_SCROLL_LOCK;
  1077. case 0x31:
  1078. return Keyboard::KEY_SPACE;
  1079. case 0x1D:
  1080. return __shiftDown ? Keyboard::KEY_RIGHT_PARENTHESIS : Keyboard::KEY_ZERO;
  1081. case 0x12:
  1082. return __shiftDown ? Keyboard::KEY_EXCLAM : Keyboard::KEY_ONE;
  1083. case 0x13:
  1084. return __shiftDown ? Keyboard::KEY_AT : Keyboard::KEY_TWO;
  1085. case 0x14:
  1086. return __shiftDown ? Keyboard::KEY_NUMBER : Keyboard::KEY_THREE;
  1087. case 0x15:
  1088. return __shiftDown ? Keyboard::KEY_DOLLAR : Keyboard::KEY_FOUR;
  1089. case 0x17:
  1090. return __shiftDown ? Keyboard::KEY_PERCENT : Keyboard::KEY_FIVE;
  1091. case 0x16:
  1092. return __shiftDown ? Keyboard::KEY_CIRCUMFLEX : Keyboard::KEY_SIX;
  1093. case 0x1A:
  1094. return __shiftDown ? Keyboard::KEY_AMPERSAND : Keyboard::KEY_SEVEN;
  1095. case 0x1C:
  1096. return __shiftDown ? Keyboard::KEY_ASTERISK : Keyboard::KEY_EIGHT;
  1097. case 0x19:
  1098. return __shiftDown ? Keyboard::KEY_LEFT_PARENTHESIS : Keyboard::KEY_NINE;
  1099. case 0x18:
  1100. return __shiftDown ? Keyboard::KEY_EQUAL : Keyboard::KEY_PLUS;
  1101. case 0x2B:
  1102. return __shiftDown ? Keyboard::KEY_LESS_THAN : Keyboard::KEY_COMMA;
  1103. case 0x1B:
  1104. return __shiftDown ? Keyboard::KEY_UNDERSCORE : Keyboard::KEY_MINUS;
  1105. case 0x2F:
  1106. return __shiftDown ? Keyboard::KEY_GREATER_THAN : Keyboard::KEY_PERIOD;
  1107. case 0x29:
  1108. return __shiftDown ? Keyboard::KEY_COLON : Keyboard::KEY_SEMICOLON;
  1109. case 0x2C:
  1110. return __shiftDown ? Keyboard::KEY_QUESTION : Keyboard::KEY_SLASH;
  1111. case 0x32:
  1112. return __shiftDown ? Keyboard::KEY_GRAVE : Keyboard::KEY_TILDE;
  1113. case 0x21:
  1114. return __shiftDown ? Keyboard::KEY_LEFT_BRACE : Keyboard::KEY_LEFT_BRACKET;
  1115. case 0x2A:
  1116. return __shiftDown ? Keyboard::KEY_BAR : Keyboard::KEY_BACK_SLASH;
  1117. case 0x1E:
  1118. return __shiftDown ? Keyboard::KEY_RIGHT_BRACE : Keyboard::KEY_RIGHT_BRACKET;
  1119. case 0x27:
  1120. return __shiftDown ? Keyboard::KEY_QUOTE : Keyboard::KEY_APOSTROPHE;
  1121. case 0x00:
  1122. return caps ? Keyboard::KEY_CAPITAL_A : Keyboard::KEY_A;
  1123. case 0x0B:
  1124. return caps ? Keyboard::KEY_CAPITAL_B : Keyboard::KEY_B;
  1125. case 0x08:
  1126. return caps ? Keyboard::KEY_CAPITAL_C : Keyboard::KEY_C;
  1127. case 0x02:
  1128. return caps ? Keyboard::KEY_CAPITAL_D : Keyboard::KEY_D;
  1129. case 0x0E:
  1130. return caps ? Keyboard::KEY_CAPITAL_E : Keyboard::KEY_E;
  1131. case 0x03:
  1132. return caps ? Keyboard::KEY_CAPITAL_F : Keyboard::KEY_F;
  1133. case 0x05:
  1134. return caps ? Keyboard::KEY_CAPITAL_G : Keyboard::KEY_G;
  1135. case 0x04:
  1136. return caps ? Keyboard::KEY_CAPITAL_H : Keyboard::KEY_H;
  1137. case 0x22:
  1138. return caps ? Keyboard::KEY_CAPITAL_I : Keyboard::KEY_I;
  1139. case 0x26:
  1140. return caps ? Keyboard::KEY_CAPITAL_J : Keyboard::KEY_J;
  1141. case 0x28:
  1142. return caps ? Keyboard::KEY_CAPITAL_K : Keyboard::KEY_K;
  1143. case 0x25:
  1144. return caps ? Keyboard::KEY_CAPITAL_L : Keyboard::KEY_L;
  1145. case 0x2E:
  1146. return caps ? Keyboard::KEY_CAPITAL_M : Keyboard::KEY_M;
  1147. case 0x2D:
  1148. return caps ? Keyboard::KEY_CAPITAL_N : Keyboard::KEY_N;
  1149. case 0x1F:
  1150. return caps ? Keyboard::KEY_CAPITAL_O : Keyboard::KEY_O;
  1151. case 0x23:
  1152. return caps ? Keyboard::KEY_CAPITAL_P : Keyboard::KEY_P;
  1153. case 0x0C:
  1154. return caps ? Keyboard::KEY_CAPITAL_Q : Keyboard::KEY_Q;
  1155. case 0x0F:
  1156. return caps ? Keyboard::KEY_CAPITAL_R : Keyboard::KEY_R;
  1157. case 0x01:
  1158. return caps ? Keyboard::KEY_CAPITAL_S : Keyboard::KEY_S;
  1159. case 0x11:
  1160. return caps ? Keyboard::KEY_CAPITAL_T : Keyboard::KEY_T;
  1161. case 0x20:
  1162. return caps ? Keyboard::KEY_CAPITAL_U : Keyboard::KEY_U;
  1163. case 0x09:
  1164. return caps ? Keyboard::KEY_CAPITAL_V : Keyboard::KEY_V;
  1165. case 0x0D:
  1166. return caps ? Keyboard::KEY_CAPITAL_W : Keyboard::KEY_W;
  1167. case 0x07:
  1168. return caps ? Keyboard::KEY_CAPITAL_X : Keyboard::KEY_X;
  1169. case 0x10:
  1170. return caps ? Keyboard::KEY_CAPITAL_Y : Keyboard::KEY_Y;
  1171. case 0x06:
  1172. return caps ? Keyboard::KEY_CAPITAL_Z : Keyboard::KEY_Z;
  1173. default:
  1174. return Keyboard::KEY_NONE;
  1175. }
  1176. }
  1177. /**
  1178. * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
  1179. */
  1180. int getUnicode(int key)
  1181. {
  1182. switch (key)
  1183. {
  1184. case Keyboard::KEY_BACKSPACE:
  1185. return 0x0008;
  1186. case Keyboard::KEY_TAB:
  1187. return 0x0009;
  1188. case Keyboard::KEY_RETURN:
  1189. case Keyboard::KEY_KP_ENTER:
  1190. return 0x000A;
  1191. case Keyboard::KEY_ESCAPE:
  1192. return 0x001B;
  1193. case Keyboard::KEY_SPACE:
  1194. case Keyboard::KEY_EXCLAM:
  1195. case Keyboard::KEY_QUOTE:
  1196. case Keyboard::KEY_NUMBER:
  1197. case Keyboard::KEY_DOLLAR:
  1198. case Keyboard::KEY_PERCENT:
  1199. case Keyboard::KEY_CIRCUMFLEX:
  1200. case Keyboard::KEY_AMPERSAND:
  1201. case Keyboard::KEY_APOSTROPHE:
  1202. case Keyboard::KEY_LEFT_PARENTHESIS:
  1203. case Keyboard::KEY_RIGHT_PARENTHESIS:
  1204. case Keyboard::KEY_ASTERISK:
  1205. case Keyboard::KEY_PLUS:
  1206. case Keyboard::KEY_COMMA:
  1207. case Keyboard::KEY_MINUS:
  1208. case Keyboard::KEY_PERIOD:
  1209. case Keyboard::KEY_SLASH:
  1210. case Keyboard::KEY_ZERO:
  1211. case Keyboard::KEY_ONE:
  1212. case Keyboard::KEY_TWO:
  1213. case Keyboard::KEY_THREE:
  1214. case Keyboard::KEY_FOUR:
  1215. case Keyboard::KEY_FIVE:
  1216. case Keyboard::KEY_SIX:
  1217. case Keyboard::KEY_SEVEN:
  1218. case Keyboard::KEY_EIGHT:
  1219. case Keyboard::KEY_NINE:
  1220. case Keyboard::KEY_COLON:
  1221. case Keyboard::KEY_SEMICOLON:
  1222. case Keyboard::KEY_LESS_THAN:
  1223. case Keyboard::KEY_EQUAL:
  1224. case Keyboard::KEY_GREATER_THAN:
  1225. case Keyboard::KEY_QUESTION:
  1226. case Keyboard::KEY_AT:
  1227. case Keyboard::KEY_CAPITAL_A:
  1228. case Keyboard::KEY_CAPITAL_B:
  1229. case Keyboard::KEY_CAPITAL_C:
  1230. case Keyboard::KEY_CAPITAL_D:
  1231. case Keyboard::KEY_CAPITAL_E:
  1232. case Keyboard::KEY_CAPITAL_F:
  1233. case Keyboard::KEY_CAPITAL_G:
  1234. case Keyboard::KEY_CAPITAL_H:
  1235. case Keyboard::KEY_CAPITAL_I:
  1236. case Keyboard::KEY_CAPITAL_J:
  1237. case Keyboard::KEY_CAPITAL_K:
  1238. case Keyboard::KEY_CAPITAL_L:
  1239. case Keyboard::KEY_CAPITAL_M:
  1240. case Keyboard::KEY_CAPITAL_N:
  1241. case Keyboard::KEY_CAPITAL_O:
  1242. case Keyboard::KEY_CAPITAL_P:
  1243. case Keyboard::KEY_CAPITAL_Q:
  1244. case Keyboard::KEY_CAPITAL_R:
  1245. case Keyboard::KEY_CAPITAL_S:
  1246. case Keyboard::KEY_CAPITAL_T:
  1247. case Keyboard::KEY_CAPITAL_U:
  1248. case Keyboard::KEY_CAPITAL_V:
  1249. case Keyboard::KEY_CAPITAL_W:
  1250. case Keyboard::KEY_CAPITAL_X:
  1251. case Keyboard::KEY_CAPITAL_Y:
  1252. case Keyboard::KEY_CAPITAL_Z:
  1253. case Keyboard::KEY_LEFT_BRACKET:
  1254. case Keyboard::KEY_BACK_SLASH:
  1255. case Keyboard::KEY_RIGHT_BRACKET:
  1256. case Keyboard::KEY_UNDERSCORE:
  1257. case Keyboard::KEY_GRAVE:
  1258. case Keyboard::KEY_A:
  1259. case Keyboard::KEY_B:
  1260. case Keyboard::KEY_C:
  1261. case Keyboard::KEY_D:
  1262. case Keyboard::KEY_E:
  1263. case Keyboard::KEY_F:
  1264. case Keyboard::KEY_G:
  1265. case Keyboard::KEY_H:
  1266. case Keyboard::KEY_I:
  1267. case Keyboard::KEY_J:
  1268. case Keyboard::KEY_K:
  1269. case Keyboard::KEY_L:
  1270. case Keyboard::KEY_M:
  1271. case Keyboard::KEY_N:
  1272. case Keyboard::KEY_O:
  1273. case Keyboard::KEY_P:
  1274. case Keyboard::KEY_Q:
  1275. case Keyboard::KEY_R:
  1276. case Keyboard::KEY_S:
  1277. case Keyboard::KEY_T:
  1278. case Keyboard::KEY_U:
  1279. case Keyboard::KEY_V:
  1280. case Keyboard::KEY_W:
  1281. case Keyboard::KEY_X:
  1282. case Keyboard::KEY_Y:
  1283. case Keyboard::KEY_Z:
  1284. case Keyboard::KEY_LEFT_BRACE:
  1285. case Keyboard::KEY_BAR:
  1286. case Keyboard::KEY_RIGHT_BRACE:
  1287. case Keyboard::KEY_TILDE:
  1288. return key;
  1289. default:
  1290. return 0;
  1291. }
  1292. }
  1293. - (void)flagsChanged: (NSEvent*)event
  1294. {
  1295. unsigned int keyCode = [event keyCode];
  1296. unsigned int flags = [event modifierFlags];
  1297. [__view->gameLock lock];
  1298. switch (keyCode)
  1299. {
  1300. case 0x39:
  1301. gameplay::Platform::keyEventInternal((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
  1302. break;
  1303. case 0x38:
  1304. gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
  1305. break;
  1306. case 0x3C:
  1307. gameplay::Platform::keyEventInternal((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
  1308. break;
  1309. case 0x3A:
  1310. gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
  1311. break;
  1312. case 0x3D:
  1313. gameplay::Platform::keyEventInternal((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
  1314. break;
  1315. case 0x3B:
  1316. gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
  1317. break;
  1318. case 0x3E:
  1319. gameplay::Platform::keyEventInternal((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
  1320. break;
  1321. case 0x37:
  1322. gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
  1323. break;
  1324. case 0x36:
  1325. gameplay::Platform::keyEventInternal((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
  1326. break;
  1327. }
  1328. [__view->gameLock unlock];
  1329. }
  1330. - (void) keyDown: (NSEvent*) event
  1331. {
  1332. if ([event isARepeat] == NO)
  1333. {
  1334. int key = getKey([event keyCode], [event modifierFlags]);
  1335. [__view->gameLock lock];
  1336. gameplay::Platform::keyEventInternal(Keyboard::KEY_PRESS, key);
  1337. int character = getUnicode(key);
  1338. if (character)
  1339. {
  1340. gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
  1341. }
  1342. [__view->gameLock unlock];
  1343. }
  1344. }
  1345. - (void) keyUp: (NSEvent*) event
  1346. {
  1347. [__view->gameLock lock];
  1348. gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey([event keyCode], [event modifierFlags]));
  1349. [__view->gameLock unlock];
  1350. }
  1351. // Gesture support for Mac OS X Trackpads
  1352. - (bool)isGestureRegistered: (Gesture::GestureEvent) evt
  1353. {
  1354. return ((_gestureEvents & evt) == evt);
  1355. }
  1356. - (void)registerGesture: (Gesture::GestureEvent) evt
  1357. {
  1358. _gestureEvents |= evt;
  1359. }
  1360. - (void)unregisterGesture: (Gesture::GestureEvent) evt
  1361. {
  1362. _gestureEvents &= (~evt);
  1363. }
  1364. - (void)magnifyWithEvent:(NSEvent *)event
  1365. {
  1366. if([self isGestureRegistered:Gesture::GESTURE_PINCH] == false) return;
  1367. NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:nil];
  1368. // Approximate the center by adding and averageing for now
  1369. // Note this is centroid on the physical device be used for touching, not the display
  1370. float xavg = 0.0f;
  1371. float yavg = 0.0f;
  1372. for(NSTouch *t in touches) {
  1373. xavg += [t normalizedPosition].x;
  1374. yavg += [t normalizedPosition].y;
  1375. }
  1376. xavg /= [touches count];
  1377. yavg /= [touches count];
  1378. [gameLock lock];
  1379. gameplay::Platform::gesturePinchEventInternal((int)xavg, (int)yavg, [event magnification]);
  1380. [gameLock unlock];
  1381. }
  1382. @end
  1383. @interface FullscreenWindow : NSWindow
  1384. {
  1385. }
  1386. - (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController;
  1387. @end
  1388. @implementation FullscreenWindow
  1389. - (BOOL)canBecomeKeyWindow
  1390. {
  1391. return YES;
  1392. }
  1393. - (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController *)gameCenterViewController
  1394. {
  1395. GKDialogController *sdc = [GKDialogController sharedDialogController];
  1396. [sdc dismiss: self];
  1397. }
  1398. @end
  1399. namespace gameplay
  1400. {
  1401. extern void print(const char* format, ...)
  1402. {
  1403. GP_ASSERT(format);
  1404. va_list argptr;
  1405. va_start(argptr, format);
  1406. vfprintf(stderr, format, argptr);
  1407. va_end(argptr);
  1408. }
  1409. extern int strcmpnocase(const char* s1, const char* s2)
  1410. {
  1411. return strcasecmp(s1, s2);
  1412. }
  1413. Platform::Platform(Game* game)
  1414. : _game(game)
  1415. {
  1416. __activeGamepads = [[NSMutableDictionary alloc] init];
  1417. __gamepads = [[NSMutableArray alloc] init];
  1418. __hidManagerRef = IOHIDManagerCreate(CFAllocatorGetDefault(), kIOHIDOptionsTypeNone);
  1419. IOHIDManagerRegisterDeviceMatchingCallback(__hidManagerRef, hidDeviceDiscoveredCallback, NULL);
  1420. IOHIDManagerRegisterDeviceRemovalCallback(__hidManagerRef, hidDeviceRemovalCallback, NULL);
  1421. CFMutableArrayRef matching = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
  1422. if (matching)
  1423. {
  1424. CFDictionaryRef matchingJoystick = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
  1425. CFDictionaryRef matchingGamepad = IOHIDCreateDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
  1426. if (matchingJoystick && matchingGamepad)
  1427. {
  1428. CFArrayAppendValue(matching, matchingJoystick);
  1429. CFRelease(matchingJoystick);
  1430. CFArrayAppendValue(matching, matchingGamepad);
  1431. CFRelease(matchingGamepad);
  1432. IOHIDManagerSetDeviceMatchingMultiple(__hidManagerRef, matching);
  1433. }
  1434. }
  1435. IOHIDManagerScheduleWithRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  1436. IOReturn kr = IOHIDManagerOpen(__hidManagerRef, kIOHIDOptionsTypeNone);
  1437. assert(kr == 0);
  1438. }
  1439. Platform::~Platform()
  1440. {
  1441. IOHIDManagerUnscheduleFromRunLoop(__hidManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  1442. IOHIDManagerClose(__hidManagerRef, kIOHIDOptionsTypeNone);
  1443. CFRelease(__hidManagerRef);
  1444. __hidManagerRef = NULL;
  1445. [__activeGamepads release];
  1446. __activeGamepads = NULL;
  1447. [__gamepads release];
  1448. __gamepads = NULL;
  1449. }
  1450. Platform* Platform::create(Game* game)
  1451. {
  1452. Platform* platform = new Platform(game);
  1453. return platform;
  1454. }
  1455. int Platform::enterMessagePump()
  1456. {
  1457. NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
  1458. NSString* path = [bundlePath stringByAppendingString:@"/Contents/Resources/"];
  1459. FileSystem::setResourcePath([path cStringUsingEncoding:NSASCIIStringEncoding]);
  1460. // Read window settings from config.
  1461. if (_game->getConfig())
  1462. {
  1463. Properties* config = _game->getConfig()->getNamespace("window", true);
  1464. if (config)
  1465. {
  1466. // Read window title.
  1467. __title = const_cast<char *>(config->getString("title"));
  1468. // Read window size.
  1469. int width = config->getInt("width");
  1470. if (width != 0)
  1471. __width = width;
  1472. int height = config->getInt("height");
  1473. if (height != 0)
  1474. __height = height;
  1475. // Read fullscreen state.
  1476. __fullscreen = config->getBool("fullscreen");
  1477. if (__fullscreen && width == 0 && height == 0)
  1478. {
  1479. CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
  1480. __width = CGRectGetWidth(mainMonitor);
  1481. __height = CGRectGetHeight(mainMonitor);
  1482. }
  1483. // Read resizable state.
  1484. __resizable = config->getBool("resizable");
  1485. }
  1486. }
  1487. NSAutoreleasePool* pool = [NSAutoreleasePool new];
  1488. NSApplication* app = [NSApplication sharedApplication];
  1489. NSRect screenBounds = [[NSScreen mainScreen] frame];
  1490. NSRect viewBounds = NSMakeRect(0, 0, __width, __height);
  1491. __view = [[View alloc] initWithFrame:viewBounds];
  1492. if (__view == NULL)
  1493. {
  1494. GP_ERROR("Failed to create view: exiting.");
  1495. return EXIT_FAILURE;
  1496. }
  1497. NSRect centered = NSMakeRect(NSMidX(screenBounds) - NSMidX(viewBounds),
  1498. NSMidY(screenBounds) - NSMidY(viewBounds),
  1499. viewBounds.size.width,
  1500. viewBounds.size.height);
  1501. NSWindow* window = NULL;
  1502. if (__fullscreen)
  1503. {
  1504. window = [[FullscreenWindow alloc]
  1505. initWithContentRect:screenBounds
  1506. styleMask:NSBorderlessWindowMask
  1507. backing:NSBackingStoreBuffered
  1508. defer:NO];
  1509. }
  1510. else
  1511. {
  1512. window = [[NSWindow alloc]
  1513. initWithContentRect:centered
  1514. styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
  1515. backing:NSBackingStoreBuffered
  1516. defer:NO];
  1517. }
  1518. [window setAcceptsMouseMovedEvents:YES];
  1519. [window setContentView:__view];
  1520. [window setDelegate:__view];
  1521. [__view release];
  1522. [app run];
  1523. [pool release];
  1524. return EXIT_SUCCESS;
  1525. }
  1526. void Platform::signalShutdown()
  1527. {
  1528. [__view haltDisplayRenderer];
  1529. // Don't perform terminate right away, enqueue to give game object
  1530. // a chance to cleanup
  1531. NSApplication* app = [NSApplication sharedApplication];
  1532. [app performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
  1533. }
  1534. bool Platform::canExit()
  1535. {
  1536. return true;
  1537. }
  1538. unsigned int Platform::getDisplayWidth()
  1539. {
  1540. return __width;
  1541. }
  1542. unsigned int Platform::getDisplayHeight()
  1543. {
  1544. return __height;
  1545. }
  1546. double Platform::getAbsoluteTime()
  1547. {
  1548. __timeAbsolute = getMachTimeInMilliseconds();
  1549. return __timeAbsolute;
  1550. }
  1551. void Platform::setAbsoluteTime(double time)
  1552. {
  1553. __timeAbsolute = time;
  1554. }
  1555. bool Platform::isVsync()
  1556. {
  1557. return __vsync;
  1558. }
  1559. void Platform::setVsync(bool enable)
  1560. {
  1561. __vsync = enable;
  1562. GLint swapInt = enable ? 1 : 0;
  1563. [[__view openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
  1564. }
  1565. void Platform::swapBuffers()
  1566. {
  1567. if (__view)
  1568. CGLFlushDrawable((CGLContextObj)[[__view openGLContext] CGLContextObj]);
  1569. }
  1570. void Platform::sleep(long ms)
  1571. {
  1572. usleep(ms * 1000);
  1573. }
  1574. void Platform::setMultiSampling(bool enabled)
  1575. {
  1576. if (enabled == __multiSampling)
  1577. {
  1578. return;
  1579. }
  1580. //todo
  1581. __multiSampling = enabled;
  1582. }
  1583. bool Platform::isMultiSampling()
  1584. {
  1585. return __multiSampling;
  1586. }
  1587. void Platform::setMultiTouch(bool enabled)
  1588. {
  1589. }
  1590. bool Platform::isMultiTouch()
  1591. {
  1592. return true;
  1593. }
  1594. bool Platform::hasAccelerometer()
  1595. {
  1596. return false;
  1597. }
  1598. void Platform::getAccelerometerValues(float* pitch, float* roll)
  1599. {
  1600. GP_ASSERT(pitch);
  1601. GP_ASSERT(roll);
  1602. *pitch = 0;
  1603. *roll = 0;
  1604. }
  1605. void Platform::getSensorValues(float* accelX, float* accelY, float* accelZ, float* gyroX, float* gyroY, float* gyroZ)
  1606. {
  1607. if (accelX)
  1608. {
  1609. *accelX = 0;
  1610. }
  1611. if (accelY)
  1612. {
  1613. *accelY = 0;
  1614. }
  1615. if (accelZ)
  1616. {
  1617. *accelZ = 0;
  1618. }
  1619. if (gyroX)
  1620. {
  1621. *gyroX = 0;
  1622. }
  1623. if (gyroY)
  1624. {
  1625. *gyroY = 0;
  1626. }
  1627. if (gyroZ)
  1628. {
  1629. *gyroZ = 0;
  1630. }
  1631. }
  1632. void Platform::getArguments(int* argc, char*** argv)
  1633. {
  1634. if (argc)
  1635. *argc = __argc;
  1636. if (argv)
  1637. *argv = __argv;
  1638. }
  1639. bool Platform::hasMouse()
  1640. {
  1641. return true;
  1642. }
  1643. void Platform::setMouseCaptured(bool captured)
  1644. {
  1645. if (captured != __mouseCaptured)
  1646. {
  1647. if (captured)
  1648. {
  1649. [NSCursor hide];
  1650. __mouseCapturedFirstPass = true;
  1651. }
  1652. else
  1653. {
  1654. [NSCursor unhide];
  1655. }
  1656. NSWindow* window = __view.window;
  1657. NSRect rect = window.frame;
  1658. CGPoint centerPoint;
  1659. centerPoint.x = rect.origin.x + (rect.size.width / 2);
  1660. centerPoint.y = rect.origin.y + (rect.size.height / 2);
  1661. CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
  1662. __mouseCaptured = captured;
  1663. }
  1664. }
  1665. bool Platform::isMouseCaptured()
  1666. {
  1667. return __mouseCaptured;
  1668. }
  1669. void Platform::setCursorVisible(bool visible)
  1670. {
  1671. if (visible != __cursorVisible)
  1672. {
  1673. if (visible)
  1674. {
  1675. [NSCursor unhide];
  1676. }
  1677. else
  1678. {
  1679. [NSCursor hide];
  1680. }
  1681. __cursorVisible = visible;
  1682. }
  1683. }
  1684. bool Platform::isCursorVisible()
  1685. {
  1686. return __cursorVisible;
  1687. }
  1688. void Platform::displayKeyboard(bool display)
  1689. {
  1690. // Do nothing.
  1691. }
  1692. void Platform::shutdownInternal()
  1693. {
  1694. Game::getInstance()->shutdown();
  1695. }
  1696. bool Platform::isGestureSupported(Gesture::GestureEvent evt)
  1697. {
  1698. // Swipe unsupported as it is considered moving mouse cursor
  1699. // Two fingers is scrolling
  1700. // Three fingers is swipe, but is not always enabled on users system
  1701. // Tap not supported as it is considered a mouse click/button click
  1702. // on some systems making it difficult to differentiate
  1703. switch(evt)
  1704. {
  1705. case Gesture::GESTURE_PINCH:
  1706. return true;
  1707. default:
  1708. break;
  1709. }
  1710. return false;
  1711. }
  1712. void Platform::registerGesture(Gesture::GestureEvent evt)
  1713. {
  1714. [__view registerGesture:evt];
  1715. }
  1716. void Platform::unregisterGesture(Gesture::GestureEvent evt)
  1717. {
  1718. [__view unregisterGesture:evt];
  1719. }
  1720. bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
  1721. {
  1722. return [__view isGestureRegistered:evt];
  1723. }
  1724. void Platform::pollGamepadState(Gamepad* gamepad)
  1725. {
  1726. HIDGamepad* gp = gamepadForGameHandle(gamepad->_handle);
  1727. if (gp)
  1728. {
  1729. // Haven't figured out how to have the triggers not also show up in the buttons array.
  1730. // So for now a value of -1 means "Don't map this button."
  1731. static const int PS3Mapping[17] = {
  1732. Gamepad::BUTTON_MENU1, // 0x0001
  1733. Gamepad::BUTTON_L3, // 0x0002
  1734. Gamepad::BUTTON_R3, // 0x0004
  1735. Gamepad::BUTTON_MENU2, // 0x0008
  1736. Gamepad::BUTTON_UP, // 0x0010
  1737. Gamepad::BUTTON_RIGHT, // 0x0020
  1738. Gamepad::BUTTON_DOWN, // 0x0040
  1739. Gamepad::BUTTON_LEFT, // 0x0080
  1740. -1, // Gamepad::BUTTON_L2, // 0x0100
  1741. -1, // Gamepad::BUTTON_R2, // 0x0200
  1742. Gamepad::BUTTON_L1, // 0x0400
  1743. Gamepad::BUTTON_R1, // 0x0800
  1744. Gamepad::BUTTON_Y, // 0x1000
  1745. Gamepad::BUTTON_B, // 0x2000
  1746. Gamepad::BUTTON_A, // 0x4000
  1747. Gamepad::BUTTON_X, // 0x8000
  1748. Gamepad::BUTTON_MENU3 // 0x10000
  1749. };
  1750. static const int XBox360Mapping[20] = {
  1751. -1, -1, -1, -1, -1,
  1752. Gamepad::BUTTON_UP,
  1753. Gamepad::BUTTON_DOWN,
  1754. Gamepad::BUTTON_LEFT,
  1755. Gamepad::BUTTON_RIGHT,
  1756. Gamepad::BUTTON_MENU2,
  1757. Gamepad::BUTTON_MENU1,
  1758. Gamepad::BUTTON_L3,
  1759. Gamepad::BUTTON_R3,
  1760. Gamepad::BUTTON_L1,
  1761. Gamepad::BUTTON_R1,
  1762. Gamepad::BUTTON_MENU3,
  1763. Gamepad::BUTTON_A,
  1764. Gamepad::BUTTON_B,
  1765. Gamepad::BUTTON_X,
  1766. Gamepad::BUTTON_Y
  1767. };
  1768. static const int SteelSeriesFreeMapping[13] = {
  1769. Gamepad::BUTTON_A,
  1770. Gamepad::BUTTON_B,
  1771. -1,
  1772. Gamepad::BUTTON_X,
  1773. Gamepad::BUTTON_Y,
  1774. -1,
  1775. Gamepad::BUTTON_L1,
  1776. Gamepad::BUTTON_R1,
  1777. -1, -1, -1,
  1778. Gamepad::BUTTON_MENU2,
  1779. Gamepad::BUTTON_MENU1
  1780. };
  1781. static const int GametelMapping103[12] = {
  1782. Gamepad::BUTTON_B,
  1783. Gamepad::BUTTON_X,
  1784. Gamepad::BUTTON_Y,
  1785. Gamepad::BUTTON_A,
  1786. Gamepad::BUTTON_L1,
  1787. Gamepad::BUTTON_R1,
  1788. Gamepad::BUTTON_MENU1,
  1789. Gamepad::BUTTON_MENU2,
  1790. Gamepad::BUTTON_RIGHT,
  1791. Gamepad::BUTTON_LEFT,
  1792. Gamepad::BUTTON_DOWN,
  1793. Gamepad::BUTTON_UP
  1794. };
  1795. const int* mapping = NULL;
  1796. float axisDeadZone = 0.0f;
  1797. if (gamepad->_vendorId == SONY_USB_VENDOR_ID &&
  1798. gamepad->_productId == SONY_USB_PS3_PRODUCT_ID)
  1799. {
  1800. mapping = PS3Mapping;
  1801. axisDeadZone = 0.07f;
  1802. }
  1803. else if (gamepad->_vendorId == MICROSOFT_VENDOR_ID &&
  1804. gamepad->_productId == MICROSOFT_XBOX360_PRODUCT_ID)
  1805. {
  1806. mapping = XBox360Mapping;
  1807. axisDeadZone = 0.2f;
  1808. }
  1809. else if (gamepad->_vendorId == STEELSERIES_VENDOR_ID &&
  1810. gamepad->_productId == STEELSERIES_FREE_PRODUCT_ID)
  1811. {
  1812. mapping = SteelSeriesFreeMapping;
  1813. axisDeadZone = 0.005f;
  1814. }
  1815. else if (gamepad->_vendorId == FRUCTEL_VENDOR_ID &&
  1816. gamepad->_productId == FRUCTEL_GAMETEL_PRODUCT_ID)
  1817. {
  1818. int ver = [gp versionNumber];
  1819. int major = ver >> 8;
  1820. int minor = ver & 0x00ff;
  1821. if (major >= 1 && minor > 1)
  1822. {
  1823. mapping = GametelMapping103;
  1824. }
  1825. }
  1826. unsigned int buttons = 0;
  1827. for (int i = 0; i < [gp numberOfButtons]; ++i)
  1828. {
  1829. HIDGamepadButton* b = [gp buttonAtIndex: i];
  1830. if ([b state])
  1831. {
  1832. // This button is down.
  1833. if (mapping)
  1834. {
  1835. if (mapping[i] >= 0)
  1836. buttons |= (1 << mapping[i]);
  1837. }
  1838. else
  1839. {
  1840. buttons |= (1 << i);
  1841. }
  1842. }
  1843. }
  1844. HIDGamepadAxis* hatSwitch = [gp getHatSwitch];
  1845. if (hatSwitch != NULL)
  1846. {
  1847. CFIndex v = [hatSwitch value];
  1848. switch (v)
  1849. {
  1850. case -1:
  1851. break;
  1852. case 0:
  1853. buttons |= (1 << Gamepad::BUTTON_UP);
  1854. break;
  1855. case 1:
  1856. buttons |= (1 << Gamepad::BUTTON_UP) | (1 << Gamepad::BUTTON_RIGHT);
  1857. break;
  1858. case 2:
  1859. buttons |= (1 << Gamepad::BUTTON_RIGHT);
  1860. break;
  1861. case 3:
  1862. buttons |= (1 << Gamepad::BUTTON_RIGHT) | (1 << Gamepad::BUTTON_DOWN);
  1863. break;
  1864. case 4:
  1865. buttons |= (1 << Gamepad::BUTTON_DOWN);
  1866. break;
  1867. case 5:
  1868. buttons |= (1 << Gamepad::BUTTON_DOWN) | (1 << Gamepad::BUTTON_LEFT);
  1869. break;
  1870. case 6:
  1871. buttons |= (1 << Gamepad::BUTTON_LEFT);
  1872. break;
  1873. case 7:
  1874. buttons |= (1 << Gamepad::BUTTON_LEFT) | (1 << Gamepad::BUTTON_UP);
  1875. break;
  1876. }
  1877. }
  1878. gamepad->setButtons(buttons);
  1879. for (unsigned int i = 0; i < [gp numberOfSticks]; ++i)
  1880. {
  1881. float rawX = [[gp axisAtIndex: i*2] calibratedValue];
  1882. float rawY = -[[gp axisAtIndex: i*2 + 1] calibratedValue];
  1883. if (std::fabs(rawX) <= axisDeadZone)
  1884. rawX = 0;
  1885. if (std::fabs(rawY) <= axisDeadZone)
  1886. rawY = 0;
  1887. gamepad->setJoystickValue(i, rawX, rawY);
  1888. }
  1889. for (unsigned int i = 0; i < [gp numberOfTriggerButtons]; ++i)
  1890. {
  1891. gamepad->setTriggerValue(i, [[gp triggerButtonAtIndex: i] calibratedStateValue]);
  1892. }
  1893. }
  1894. }
  1895. }
  1896. HIDGamepad* gamepadForLocationID(NSNumber* locationID)
  1897. {
  1898. HIDGamepad* fgamepad = NULL;
  1899. for(HIDGamepad* gamepad in __gamepads)
  1900. {
  1901. if([[gamepad locationID] isEqual:locationID])
  1902. {
  1903. fgamepad = gamepad;
  1904. break;
  1905. }
  1906. }
  1907. return fgamepad;
  1908. }
  1909. HIDGamepad* gamepadForLocationIDValue(unsigned int locationIDValue)
  1910. {
  1911. return gamepadForLocationID([NSNumber numberWithUnsignedInt:locationIDValue]);
  1912. }
  1913. HIDGamepad* gamepadForGameHandle(int gameHandle)
  1914. {
  1915. HIDGamepad* gamepad = NULL;
  1916. for(NSNumber* locationID in __activeGamepads)
  1917. {
  1918. NSNumber* handleID = [__activeGamepads objectForKey:locationID];
  1919. if([handleID integerValue] == gameHandle)
  1920. {
  1921. gamepad = gamepadForLocationID(locationID);
  1922. break;
  1923. }
  1924. }
  1925. return gamepad;
  1926. }
  1927. CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, UInt32 inUsage)
  1928. {
  1929. // create a dictionary to add usage page/usages to
  1930. CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  1931. if (result) {
  1932. if (inUsagePage)
  1933. {
  1934. // Add key for device type to refine the matching dictionary.
  1935. CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
  1936. if (pageCFNumberRef)
  1937. {
  1938. CFDictionarySetValue(result, CFSTR( kIOHIDDeviceUsagePageKey ), pageCFNumberRef);
  1939. CFRelease(pageCFNumberRef);
  1940. // note: the usage is only valid if the usage page is also defined
  1941. if (inUsage)
  1942. {
  1943. CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
  1944. if (usageCFNumberRef)
  1945. {
  1946. CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
  1947. CFRelease(usageCFNumberRef);
  1948. }
  1949. else
  1950. {
  1951. fprintf(stderr, "%s: CFNumberCreate( usage ) failed.", __PRETTY_FUNCTION__);
  1952. }
  1953. }
  1954. }
  1955. else
  1956. {
  1957. fprintf( stderr, "%s: CFNumberCreate( usage page ) failed.", __PRETTY_FUNCTION__);
  1958. }
  1959. }
  1960. }
  1961. else
  1962. {
  1963. fprintf( stderr, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__);
  1964. }
  1965. return result;
  1966. }
  1967. CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key)
  1968. {
  1969. CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
  1970. if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID())
  1971. {
  1972. return NULL;
  1973. }
  1974. return (CFStringRef)typeRef;
  1975. }
  1976. int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key)
  1977. {
  1978. CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
  1979. if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID())
  1980. {
  1981. return 0;
  1982. }
  1983. int value;
  1984. CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
  1985. return value;
  1986. }
  1987. static void hidDeviceDiscoveredCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
  1988. {
  1989. CFNumberRef locID = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
  1990. if(locID)
  1991. {
  1992. HIDGamepad* gamepad = [[HIDGamepad alloc] initWithDevice: device];
  1993. [__gamepads addObject:gamepad];
  1994. }
  1995. }
  1996. static void hidDeviceRemovalCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef device)
  1997. {
  1998. int removeIndex = -1;
  1999. NSNumber *locID = (NSNumber*)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDLocationIDKey));
  2000. if(locID)
  2001. {
  2002. for(int i = 0; i < [__gamepads count]; i++)
  2003. {
  2004. HIDGamepad* gamepad = [__gamepads objectAtIndex:i];
  2005. if([[gamepad locationID] isEqual:locID])
  2006. {
  2007. removeIndex = i;
  2008. break;
  2009. }
  2010. }
  2011. }
  2012. if(removeIndex >= 0)
  2013. {
  2014. [__gamepads removeObjectAtIndex:removeIndex];
  2015. }
  2016. }
  2017. static void hidDeviceValueAvailableCallback(void* inContext, IOReturn inResult, void* inSender)
  2018. {
  2019. HIDGamepad* d = (HIDGamepad*)inContext;
  2020. do
  2021. {
  2022. IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout( ( IOHIDQueueRef ) inSender, 0. );
  2023. if (!valueRef) break;
  2024. [d hidValueAvailable:valueRef];
  2025. CFRelease(valueRef); // Don't forget to release our HID value reference
  2026. } while (1);
  2027. }
  2028. bool Platform::launchURL(const char *url)
  2029. {
  2030. if (url == NULL || *url == '\0')
  2031. return false;
  2032. CFURLRef urlRef = CFURLCreateWithBytes(
  2033. NULL,
  2034. (UInt8*)url,
  2035. strlen(url),
  2036. kCFStringEncodingASCII,
  2037. NULL
  2038. );
  2039. const OSStatus err = LSOpenCFURLRef(urlRef, 0);
  2040. CFRelease(urlRef);
  2041. return (err == noErr);
  2042. }
  2043. NSString* getAbsolutePath(const char* path)
  2044. {
  2045. NSString* bundlePathStr = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/Contents/Resources"];
  2046. if (path == NULL )
  2047. return bundlePathStr;
  2048. NSString* absPath = [NSString stringWithUTF8String:path];
  2049. if ([absPath length] == 0)
  2050. return @"";
  2051. if ([absPath hasPrefix:@"/"])
  2052. {
  2053. absPath = [NSString stringWithUTF8String:path];
  2054. }
  2055. else
  2056. {
  2057. absPath = bundlePathStr;
  2058. absPath = [absPath stringByAppendingString:@"/"];
  2059. absPath = [absPath stringByAppendingString:[NSString stringWithUTF8String:path]];
  2060. }
  2061. return absPath;
  2062. }
  2063. std::string Platform::displayFileDialog(size_t mode, const char* title, const char* filterDescription, const char* filterExtensions, const char* initialDirectory)
  2064. {
  2065. std::string filename = "";
  2066. if (mode == FileSystem::OPEN)
  2067. {
  2068. NSOpenPanel* openPanel = [NSOpenPanel openPanel];
  2069. [openPanel setCanChooseFiles:TRUE];
  2070. [openPanel setCanChooseDirectories:FALSE];
  2071. [openPanel setAllowsMultipleSelection:FALSE];
  2072. // Title
  2073. NSString* titleStr = [NSString stringWithUTF8String:title];
  2074. [openPanel setTitle:titleStr];
  2075. // Filter ext.
  2076. NSString* ext = [NSString stringWithUTF8String:filterExtensions];
  2077. NSArray* fileTypes = [NSArray arrayWithObjects: ext, nil];
  2078. [openPanel setAllowedFileTypes:fileTypes];
  2079. // Set the initial directory
  2080. NSString* absPath = getAbsolutePath(initialDirectory);
  2081. NSURL* url = [NSURL fileURLWithPath:absPath];
  2082. [openPanel setDirectoryURL:url];
  2083. // Show the open dialog
  2084. if ([openPanel runModal] == NSOKButton)
  2085. {
  2086. NSURL* selectedFileName = [openPanel URL];
  2087. NSString* urlStr = [selectedFileName absoluteString];
  2088. filename = std::string([urlStr UTF8String]);
  2089. const std::string fileProtocol = std::string("file://localhost");
  2090. filename.replace(filename.find(fileProtocol), fileProtocol.size(), "");
  2091. }
  2092. }
  2093. else
  2094. {
  2095. NSSavePanel* savePanel = [NSSavePanel savePanel];
  2096. [savePanel setCanCreateDirectories:TRUE];
  2097. [savePanel setCanSelectHiddenExtension:TRUE];
  2098. // Title
  2099. NSString* titleStr = [NSString stringWithUTF8String:title];
  2100. [savePanel setTitle:titleStr];
  2101. // Filter ext.
  2102. NSString* ext = [NSString stringWithUTF8String:filterExtensions];
  2103. NSArray* fileTypes = [NSArray arrayWithObjects: ext, nil];
  2104. [savePanel setAllowedFileTypes:fileTypes];
  2105. // Set the initial directory
  2106. NSString* absPath = getAbsolutePath(initialDirectory);
  2107. NSURL* url = [NSURL fileURLWithPath:absPath];
  2108. [savePanel setDirectoryURL:url];
  2109. // Show the save dialog
  2110. if ([savePanel runModal] == NSOKButton)
  2111. {
  2112. NSURL* selectedFileName = [savePanel URL];
  2113. NSString* urlStr = [selectedFileName absoluteString];
  2114. filename = std::string([urlStr UTF8String]);
  2115. const std::string fileProtocol = std::string("file://localhost");
  2116. filename.replace(filename.find(fileProtocol), fileProtocol.size(), "");
  2117. }
  2118. }
  2119. return filename;
  2120. }
  2121. #endif