-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSNIIS_Mac.cpp
347 lines (304 loc) · 11.7 KB
/
SNIIS_Mac.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/// @file SNIIS_Mac.cpp
/// Mac OSX implementation of input system
#include "SNIIS_Mac.h"
#include "SNIIS_Intern.h"
#if SNIIS_SYSTEM_MAC
using namespace SNIIS;
// --------------------------------------------------------------------------------------------------------------------
// Constructor
MacInput::MacInput(id pWindowId)
{
mWindow = pWindowId;
// create the manager
mHidManager = IOHIDManagerCreate( kCFAllocatorDefault, 0);
if( !mHidManager )
throw std::runtime_error( "Failed to create HIDManager");
// tell 'em we want it all
IOHIDManagerSetDeviceMatching( mHidManager, nullptr);
// register our enumeration callback
IOHIDManagerRegisterDeviceMatchingCallback( mHidManager, &MacInput::HandleNewDeviceCallback, (void*) this);
// register us for running the event loop
IOHIDManagerScheduleWithRunLoop( mHidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
// and open the manager, enumerating all devices along with it
IOReturn res = IOHIDManagerOpen( mHidManager, 0);
Log( "IOHIDManagerOpen() returned %d", res);
if( res != kIOReturnSuccess )
throw std::runtime_error( "Failed to open HIDManager / enumerate devices");
// run the update loop to get the callbacks for new devices
while( CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource )
/**/;
// remove the manager from the callbacks and runloop
IOHIDManagerRegisterDeviceMatchingCallback( mHidManager, nullptr, nullptr);
// Since some OSX update the Unschedule() thingy also unschedules all devices, so we never get any event notifications
// simply leaving it be should be fine, as we unregistered the callback
// IOHIDManagerUnscheduleFromRunLoop( mHidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
// --------------------------------------------------------------------------------------------------------------------
// Destructor
MacInput::~MacInput()
{
for( auto d : mDevices )
delete d;
if( mHidManager )
IOHIDManagerClose( mHidManager, 0);
CFRelease( mHidManager);
}
// --------------------------------------------------------------------------------------------------------------------
// Updates the inputs, to be called before handling system messages
void MacInput::Update()
{
// Basis work
InputSystem::Update();
// device work
for( size_t a = 0; a < mMacDevices.size(); ++a )
{
MacDevice* d = mMacDevices[a];
d->StartUpdate();
}
// then run the update loop
while( CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource )
/**/;
// Mice need postprocessing
for( auto d : mDevices )
{
if( auto m = dynamic_cast<MacMouse*> (d) )
m->EndUpdate();
// from now on everything generates signals
d->ResetFirstUpdateFlag();
}
}
// --------------------------------------------------------------------------------------------------------------------
// Notifies the input system that the application has lost/gained focus.
void MacInput::InternSetFocus( bool pHasFocus)
{
for( auto d : mMacDevices )
d->SetFocus( pHasFocus);
}
// --------------------------------------------------------------------------------------------------------------------
void MacInput::InternSetMouseGrab( bool enabled)
{
auto wr = MacHelper_GetWindowRect( mWindow);
Pos p;
if( enabled )
{
// if enabled, move system mouse to window center and start taking offsets from there
p.x = wr.w / 2; p.y = wr.h / 2;
} else
{
// if disabled, move mouse to last reported mouse position to achieve a smooth non-jumpy transition between the modes
p.x = std::max( 0.0f, std::min( wr.w, float( GetMouseX())));
p.y = std::max( 0.0f, std::min( wr.h, float( GetMouseY())));
}
Pos gp = MacHelper_WinToDisplay( mWindow, p);
MacHelper_SetMousePos( gp);
}
// --------------------------------------------------------------------------------------------------------------------
// Callback for each enumerated device
void MacInput::HandleNewDeviceCallback( void* context, IOReturn result, void* sender, IOHIDDeviceRef device)
{
SNIIS_UNUSED( sender);
if( result == kIOReturnSuccess )
{
auto inp = reinterpret_cast<MacInput*> (context);
inp->HandleNewDevice( device);
}
}
// --------------------------------------------------------------------------------------------------------------------
// Handle a newly detected device
void MacInput::HandleNewDevice( IOHIDDeviceRef device)
{
// get usage page and usage
int32_t usepage = 0, usage = 0;
auto ref = IOHIDDeviceGetProperty( device, CFSTR( kIOHIDPrimaryUsagePageKey));
if( ref )
CFNumberGetValue( (CFNumberRef) ref, kCFNumberSInt32Type, &usepage);
ref = IOHIDDeviceGetProperty( device, CFSTR( kIOHIDPrimaryUsageKey));
if( ref )
CFNumberGetValue( (CFNumberRef) ref, kCFNumberSInt32Type, &usage);
// get the names
auto cfstr = (CFStringRef) IOHIDDeviceGetProperty( device, CFSTR(kIOHIDProductKey));
auto cfstr2 = (CFStringRef) IOHIDDeviceGetProperty( device, CFSTR(kIOHIDManufacturerKey));
std::vector<char> tmp( 500, 0);
if( cfstr )
CFStringGetCString( cfstr, &tmp[0], tmp.size(), kCFStringEncodingUTF8);
else
sprintf( &tmp[0], "(null)");
size_t l = strlen( tmp.data());
tmp[l++] = '|';
if( cfstr2 )
CFStringGetCString( cfstr2, &tmp[l], tmp.size() - l, kCFStringEncodingUTF8);
else
sprintf( &tmp[l], "(null)");
Log( "HandleNewDevice \"%s\", page %d, usage %d", tmp.data(), usepage, usage);
if( usepage != kHIDPage_GenericDesktop )
return;
Log( "New device \"%s\" at page %d, usage %d", tmp.data(), usepage, usage);
// extra ugly: store last mouse because we might have to add the second trackpad to it
static MacMouse* lastMouse = nullptr;
switch( usage )
{
case kHIDUsage_GD_Mouse:
case kHIDUsage_GD_Pointer:
{
try {
bool isTrackpad = strncmp( tmp.data(), "Apple Internal Keyboard / Trackpad", 34) == 0;
if( isTrackpad && lastMouse && lastMouse->IsTrackpad() )
{
Log( "-> second HID of internal trackpad, adding to Mouse %d (id %d)", lastMouse->GetCount(), lastMouse->GetId());
lastMouse->AddDevice( device);
}
else
{
Log( "-> Mouse %d (id %d)", mNumMice, mDevices.size());
auto m = new MacMouse( this, mDevices.size(), device, isTrackpad);
InputSystemHelper::AddDevice( m);
mMacDevices.push_back( m);
if( isTrackpad )
lastMouse = m;
}
} catch( std::exception& e)
{
Log( "Exception: %s", e.what());
}
break;
}
case kHIDUsage_GD_Keyboard:
case kHIDUsage_GD_Keypad:
{
try {
Log( "-> Keyboard %d (id %d)", mNumKeyboards, mDevices.size());
auto k = new MacKeyboard( this, mDevices.size(), device);
InputSystemHelper::AddDevice( k);
mMacDevices.push_back( k);
} catch( std::exception& e)
{
Log( "Exception: %s", e.what());
}
break;
}
case kHIDUsage_GD_Joystick:
case kHIDUsage_GD_GamePad:
case kHIDUsage_GD_MultiAxisController:
{
try {
Log( "-> Controller %d (id %d)", mNumJoysticks, mDevices.size());
auto j = new MacJoystick( this, mDevices.size(), device);
InputSystemHelper::AddDevice( j);
mMacDevices.push_back( j);
} catch( std::exception& e)
{
Log( "Exception: %s", e.what());
}
break;
}
}
}
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
std::vector<MacControl> EnumerateDeviceControls( IOHIDDeviceRef devref)
{
// enumerate all controls of that device
std::vector<MacControl> controls;
CFArrayRef elements = IOHIDDeviceCopyMatchingElements( devref, nullptr, kIOHIDOptionsTypeNone);
for( size_t a = 0, count = CFArrayGetCount( elements); a < count; ++a )
{
auto elmref = (IOHIDElementRef) CFArrayGetValueAtIndex( elements, CFIndex( a));
auto type = IOHIDElementGetType( elmref);
auto usepage = IOHIDElementGetUsagePage( elmref), usage = IOHIDElementGetUsage( elmref);
size_t prevSize = controls.size();
if( type == kIOHIDElementTypeInput_Axis || type == kIOHIDElementTypeInput_Misc )
{
auto min = IOHIDElementGetLogicalMin( elmref), max = IOHIDElementGetLogicalMax( elmref);
if( usage == kHIDUsage_GD_Hatswitch )
{
controls.push_back( MacControl{ devref, MacControl::Type_Hat, "", 0, usepage, usage, min, max });
controls.push_back( MacControl{ devref, MacControl::Type_Hat_Second, "", 0, usepage, usage, min, max });
}
else
{
controls.push_back( MacControl{ devref, MacControl::Type_Axis, "", 0, usepage, usage, min, max });
}
}
else if( type == kIOHIDElementTypeInput_Button )
{
controls.push_back( MacControl{ devref, MacControl::Type_Button, "", 0, usepage, usage, 0, 1 });
}
// add a few things afterwards if we got new controls
for( size_t a = prevSize; a < controls.size(); ++a )
{
controls[a].mCookie = IOHIDElementGetCookie( elmref);
auto name = IOHIDElementGetName( elmref);
if( name )
{
std::vector<char> tmp( 500, 0);
CFStringGetCString( name, &tmp[0], tmp.size(), kCFStringEncodingUTF8);
controls[a].mName = &tmp[0];
}
}
}
return controls;
}
void InputElementValueChangeCallback( void* ctx, IOReturn res, void* sender, IOHIDValueRef val)
{
SNIIS_UNUSED( sender);
if( res != kIOReturnSuccess )
return;
//also ignore all events if we ain't focus'd. Did I use that word correctly?
if( !gInstance->HasFocus() )
return;
auto dev = static_cast<MacDevice*> (ctx);
auto elm = IOHIDValueGetElement( val);
auto keksie = IOHIDElementGetCookie( elm);
auto value = IOHIDValueGetIntegerValue( val);
auto usepage = IOHIDElementGetUsagePage( elm), usage = IOHIDElementGetUsage( elm);
auto hiddev = IOHIDElementGetDevice( elm);
dev->HandleEvent( hiddev, keksie, usepage, usage, value);
}
std::pair<float, float> ConvertHatToAxes( long min, long max, long value)
{
std::pair<float, float> axes;
// Hats deliver a value that starts at North (x=0, y=-1) and goes a full circle clockwise within the value range
// We need to decompose this into two axes from roughly 8 standard cases with a little safety each
float v = float( value - min) / float( max - min);
if( v > 0.1f && v < 0.4f )
axes.first = 1.0f;
else if( v > 0.6f && v < 0.9f )
axes.first = -1.0f;
else
axes.first = 0.0f;
if( v < 0.15f || v > 0.85f )
axes.second = -1.0f;
else if( v > 0.35f && v < 0.65f )
axes.second = 1.0f;
else
axes.second = 0.0f;
return axes;
}
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Initializes the input system with the given InitArgs. When successful, gInstance is not Null.
bool InputSystem::Initialize(void* pInitArg)
{
if( gInstance )
throw std::runtime_error( "Input already initialized");
try
{
gInstance = new MacInput( pInitArg);
} catch( std::exception& e)
{
// nope
if( gLogCallback )
gLogCallback( (std::string( "Exception while creating SNIIS instance: ") + e.what()).c_str());
gInstance = nullptr;
return false;
}
return true;
}
// --------------------------------------------------------------------------------------------------------------------
// Destroys the input system. After returning gInstance is Null again
void InputSystem::Shutdown()
{
delete gInstance;
gInstance = nullptr;
}
#endif // SNIIS_SYSTEM_MAC