11#include < Cocoa/Cocoa.h>
22#include < mach/mach.h>
33#include < mach/mach_time.h>
4+ #include < IOKit/hid/IOHidLib.h>
45
56#include " game.h"
67
8+ // timing
9+ int osGetTime () {
10+ static mach_timebase_info_data_t timebaseInfo;
11+ if (timebaseInfo.denom == 0 ) {
12+ mach_timebase_info (&timebaseInfo);
13+ }
14+
15+ uint64_t absolute = mach_absolute_time ();
16+ uint64_t milliseconds = absolute * timebaseInfo.numer / (timebaseInfo.denom * 1000000ULL );
17+ return int (milliseconds);
18+ }
19+
720// sound
821#define SND_SIZE 2352
922
1023static AudioQueueRef audioQueue;
1124
12- void soundFill (void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
25+ void sndFill (void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
1326 void * frames = inBuffer->mAudioData ;
1427 UInt32 count = inBuffer->mAudioDataBytesCapacity / 4 ;
1528 Sound::fill ((Sound::Frame*)frames, count);
1629 inBuffer->mAudioDataByteSize = count * 4 ;
1730 AudioQueueEnqueueBuffer (audioQueue, inBuffer, 0 , NULL );
1831}
1932
20- void soundInit () {
33+ void sndInit () {
2134 AudioStreamBasicDescription deviceFormat;
2235 deviceFormat.mSampleRate = 44100 ;
2336 deviceFormat.mFormatID = kAudioFormatLinearPCM ;
@@ -29,12 +42,12 @@ void soundInit() {
2942 deviceFormat.mBitsPerChannel = 16 ;
3043 deviceFormat.mReserved = 0 ;
3144
32- AudioQueueNewOutput (&deviceFormat, soundFill , NULL , NULL , NULL , 0 , &audioQueue);
45+ AudioQueueNewOutput (&deviceFormat, sndFill , NULL , NULL , NULL , 0 , &audioQueue);
3346
3447 for (int i = 0 ; i < 2 ; i++) {
3548 AudioQueueBufferRef mBuffer ;
3649 AudioQueueAllocateBuffer (audioQueue, SND_SIZE, &mBuffer );
37- soundFill (NULL , audioQueue, mBuffer );
50+ sndFill (NULL , audioQueue, mBuffer );
3851 }
3952 AudioQueueStart (audioQueue, NULL );
4053}
@@ -51,7 +64,6 @@ InputKey keyToInputKey(int code) {
5164 for (int i = 0 ; i < sizeof (codes) / sizeof (codes[0 ]); i++)
5265 if (codes[i] == code) {
5366 return (InputKey)(ikLeft + i);
54- LOG (" %d\n " , code);
5567 }
5668 return ikNone;
5769}
@@ -65,24 +77,133 @@ InputKey mouseToInputKey(int btn) {
6577 return ikNone;
6678}
6779
80+ IOHIDDeviceRef joyDevices[4 ] = { 0 };
81+ IOHIDManagerRef hidManager;
82+
83+ JoyKey joyButtonToKey (uint32_t button) {
84+ static const JoyKey keys[] = { jkA, jkB, jkX, jkY, jkLB, jkRB, jkLT, jkRT, jkStart, jkSelect, jkNone, jkUp, jkDown, jkLeft, jkRight };
85+
86+ if (button >= 0 || button < COUNT (keys))
87+ return keys[button];
88+ return jkNone;
89+ }
90+
6891bool osJoyReady (int index) {
69- return false ;
92+ if (index < 0 || index >= COUNT (joyDevices))
93+ return false ;
94+ return joyDevices[index] != NULL ;
7095}
7196
7297void osJoyVibrate (int index, float L, float R) {
7398 // TODO
7499}
75100
76- // timing
77- int osGetTime () {
78- static mach_timebase_info_data_t timebaseInfo;
79- if (timebaseInfo.denom == 0 ) {
80- mach_timebase_info (&timebaseInfo);
101+ float joyAxisValue (IOHIDValueRef value) {
102+ IOHIDElementRef element = IOHIDValueGetElement (value);
103+ CFIndex val = IOHIDValueGetIntegerValue (value);
104+ CFIndex min = IOHIDElementGetLogicalMin (element);
105+ CFIndex max = IOHIDElementGetLogicalMax (element);
106+
107+ float v = float (val - min) / float (max - min);
108+ if (v < 0 .2f ) v = 0 .0f ; // check for deadzone
109+ return v * 2 .0f - 1 .0f ;
110+ }
111+
112+ void hidValueCallback (void *context, IOReturn result, void *sender, IOHIDValueRef value) {
113+ if (result != kIOReturnSuccess )
114+ return ;
115+
116+ IOHIDElementRef element = IOHIDValueGetElement (value);
117+ IOHIDDeviceRef device = IOHIDElementGetDevice (element);
118+
119+ int joyIndex = -1 ;
120+ for (int i = 0 ; i < COUNT (joyDevices); i++) {
121+ if (joyDevices[i] == device) {
122+ joyIndex = i;
123+ break ;
124+ }
81125 }
82126
83- uint64_t absolute = mach_absolute_time ();
84- uint64_t milliseconds = absolute * timebaseInfo.numer / (timebaseInfo.denom * 1000000ULL );
85- return int (milliseconds);
127+ if (joyIndex == -1 ) return ;
128+
129+ // TODO: add mapping for most popular gamepads by kIOHIDVendorIDKey / kIOHIDProductIDKey
130+ switch (IOHIDElementGetUsagePage (element)) {
131+ case kHIDPage_GenericDesktop : {
132+ switch (IOHIDElementGetUsage (IOHIDValueGetElement (value))) {
133+ case kHIDUsage_GD_X :
134+ Input::setJoyPos (joyIndex, jkL, vec2 (joyAxisValue (value), Input::joy[joyIndex].L .y ));
135+ break ;
136+ case kHIDUsage_GD_Y :
137+ Input::setJoyPos (joyIndex, jkL, vec2 (Input::joy[joyIndex].L .x , joyAxisValue (value)));
138+ break ;
139+ case kHIDUsage_GD_Rx :
140+ Input::setJoyPos (joyIndex, jkR, vec2 (joyAxisValue (value), Input::joy[joyIndex].R .y ));
141+ break ;
142+ case kHIDUsage_GD_Ry :
143+ Input::setJoyPos (joyIndex, jkR, vec2 (Input::joy[joyIndex].R .x , joyAxisValue (value)));
144+ break ;
145+ case kHIDUsage_GD_Z :
146+ Input::setJoyPos (joyIndex, jkLT, vec2 (joyAxisValue (value) * 0 .5f + 0 .5f , 0 .0f ));
147+ break ;
148+ case kHIDUsage_GD_Rz :
149+ Input::setJoyPos (joyIndex, jkRT, vec2 (joyAxisValue (value) * 0 .5f + 0 .5f , 0 .0f ));
150+ break ;
151+ default : LOG (" ! joy: unknown joy 0x%x (%d)\n " , IOHIDElementGetUsage (IOHIDValueGetElement (value)), (int )IOHIDValueGetIntegerValue (value));
152+ }
153+ break ;
154+ }
155+ case kHIDPage_Button : {
156+ uint32_t button = IOHIDElementGetUsage (IOHIDValueGetElement (value)) - kHIDUsage_Button_1 ;
157+ bool down = IOHIDValueGetIntegerValue (value) != 0 ;
158+ JoyKey key = joyButtonToKey (button);
159+ Input::setJoyDown (joyIndex, key, down);
160+ if (key == jkNone) LOG (" ! joy: unknown button %d\n " , button);
161+ break ;
162+ }
163+ default : ;
164+ }
165+ }
166+
167+ void joyAdd (void * context, IOReturn, void *, IOHIDDeviceRef device) {
168+ for (int i = 0 ; i < COUNT (joyDevices); i++) {
169+ if (joyDevices[i] == NULL ) {
170+ joyDevices[i] = device;
171+ break ;
172+ }
173+ }
174+ }
175+
176+ void joyRemove (void * context, IOReturn, void *, IOHIDDeviceRef device) {
177+ for (int i = 0 ; i < COUNT (joyDevices); i++) {
178+ if (joyDevices[i] == device) {
179+ joyDevices[i] = NULL ;
180+ break ;
181+ }
182+ }
183+ }
184+
185+ void joyInit () {
186+ hidManager = IOHIDManagerCreate (kCFAllocatorDefault , kIOHIDManagerOptionNone );
187+
188+ NSDictionary *matchingGamepad = @{
189+ @(kIOHIDDeviceUsagePageKey ): @(kHIDPage_GenericDesktop ),
190+ @(kIOHIDDeviceUsageKey ): @(kHIDUsage_GD_GamePad )
191+ };
192+ NSArray *matchDicts = @[ matchingGamepad ];
193+
194+ IOHIDManagerSetDeviceMatchingMultiple (hidManager, (__bridge CFArrayRef) matchDicts);
195+ IOHIDManagerRegisterDeviceMatchingCallback (hidManager, joyAdd, NULL );
196+ IOHIDManagerRegisterDeviceRemovalCallback (hidManager, joyRemove, NULL );
197+ IOHIDManagerScheduleWithRunLoop (hidManager, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
198+ IOHIDManagerOpen (hidManager, kIOHIDOptionsTypeNone );
199+ IOHIDManagerRegisterInputValueCallback (hidManager, hidValueCallback, NULL );
200+ }
201+
202+ void joyFree () {
203+ IOHIDManagerUnscheduleFromRunLoop (hidManager, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode );
204+ IOHIDManagerRegisterDeviceMatchingCallback (hidManager, NULL , 0 );
205+ IOHIDManagerRegisterDeviceRemovalCallback (hidManager, NULL , 0 );
206+ IOHIDManagerClose (hidManager, kIOHIDOptionsTypeNone );
86207}
87208
88209@interface OpenLaraGLView : NSOpenGLView
@@ -216,10 +337,10 @@ int main() {
216337
217338 // init window
218339 NSRect rect = NSMakeRect (0 , 0 , 1280 , 720 );
219- NSWindow *mainWindow = [[NSWindow alloc ] initWithContentRect: rect styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask backing: NSBackingStoreBuffered defer: YES ];
220- mainWindow .title = @" OpenLara" ;
221- mainWindow .acceptsMouseMovedEvents = YES ;
222- mainWindow .delegate = [[OpenLaraWindowDelegate alloc ] init ];
340+ NSWindow *window = [[NSWindow alloc ] initWithContentRect: rect styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask backing: NSBackingStoreBuffered defer: YES ];
341+ window .title = @" OpenLara" ;
342+ window .acceptsMouseMovedEvents = YES ;
343+ window .delegate = [[OpenLaraWindowDelegate alloc ] init ];
223344
224345 // init OpenGL context
225346 NSOpenGLPixelFormatAttribute attribs[] = {
@@ -232,9 +353,9 @@ int main() {
232353 };
233354 NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc ] initWithAttributes: attribs];
234355
235- OpenLaraGLView *view = [[OpenLaraGLView alloc ] initWithFrame: mainWindow .contentLayoutRect pixelFormat: format];
356+ OpenLaraGLView *view = [[OpenLaraGLView alloc ] initWithFrame: window .contentLayoutRect pixelFormat: format];
236357 view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
237- mainWindow .contentView = view;
358+ window .contentView = view;
238359 [view.openGLContext makeCurrentContext ];
239360
240361 // get path to game content
@@ -244,17 +365,19 @@ int main() {
244365 strcat (contentDir, " /" );
245366
246367 // show window
247- [mainWindow center ];
248- [mainWindow makeKeyAndOrderFront: nil ];
368+ [window center ];
369+ [window makeKeyAndOrderFront: nil ];
249370
250- soundInit ();
371+ joyInit ();
372+ sndInit ();
251373 Game::init ();
252374
253375 if (!Core::isQuit) {
254376 [application run ];
255377 }
256378
257379 Game::deinit ();
380+ joyFree ();
258381 // TODO: sndFree
259382
260383 return 0 ;
0 commit comments