*** empty log message ***
[chuck-blob.git] / v2 / util_hid.cpp
blob87b298232bbdb310729d4bd8fe2a6bd29391a28f
1 /*----------------------------------------------------------------------------
2 ChucK Concurrent, On-the-fly Audio Programming Language
3 Compiler and Virtual Machine
5 Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved.
6 http://chuck.cs.princeton.edu/
7 http://soundlab.cs.princeton.edu/
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 U.S.A.
23 -----------------------------------------------------------------------------*/
25 //-----------------------------------------------------------------------------
26 // file: util_hid.cpp
27 // desc: refactored HID joystick, keyboard, mouse support
29 // author: Spencer Salazar (ssalazar@cs.princeton.edu)
30 // date: summer 2006
31 //-----------------------------------------------------------------------------
33 #include "chuck_def.h"
34 #include "chuck_errmsg.h"
35 #include "util_hid.h"
36 #include "hidio_sdl.h"
38 #include <limits.h>
39 #include <vector>
40 #include <map>
42 using namespace std;
44 /* device types */
45 const t_CKUINT CK_HID_DEV_NONE = 0;
46 const t_CKUINT CK_HID_DEV_JOYSTICK = 1;
47 const t_CKUINT CK_HID_DEV_MOUSE = 2;
48 const t_CKUINT CK_HID_DEV_KEYBOARD = 3;
49 const t_CKUINT CK_HID_DEV_WIIREMOTE = 4;
50 const t_CKUINT CK_HID_DEV_TILTSENSOR = 5;
51 const t_CKUINT CK_HID_DEV_TABLET = 6;
52 const t_CKUINT CK_HID_DEV_COUNT = 7;
54 /* message types */
55 const t_CKUINT CK_HID_JOYSTICK_AXIS = 0;
56 const t_CKUINT CK_HID_BUTTON_DOWN = 1;
57 const t_CKUINT CK_HID_BUTTON_UP = 2;
58 const t_CKUINT CK_HID_JOYSTICK_HAT = 3;
59 const t_CKUINT CK_HID_JOYSTICK_BALL = 4;
60 const t_CKUINT CK_HID_MOUSE_MOTION = 5;
61 const t_CKUINT CK_HID_MOUSE_WHEEL = 6;
62 const t_CKUINT CK_HID_DEVICE_CONNECTED = 7;
63 const t_CKUINT CK_HID_DEVICE_DISCONNECTED = 8;
64 const t_CKUINT CK_HID_ACCELEROMETER = 9;
65 const t_CKUINT CK_HID_WIIREMOTE_IR = 10;
66 const t_CKUINT CK_HID_LED = 11;
67 const t_CKUINT CK_HID_FORCE_FEEDBACK = 12;
68 const t_CKUINT CK_HID_SPEAKER = 13;
69 const t_CKUINT CK_HID_TABLET_PRESSURE = 14;
70 const t_CKUINT CK_HID_TABLET_MOTION = 15;
71 const t_CKUINT CK_HID_TABLET_ROTATION = 16;
72 const t_CKUINT CK_HID_MSG_COUNT = 17;
74 #if defined(__PLATFORM_MACOSX__) && !defined(__CHIP_MODE__)
75 #pragma mark OS X General HID support
77 /* TODO: ***********************************************************************
79 Make Joystick/Mouse/Keyboard_open/_close thread safe (i.e., do all ->configure
80 and ->startQueue calls on the Hid thread). *** DONE
82 Make ->disconnect delete the element list *** worked around
84 Make Keyboard, Mouse dis/reattachment work *** DONE
86 Hid Probe
88 *******************************************************************************/
91 #include <mach/mach.h>
92 #include <mach/mach_error.h>
93 #include <IOKit/IOKitLib.h>
94 #include <IOKit/IOCFPlugIn.h>
95 #include <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
96 #include <IOKit/hid/IOHIDLib.h>
97 #include <IOKit/hid/IOHIDKeys.h>
98 #include <IOKit/IOMessage.h>
99 #include <CoreFoundation/CoreFoundation.h>
100 #include <Carbon/Carbon.h>
102 // check for OS X 10.4/CGEvent
103 #include <AvailabilityMacros.h>
104 #undef MAC_OS_X_VERSION_MIN_REQUIRED
105 #undef MAC_OS_X_VERSION_MAX_ALLOWED
106 #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_3
107 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_4
108 #include <ApplicationServices/ApplicationServices.h>
109 #ifdef __CGEVENT_H__
110 #define __CK_HID_CURSOR_TRACK__
111 #endif
113 #ifndef __DAN__
114 #undef __CK_HID_CURSOR_TRACK__
115 #endif
117 #ifdef __CK_HID_WIIREMOTE__
118 #include <IOBluetooth/IOBluetoothUserLib.h>
119 #include "util_buffers.h"
120 #endif // __CK_HID_WIIREMOTE__
122 class lockable
124 public:
125 void lock()
127 m_mutex.acquire();
130 void unlock()
132 m_mutex.release();
135 private:
136 XMutex m_mutex;
140 template< typename _Tp, typename _Alloc = allocator< _Tp > >
141 class xvector : public vector< _Tp, _Alloc >, public lockable
145 template <typename _Key, typename _Tp, typename _Compare = less<_Key>,
146 typename _Alloc = allocator<pair<const _Key, _Tp> > >
147 class xmultimap : public multimap< _Key, _Tp, _Compare, _Alloc >, public lockable
151 class OSX_Device : public lockable
153 public:
154 OSX_Device()
156 refcount = 0;
159 virtual ~OSX_Device() {}
161 virtual void open() {};
162 virtual void close() {};
164 t_CKUINT refcount; // incremented on open, decremented on close
167 class OSX_Hid_Device_Element
169 public:
170 OSX_Hid_Device_Element()
172 cookie = 0;
173 num = 0;
174 value = 0;
175 usage = usage_page = 0;
176 min = 0;
177 max = 0;
180 IOHIDElementCookie cookie;
182 t_CKINT type;
183 t_CKINT num;
184 t_CKINT value;
186 t_CKINT usage_page;
187 t_CKINT usage;
189 t_CKINT min;
190 t_CKINT max;
193 class OSX_Hid_Device : public OSX_Device
195 public:
196 OSX_Hid_Device()
198 configed = preconfiged = FALSE;
199 plugInInterface = NULL;
200 interface = NULL;
201 queue = NULL;
202 strncpy( name, "Device", 256 );
203 elements = NULL;
204 outputs = NULL;
205 usage = usage_page = 0;
206 type = num = -1;
207 buttons = -1;
208 axes = -1;
209 hats = -1;
210 wheels = -1;
211 refcount = 0;
212 hidProperties = NULL;
213 removal_notification = NULL;
214 vendorID = productID = locationID = NULL;
215 manufacturer = product = NULL;
218 ~OSX_Hid_Device()
220 cleanup();
223 t_CKINT preconfigure( io_object_t ioHIDDeviceObject );
224 t_CKINT configure();
225 void enumerate_elements( CFArrayRef cfElements );
226 t_CKINT start();
227 t_CKINT stop();
228 t_CKBOOL is_connected() const;
229 t_CKBOOL is_same_as( io_object_t ioHIDDeviceObject ) const;
230 void disconnect();
231 void cleanup();
233 IOCFPlugInInterface ** plugInInterface;
234 IOHIDDeviceInterface ** interface; // device interface
235 IOHIDQueueInterface ** queue; // device event queue
237 char name[256];
239 // uniquely identifying information
240 /* For ChucK's purposes, HID devices are uniquely identified by the tuple of
241 (USB device location, vendor ID, product ID,
242 manufacturer name, product name ) */
243 CFNumberRef locationID;
244 CFNumberRef vendorID;
245 CFNumberRef productID;
246 CFStringRef manufacturer;
247 CFStringRef product;
249 t_CKBOOL preconfiged, configed;
251 t_CKINT type;
252 t_CKINT num;
253 t_CKINT usage_page;
254 t_CKINT usage;
256 map< IOHIDElementCookie, OSX_Hid_Device_Element * > * elements;
258 /* Note: setting any of these to -1 informs the element enumerating
259 method that we are not interested in that particular element type */
260 t_CKINT buttons;
261 t_CKINT axes;
262 t_CKINT hats;
263 t_CKINT wheels;
265 /* outputs are stored in a map of vectors of OSX_Hid_Device_Elements
266 Thus if ( *outputs )[CK_HID_OUTPUT_TYPE] != NULL &&
267 ( *outputs )[CK_HID_OUTPUT_TYPE]->size() > n,
268 ( *outputs )[CK_HID_OUTPUT_TYPE]->at( n ) is the nth output of type
269 CK_HID_OUTPUT_TYPE. */
270 map< unsigned int, vector< OSX_Hid_Device_Element * > * > * outputs;
272 CFRunLoopSourceRef eventSource;
274 CFMutableDictionaryRef hidProperties;
276 io_object_t removal_notification;
278 const static t_CKINT event_queue_size = 50;
279 // queues use wired kernel memory so should be set to as small as possible
280 // but should account for the maximum possible events in the queue
281 // USB updates will likely occur at 100 Hz so one must account for this rate
282 // if states change quickly (updates are only posted on state changes)
285 struct OSX_Hid_op
287 enum
289 open,
290 close
291 } type;
293 xvector< OSX_Hid_Device * > * v;
294 xvector< OSX_Hid_Device * >::size_type index;
297 #pragma mark global variables
299 // hid run loop (event dispatcher)
300 static CFRunLoopRef rlHid = NULL;
301 // special hid run loop mode (limits events to hid device events only)
302 //static const CFStringRef kCFRunLoopChuckHidMode = CFSTR( "ChucKHid" );
303 static const CFStringRef kCFRunLoopChuckHidMode = kCFRunLoopDefaultMode;
305 // general hid callback for device events
306 static void Hid_callback( void * target, IOReturn result,
307 void * refcon, void * sender );
309 // CFRunLoop source for open/close device operations
310 static CFRunLoopSourceRef hidOpSource = NULL;
311 // cbuffer for open/close device operations
312 static CBufferSimple * hid_operation_buffer = NULL;
313 // callback for open/close device operations
314 static void Hid_do_operation( void * info );
316 // IO iterator for new hid devices
317 // we only keep track of this so we can release it later
318 io_iterator_t hid_iterator = NULL;
320 // notification port for device add/remove events
321 static IONotificationPortRef newDeviceNotificationPort = NULL;
322 // callback for new devices
323 static void Hid_new_devices( void * refcon, io_iterator_t iterator );
324 // callback for device removal
325 static void Hid_device_removed( void * refcon, io_service_t service,
326 natural_t messageType, void * messageArgument );
328 /* the mutexes should be acquired whenever making changes to the corresponding
329 vectors or accessing them from a non-hid thread after the hid thread has
330 been started */
331 static xvector< OSX_Hid_Device * > * joysticks = NULL;
332 static xvector< OSX_Hid_Device * > * mice = NULL;
333 static xvector< OSX_Hid_Device * > * keyboards = NULL;
335 static xmultimap< string, OSX_Hid_Device * > * joystick_names = NULL;
336 static xmultimap< string, OSX_Hid_Device * > * mouse_names = NULL;
337 static xmultimap< string, OSX_Hid_Device * > * keyboard_names = NULL;
339 // has hid been initialized?
340 static t_CKBOOL g_hid_init = FALSE;
341 // table to translate keys to ASCII
342 static t_CKBYTE g_hid_key_table[256];
344 // cursor track stuff
345 #ifdef __CK_HID_CURSOR_TRACK__
346 static t_CKINT cursorX = 0;
347 static t_CKINT cursorY = 0;
348 static t_CKFLOAT scaledCursorX = 0;
349 static t_CKFLOAT scaledCursorY = 0;
350 static CFRunLoopRef rlCursorTrack = NULL;
351 static t_CKBOOL g_ct_go = FALSE;
352 #endif // __CK_HID_CURSOR_TRACK__
354 #ifdef __CK_HID_WIIREMOTE__
355 // wii remote stuff
356 static CFRunLoopSourceRef cfrlWiiRemoteSource = NULL;
357 static void WiiRemote_cfrl_callback( void * info );
358 #endif
360 t_CKINT OSX_Hid_Device::preconfigure( io_object_t ioHIDDeviceObject )
362 // can only be called in Hid thread, or if the hid thread hasnt started
363 assert( rlHid == CFRunLoopGetCurrent() || rlHid == NULL );
365 if( preconfiged )
366 return 0;
368 switch( type )
370 case CK_HID_DEV_NONE:
371 strncpy( name, "Human Interface Device", 256 );
372 buttons = 0;
373 axes = 0;
374 wheels = 0;
375 hats = 0;
376 break;
378 case CK_HID_DEV_JOYSTICK:
379 strncpy( name, "Joystick", 256 );
380 buttons = 0;
381 axes = 0;
382 wheels = 0;
383 hats = 0;
384 break;
386 case CK_HID_DEV_MOUSE:
387 strncpy( name, "Mouse", 256 );
388 buttons = 0;
389 axes = 0;
390 wheels = 0;
391 hats = -1;
392 break;
394 case CK_HID_DEV_KEYBOARD:
395 strncpy( name, "Keyboard", 256 );
396 buttons = 0;
397 axes = -1;
398 wheels = -1;
399 hats = -1;
400 break;
403 // retrieve a dictionary of device properties
404 kern_return_t kern_result = IORegistryEntryCreateCFProperties( ioHIDDeviceObject,
405 &hidProperties,
406 kCFAllocatorDefault,
407 kNilOptions);
409 if( kern_result != KERN_SUCCESS || hidProperties == NULL )
411 hidProperties = NULL;
412 return -1;
415 // get the device name, and copy it into the device record
416 CFTypeRef refCF = CFDictionaryGetValue( hidProperties,
417 CFSTR( kIOHIDProductKey ) );
418 if( refCF )
419 CFStringGetCString( ( CFStringRef )refCF, name, 256,
420 kCFStringEncodingASCII );
422 // retrieve uniquely identifying information
423 if( !locationID )
425 locationID = ( CFNumberRef ) CFDictionaryGetValue( hidProperties,
426 CFSTR( kIOHIDLocationIDKey ) );
427 if( locationID )
428 CFRetain( locationID );
431 if( !vendorID )
433 vendorID = ( CFNumberRef ) CFDictionaryGetValue( hidProperties,
434 CFSTR( kIOHIDVendorIDKey ) );
435 if( vendorID )
436 CFRetain( vendorID );
439 if( !productID )
441 productID = ( CFNumberRef ) CFDictionaryGetValue( hidProperties,
442 CFSTR( kIOHIDProductIDKey ) );
443 if( productID )
444 CFRetain( productID );
447 if( !manufacturer )
449 manufacturer = ( CFStringRef ) CFDictionaryGetValue( hidProperties,
450 CFSTR( kIOHIDManufacturerKey ) );
451 if( manufacturer )
452 CFRetain( manufacturer );
455 if( !product )
457 product = ( CFStringRef ) CFDictionaryGetValue( hidProperties,
458 CFSTR( kIOHIDProductKey ) );
459 if( product )
460 CFRetain( product );
463 // create plugin interface
464 IOReturn result;
465 SInt32 score = 0;
466 result = IOCreatePlugInInterfaceForService( ioHIDDeviceObject,
467 kIOHIDDeviceUserClientTypeID,
468 kIOCFPlugInInterfaceID,
469 &plugInInterface,
470 &score);
472 if( result != kIOReturnSuccess )
474 CFRelease( hidProperties );
475 hidProperties = NULL;
476 return -1;
479 #ifdef __LITTLE_ENDIAN__
480 /* MacBooks and MacBook Pros have some sort of "phantom" trackpad, which
481 shows up in the HID registry and claims to have no wheels. The device
482 name is usually Trackpad with the manufacturer being Apple. This may
483 disable legitimate Trackpads, but hopefully not... */
484 if( type == CK_HID_DEV_MOUSE && strncmp( "Trackpad", name, 256 ) == 0 )
486 refCF = CFDictionaryGetValue( hidProperties,
487 CFSTR( kIOHIDManufacturerKey ) );
488 if( refCF != NULL &&
489 CFStringCompare( ( CFStringRef ) refCF, CFSTR( "Apple" ), 0 ) == 0 )
491 preconfiged = TRUE;
492 configure();
494 if( wheels == 0 )
495 // this is the "phantom" trackpad
497 // iterate through the element map, delete device element records
498 map< IOHIDElementCookie, OSX_Hid_Device_Element * >::iterator iter, end;
499 end = elements->end();
500 for( iter = elements->begin(); iter != end; iter++ )
501 delete iter->second;
502 delete elements;
503 elements = NULL;
505 if( queue )
507 ( *queue )->dispose( queue );
508 ( *queue )->Release( queue );
509 queue = NULL;
512 if( interface )
514 ( *interface )->close( interface );
515 ( *interface )->Release( interface );
516 interface = NULL;
519 return -1;
523 #endif // __LITTLE_ENDIAN__
525 // register callback for removal notification, among a few other things
526 result = IOServiceAddInterestNotification( newDeviceNotificationPort,
527 ioHIDDeviceObject,
528 kIOGeneralInterest,
529 Hid_device_removed,
530 this,
531 &removal_notification );
533 preconfiged = TRUE;
535 return 0;
538 t_CKINT OSX_Hid_Device::configure()
540 // can only be called in Hid thread, or if the hid thread hasnt started
541 assert( rlHid == NULL || rlHid == CFRunLoopGetCurrent() );
543 if( configed )
544 return 0;
546 if( !preconfiged )
547 return -1;
549 EM_log( CK_LOG_INFO, "hid: configuring %s", name );
551 // create the device interface
552 HRESULT plugInResult = S_OK;
553 plugInResult = (*plugInInterface)->QueryInterface( plugInInterface,
554 CFUUIDGetUUIDBytes( kIOHIDDeviceInterfaceID ),
555 ( LPVOID * ) &interface );
556 (*plugInInterface)->Release( plugInInterface );
557 plugInInterface = NULL;
558 if( plugInResult != S_OK )
560 CFRelease( hidProperties );
561 hidProperties = NULL;
562 return -1;
565 // open the interface
566 IOReturn result;
567 result = (*interface)->open( interface, 0 );
568 if( result != kIOReturnSuccess )
570 CFRelease( hidProperties );
571 hidProperties = NULL;
572 (*interface)->Release( interface );
573 interface = NULL;
574 return -1;
577 // create an event queue, so we dont have to poll individual elements
578 queue = (*interface)->allocQueue( interface );
579 if( !queue )
581 CFRelease( hidProperties );
582 hidProperties = NULL;
583 (*interface)->close( interface );
584 (*interface)->Release( interface );
585 interface = NULL;
586 return -1;
589 result = (*queue)->create( queue, 0, event_queue_size );
590 if( result != kIOReturnSuccess )
592 CFRelease( hidProperties );
593 hidProperties = NULL;
594 (*queue)->Release( queue );
595 queue = NULL;
596 (*interface)->close( interface );
597 (*interface)->Release( interface );
598 interface = NULL;
599 return -1;
602 // set up the call back mechanism
603 result = (*queue)->createAsyncEventSource( queue, &eventSource );
604 if( result != kIOReturnSuccess )
606 CFRelease( hidProperties );
607 hidProperties = NULL;
608 (*queue)->dispose( queue );
609 (*queue)->Release( queue );
610 queue = NULL;
611 (*interface)->close( interface );
612 (*interface)->Release( interface );
613 interface = NULL;
614 return -1;
617 result = (*queue)->setEventCallout( queue, Hid_callback, NULL, this );
618 if( result != kIOReturnSuccess )
620 CFRelease( hidProperties );
621 hidProperties = NULL;
622 (*queue)->dispose( queue );
623 (*queue)->Release( queue );
624 queue = NULL;
625 (*interface)->close( interface );
626 (*interface)->Release( interface );
627 interface = NULL;
628 return -1;
631 if( hidProperties != NULL )
633 if( elements == NULL )
635 // retrieve the array of elements...
636 CFTypeRef refCF = CFDictionaryGetValue( hidProperties,
637 CFSTR( kIOHIDElementKey ) );
638 if( !refCF )
640 CFRelease( hidProperties );
641 hidProperties = NULL;
642 (*queue)->dispose( queue );
643 (*queue)->Release( queue );
644 queue = NULL;
645 (*interface)->close( interface );
646 (*interface)->Release( interface );
647 interface = NULL;
648 return -1;
651 // ...allocate space for element records...
652 elements = new map< IOHIDElementCookie, OSX_Hid_Device_Element * >;
653 // axes = 0;
654 // buttons = 0;
655 // hats = 0;
657 // ...and parse the element array recursively
658 enumerate_elements( ( CFArrayRef ) refCF );
661 else
663 map< IOHIDElementCookie, OSX_Hid_Device_Element * >::iterator iter, end;
664 end = elements->end();
665 for( iter = elements->begin(); iter != end; iter++ )
666 ( *queue )->addElement( queue, iter->second->cookie, 0 );
669 CFRelease( hidProperties );
670 hidProperties = NULL;
673 configed = TRUE;
675 return 0;
678 //------------------------------------------------------------------------------
679 // name: enumerate_device_elements
680 // desc: perform depth depth first search on potentially nested arrays of
681 // elements on the device, add found elements to the vector
682 //------------------------------------------------------------------------------
683 void OSX_Hid_Device::enumerate_elements( CFArrayRef cfElements )
685 // can only be called in Hid thread, or if the hid thread hasnt started
686 assert( rlHid == NULL || rlHid == CFRunLoopGetCurrent() );
688 CFTypeRef refCF = 0;
689 CFDictionaryRef element_dictionary;
690 OSX_Hid_Device_Element * element;
691 t_CKINT usage_page, usage, element_type;
692 IOReturn result;
694 CFIndex i, len = CFArrayGetCount( cfElements );
695 for( i = 0; i < len; i++ )
697 // retrieve the dictionary of the ith element's data
698 refCF = CFArrayGetValueAtIndex( cfElements, i );
699 element_dictionary = ( CFDictionaryRef ) refCF;
701 // get element type
702 refCF = CFDictionaryGetValue( element_dictionary,
703 CFSTR( kIOHIDElementTypeKey ) );
704 if( !refCF ||
705 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType,
706 &element_type ) )
707 continue;
709 // get element usage page
710 refCF = CFDictionaryGetValue( element_dictionary,
711 CFSTR( kIOHIDElementUsagePageKey ) );
712 if( !refCF ||
713 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType,
714 &usage_page ) )
715 continue;
717 // get element usage
718 refCF = CFDictionaryGetValue( element_dictionary,
719 CFSTR( kIOHIDElementUsageKey ) );
720 if( !refCF ||
721 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType,
722 &usage ) )
723 continue;
725 switch( element_type )
727 case kIOHIDElementTypeInput_Misc:
728 case kIOHIDElementTypeInput_Button:
729 case kIOHIDElementTypeInput_Axis:
730 case kIOHIDElementTypeInput_ScanCodes:
731 // an input of some sort
732 switch( usage_page )
734 case kHIDPage_GenericDesktop:
735 switch( usage )
737 case kHIDUsage_GD_X:
738 case kHIDUsage_GD_Y:
739 case kHIDUsage_GD_Z:
740 case kHIDUsage_GD_Rx:
741 case kHIDUsage_GD_Ry:
742 case kHIDUsage_GD_Rz:
743 case kHIDUsage_GD_Slider:
744 case kHIDUsage_GD_Dial:
745 // this is an axis
746 if( axes == -1 )
747 continue;
749 element = new OSX_Hid_Device_Element;
751 element->num = axes;
752 if( this->type == CK_HID_DEV_JOYSTICK )
753 element->type = CK_HID_JOYSTICK_AXIS;
754 else
755 element->type = CK_HID_MOUSE_MOTION;
756 element->usage = usage;
757 element->usage_page = usage_page;
759 refCF = CFDictionaryGetValue( element_dictionary,
760 CFSTR( kIOHIDElementCookieKey ) );
761 if( !refCF ||
762 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->cookie ) )
764 delete element;
765 continue;
768 refCF = CFDictionaryGetValue( element_dictionary,
769 CFSTR( kIOHIDElementMinKey ) );
770 if( !refCF ||
771 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->min ) )
773 delete element;
774 continue;
777 refCF = CFDictionaryGetValue( element_dictionary,
778 CFSTR( kIOHIDElementMaxKey ) );
779 if( !refCF ||
780 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->max ) )
782 delete element;
783 continue;
786 result = (*queue)->addElement( queue, element->cookie, 0 );
787 if( result != kIOReturnSuccess )
789 delete element;
790 continue;
793 EM_log( CK_LOG_FINE, "adding axis %d", axes );
795 (*elements)[element->cookie] = element;
796 axes++;
798 break;
800 case kHIDUsage_GD_Wheel:
801 // this is an wheel
802 if( wheels == -1 )
803 continue;
805 element = new OSX_Hid_Device_Element;
807 element->num = wheels;
808 if( this->type == CK_HID_DEV_JOYSTICK )
809 element->type = CK_HID_JOYSTICK_AXIS;
810 else
811 element->type = CK_HID_MOUSE_WHEEL;
812 element->usage = usage;
813 element->usage_page = usage_page;
815 refCF = CFDictionaryGetValue( element_dictionary,
816 CFSTR( kIOHIDElementCookieKey ) );
817 if( !refCF ||
818 !CFNumberGetValue( ( CFNumberRef ) refCF,
819 kCFNumberLongType,
820 &element->cookie ) )
822 delete element;
823 continue;
826 result = (*queue)->addElement( queue,
827 element->cookie,
828 0 );
829 if( result != kIOReturnSuccess )
831 delete element;
832 continue;
835 EM_log( CK_LOG_FINE, "adding wheel %d", wheels );
837 (*elements)[element->cookie] = element;
838 wheels++;
839 break;
841 case kHIDUsage_GD_Hatswitch:
842 // this is a hat
843 if( hats == -1 )
844 continue;
846 element = new OSX_Hid_Device_Element;
848 element->type = CK_HID_JOYSTICK_HAT;
849 element->num = hats;
850 element->usage = usage;
851 element->usage_page = usage_page;
853 refCF = CFDictionaryGetValue( element_dictionary,
854 CFSTR( kIOHIDElementCookieKey ) );
855 if( !refCF ||
856 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->cookie ) )
858 delete element;
859 continue;
862 result = (*queue)->addElement( queue, element->cookie, 0 );
863 if( result != kIOReturnSuccess )
865 delete element;
866 continue;
869 EM_log( CK_LOG_FINE, "adding hat %d", hats );
871 (*elements)[element->cookie] = element;
872 hats++;
874 break;
876 default:
877 EM_log( CK_LOG_INFO, "unknown page: %i usage: %i", usage_page, usage );
880 break;
882 case kHIDPage_Button:
883 case kHIDPage_KeyboardOrKeypad:
884 // this is a button
885 if( buttons == -1 )
886 continue;
888 /* filter out error and reserved usages*/
889 if( usage_page == kHIDPage_KeyboardOrKeypad &&
890 !( usage >= kHIDUsage_KeyboardA &&
891 usage <= kHIDUsage_KeyboardRightGUI ) )
892 continue;
894 element = new OSX_Hid_Device_Element;
896 element->type = CK_HID_BUTTON_DOWN;
897 element->num = buttons;
898 element->usage = usage;
899 element->usage_page = usage_page;
901 refCF = CFDictionaryGetValue( element_dictionary,
902 CFSTR( kIOHIDElementCookieKey ) );
903 if( !refCF ||
904 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->cookie ) )
906 delete element;
907 continue;
910 result = (*queue)->addElement( queue, element->cookie, 0 );
911 if( result != kIOReturnSuccess )
913 delete element;
914 continue;
917 EM_log( CK_LOG_FINE, "adding button %d", buttons );
919 (*elements)[element->cookie] = element;
920 buttons++;
922 break;
924 default:
925 EM_log( CK_LOG_FINER, "unknown page: %i usage: %i", usage_page, usage );
928 break;
930 case kIOHIDElementTypeCollection:
931 refCF = CFDictionaryGetValue( element_dictionary, CFSTR( kIOHIDElementKey ) );
932 if( !refCF )
933 continue;
935 enumerate_elements( ( CFArrayRef ) refCF );
936 break;
938 case kIOHIDElementTypeOutput:
939 switch( usage_page )
941 case kHIDPage_LEDs:
942 /* filter out error and reserved usages */
943 if( usage > kHIDUsage_LED_ExternalPowerConnected )
944 continue;
946 element = new OSX_Hid_Device_Element;
948 refCF = CFDictionaryGetValue( element_dictionary,
949 CFSTR( kIOHIDElementCookieKey ) );
950 if( !refCF ||
951 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->cookie ) )
953 delete element;
954 continue;
957 refCF = CFDictionaryGetValue( element_dictionary,
958 CFSTR( kIOHIDElementMinKey ) );
959 if( !refCF ||
960 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->min ) )
962 delete element;
963 continue;
966 refCF = CFDictionaryGetValue( element_dictionary,
967 CFSTR( kIOHIDElementMaxKey ) );
968 if( !refCF ||
969 !CFNumberGetValue( ( CFNumberRef ) refCF, kCFNumberLongType, &element->max ) )
971 delete element;
972 continue;
975 if( outputs == NULL )
976 outputs = new map< unsigned int, vector< OSX_Hid_Device_Element * > * >;
978 if( outputs->find( CK_HID_LED ) == outputs->end() )
979 ( *outputs )[CK_HID_LED] = new vector< OSX_Hid_Device_Element * >;
981 element->type = CK_HID_LED;
982 element->num = ( *outputs )[CK_HID_LED]->size();
983 element->usage = usage;
984 element->usage_page = usage_page;
986 EM_log( CK_LOG_FINE, "adding led %d", element->num );
988 ( *outputs )[CK_HID_LED]->push_back( element );
990 break;
993 break;
998 t_CKINT OSX_Hid_Device::start()
1000 // can only be called in Hid thread
1001 assert( rlHid == CFRunLoopGetCurrent() );
1003 EM_log( CK_LOG_INFO, "hid: starting %s", name );
1004 EM_pushlog();
1006 // configure if needed
1007 if( !configed )
1009 // lock the device so we can mutate it
1010 this->lock();
1012 // configure
1013 if( configure() )
1015 // error during configuration!
1016 this->unlock();
1018 EM_log( CK_LOG_SEVERE, "hid: error configuring %s", name );
1019 EM_poplog();
1021 return -1;
1024 this->unlock();
1027 EM_log( CK_LOG_INFO, "hid: adding %s to run loop", name );
1028 CFRunLoopAddSource( rlHid, eventSource, kCFRunLoopChuckHidMode );
1030 IOReturn result = ( *queue )->start( queue );
1031 if( result != kIOReturnSuccess )
1033 EM_log( CK_LOG_SEVERE, "hid: error starting event queue for %s", name );
1034 EM_poplog();
1035 return -1;
1038 EM_poplog();
1040 return 0;
1043 t_CKINT OSX_Hid_Device::stop()
1045 // can only be called in Hid thread
1046 assert( rlHid == CFRunLoopGetCurrent() );
1048 if( queue )
1050 IOReturn result = ( *queue )->stop( queue );
1051 if( result != kIOReturnSuccess )
1052 EM_log( CK_LOG_INFO, "hid: error stopping event queue for %s", name );
1055 CFRunLoopRemoveSource( rlHid, eventSource, kCFRunLoopChuckHidMode );
1057 return 0;
1060 t_CKBOOL OSX_Hid_Device::is_connected() const
1062 if( plugInInterface == NULL && interface == NULL )
1063 return FALSE;
1064 return TRUE;
1067 t_CKBOOL OSX_Hid_Device::is_same_as( io_object_t ioHIDDeviceObject ) const
1069 CFMutableDictionaryRef hidProperties2 = NULL;
1070 t_CKBOOL result = FALSE;
1072 // retrieve a dictionary of device properties
1073 kern_return_t kern_result = IORegistryEntryCreateCFProperties( ioHIDDeviceObject,
1074 &hidProperties2,
1075 kCFAllocatorDefault,
1076 kNilOptions );
1078 if( kern_result != KERN_SUCCESS || hidProperties2 == NULL )
1080 return FALSE;
1083 // gewang: moved these decl's above goto's
1084 CFNumberRef locationID2 = 0;
1085 CFNumberRef vendorID2 = 0;
1086 CFNumberRef productID2 = 0;
1087 CFStringRef manufacturer2 = 0;
1088 CFStringRef product2 = 0;
1090 // if no "uniquely identifying" info is available, then assume its new
1091 if( !locationID && !vendorID && !productID && !manufacturer && !product )
1092 goto end;
1094 locationID2 = ( CFNumberRef ) CFDictionaryGetValue( hidProperties2,
1095 CFSTR( kIOHIDLocationIDKey ) );
1096 if( ( locationID && !locationID2 ) || ( !locationID && locationID2 ) )
1097 goto end; // not the same
1098 if( locationID && locationID2 && CFNumberCompare( locationID, locationID2, NULL ) )
1099 goto end; // not the same
1101 vendorID2 = ( CFNumberRef ) CFDictionaryGetValue( hidProperties2,
1102 CFSTR( kIOHIDVendorIDKey ) );
1103 if( ( vendorID && !vendorID2 ) || ( !vendorID && vendorID2 ) )
1104 goto end; // not the same
1105 if( vendorID && vendorID2 && CFNumberCompare( vendorID, vendorID2, NULL ) )
1106 goto end; // not the same
1108 productID2 = ( CFNumberRef ) CFDictionaryGetValue( hidProperties2,
1109 CFSTR( kIOHIDProductIDKey ) );
1110 if( ( productID && !productID2 ) || ( !productID && productID2 ) )
1111 goto end; // not the same
1112 if( productID && productID2 && CFNumberCompare( productID, productID2, NULL ) )
1113 goto end; // not the same
1115 manufacturer2 = ( CFStringRef ) CFDictionaryGetValue( hidProperties2,
1116 CFSTR( kIOHIDManufacturerKey ) );
1117 if( ( manufacturer && !manufacturer2 ) || ( !manufacturer && manufacturer2 ) )
1118 goto end; // not the same
1119 if( manufacturer && manufacturer2 && CFStringCompare( manufacturer, manufacturer2, 0 ) )
1120 goto end; // not the same
1122 product2 = ( CFStringRef ) CFDictionaryGetValue( hidProperties2,
1123 CFSTR( kIOHIDProductKey ) );
1124 if( ( product && !product2 ) || ( !product && product2 ) )
1125 goto end; // not the same
1126 if( product && product2 && CFStringCompare( product, product2, 0 ) )
1127 goto end; // not the same
1129 // if execution reaches this point, then they are the same
1130 result = TRUE;
1132 end:
1133 CFRelease( hidProperties2 );
1134 return result;
1137 //------------------------------------------------------------------------------
1138 // name: disconnect()
1139 // desc: called when device is physically disconnected from the machine.
1140 // deallocates per-connection resources, but leaves stuff that should be the
1141 // same across connections (element list, name, other general properties, etc)
1142 //------------------------------------------------------------------------------
1143 void OSX_Hid_Device::disconnect()
1145 // can only be called in Hid thread, or if the hid thread hasnt started
1146 assert( rlHid == NULL || rlHid == CFRunLoopGetCurrent() );
1148 if( plugInInterface )
1150 ( *plugInInterface )->Release( plugInInterface );
1151 plugInInterface = NULL;
1154 if( queue )
1156 if( refcount )
1158 ( *queue )->stop( queue );
1160 if( rlHid )
1161 CFRunLoopRemoveSource( rlHid, eventSource,
1162 kCFRunLoopChuckHidMode );
1165 ( *queue )->dispose( queue );
1166 ( *queue )->Release( queue );
1167 queue = NULL;
1170 if( interface )
1172 ( *interface )->close( interface );
1173 ( *interface )->Release( interface );
1174 interface = NULL;
1177 if( removal_notification )
1179 IOObjectRelease( removal_notification );
1180 removal_notification = NULL;
1183 configed = FALSE;
1184 preconfiged = FALSE;
1187 //------------------------------------------------------------------------------
1188 // name: cleanup()
1189 // desc: deallocates all resources associated with the device
1190 //------------------------------------------------------------------------------
1191 void OSX_Hid_Device::cleanup()
1193 // can only be called in Hid thread, or if the hid thread hasnt started
1194 assert( rlHid == NULL || rlHid == CFRunLoopGetCurrent() );
1196 disconnect();
1198 // delete all elements
1199 if( elements )
1201 this->lock();
1203 // iterate through the axis map, delete device element records
1204 map< IOHIDElementCookie, OSX_Hid_Device_Element * >::iterator iter, end;
1205 end = elements->end();
1206 for( iter = elements->begin(); iter != end; iter++ )
1207 delete iter->second;
1208 delete elements;
1209 elements = NULL;
1211 // TODO: delete outputs also
1213 this->unlock();
1216 if( hidProperties )
1218 CFRelease( hidProperties );
1219 hidProperties = NULL;
1222 this->lock();
1224 if( locationID )
1226 CFRelease( locationID );
1227 locationID = NULL;
1230 if( vendorID )
1232 CFRelease( vendorID );
1233 vendorID = NULL;
1236 if( productID )
1238 CFRelease( productID );
1239 productID = NULL;
1242 if( manufacturer )
1244 CFRelease( manufacturer );
1245 manufacturer = NULL;
1248 if( product )
1250 CFRelease( product );
1251 product = NULL;
1254 this->unlock();
1257 static void Hid_key_table_init()
1259 memset( g_hid_key_table, 0, sizeof( g_hid_key_table ) );
1261 // ASCII letters
1262 g_hid_key_table[kHIDUsage_KeyboardA] = 'A';
1263 g_hid_key_table[kHIDUsage_KeyboardB] = 'B';
1264 g_hid_key_table[kHIDUsage_KeyboardC] = 'C';
1265 g_hid_key_table[kHIDUsage_KeyboardD] = 'D';
1266 g_hid_key_table[kHIDUsage_KeyboardE] = 'E';
1267 g_hid_key_table[kHIDUsage_KeyboardF] = 'F';
1268 g_hid_key_table[kHIDUsage_KeyboardG] = 'G';
1269 g_hid_key_table[kHIDUsage_KeyboardH] = 'H';
1270 g_hid_key_table[kHIDUsage_KeyboardI] = 'I';
1271 g_hid_key_table[kHIDUsage_KeyboardJ] = 'J';
1272 g_hid_key_table[kHIDUsage_KeyboardK] = 'K';
1273 g_hid_key_table[kHIDUsage_KeyboardL] = 'L';
1274 g_hid_key_table[kHIDUsage_KeyboardM] = 'M';
1275 g_hid_key_table[kHIDUsage_KeyboardN] = 'N';
1276 g_hid_key_table[kHIDUsage_KeyboardO] = 'O';
1277 g_hid_key_table[kHIDUsage_KeyboardP] = 'P';
1278 g_hid_key_table[kHIDUsage_KeyboardQ] = 'Q';
1279 g_hid_key_table[kHIDUsage_KeyboardR] = 'R';
1280 g_hid_key_table[kHIDUsage_KeyboardS] = 'S';
1281 g_hid_key_table[kHIDUsage_KeyboardT] = 'T';
1282 g_hid_key_table[kHIDUsage_KeyboardU] = 'U';
1283 g_hid_key_table[kHIDUsage_KeyboardV] = 'V';
1284 g_hid_key_table[kHIDUsage_KeyboardW] = 'W';
1285 g_hid_key_table[kHIDUsage_KeyboardX] = 'X';
1286 g_hid_key_table[kHIDUsage_KeyboardY] = 'Y';
1287 g_hid_key_table[kHIDUsage_KeyboardZ] = 'Z';
1289 // ASCII numbers
1290 g_hid_key_table[kHIDUsage_Keyboard1] = '1';
1291 g_hid_key_table[kHIDUsage_Keyboard2] = '2';
1292 g_hid_key_table[kHIDUsage_Keyboard3] = '3';
1293 g_hid_key_table[kHIDUsage_Keyboard4] = '4';
1294 g_hid_key_table[kHIDUsage_Keyboard5] = '5';
1295 g_hid_key_table[kHIDUsage_Keyboard6] = '6';
1296 g_hid_key_table[kHIDUsage_Keyboard7] = '7';
1297 g_hid_key_table[kHIDUsage_Keyboard8] = '8';
1298 g_hid_key_table[kHIDUsage_Keyboard9] = '9';
1299 g_hid_key_table[kHIDUsage_Keyboard0] = '0';
1301 // ASCII whitespace/control characters
1302 g_hid_key_table[kHIDUsage_KeyboardEscape] = '\e';
1303 g_hid_key_table[kHIDUsage_KeyboardDeleteOrBackspace] = '\b';
1304 g_hid_key_table[kHIDUsage_KeyboardReturnOrEnter] = '\n';
1305 g_hid_key_table[kHIDUsage_KeyboardTab] = '\t';
1306 g_hid_key_table[kHIDUsage_KeyboardSpacebar] = ' ';
1308 // ASCII symbols
1309 g_hid_key_table[kHIDUsage_KeyboardHyphen] = '-';
1310 g_hid_key_table[kHIDUsage_KeyboardEqualSign] = '=';
1311 g_hid_key_table[kHIDUsage_KeyboardOpenBracket] = '[';
1312 g_hid_key_table[kHIDUsage_KeyboardCloseBracket] = ']';
1313 g_hid_key_table[kHIDUsage_KeyboardBackslash] = '\\';
1314 g_hid_key_table[kHIDUsage_KeyboardNonUSPound] = '#';
1315 g_hid_key_table[kHIDUsage_KeyboardSemicolon] = ';';
1316 g_hid_key_table[kHIDUsage_KeyboardQuote] = '\'';
1317 g_hid_key_table[kHIDUsage_KeyboardGraveAccentAndTilde] = '`';
1318 g_hid_key_table[kHIDUsage_KeyboardComma] = ',';
1319 g_hid_key_table[kHIDUsage_KeyboardPeriod] = '.';
1320 g_hid_key_table[kHIDUsage_KeyboardSlash] = '/';
1322 // ASCII keypad symbols/whitespace
1323 g_hid_key_table[kHIDUsage_KeypadSlash] = '/';
1324 g_hid_key_table[kHIDUsage_KeypadAsterisk] = '*';
1325 g_hid_key_table[kHIDUsage_KeypadHyphen] = '-';
1326 g_hid_key_table[kHIDUsage_KeypadPlus] = '+';
1327 g_hid_key_table[kHIDUsage_KeypadEnter] = '\n';
1329 // ASCII keypad numbers
1330 g_hid_key_table[kHIDUsage_Keypad1] = '1';
1331 g_hid_key_table[kHIDUsage_Keypad2] = '2';
1332 g_hid_key_table[kHIDUsage_Keypad3] = '3';
1333 g_hid_key_table[kHIDUsage_Keypad4] = '4';
1334 g_hid_key_table[kHIDUsage_Keypad5] = '5';
1335 g_hid_key_table[kHIDUsage_Keypad6] = '6';
1336 g_hid_key_table[kHIDUsage_Keypad7] = '7';
1337 g_hid_key_table[kHIDUsage_Keypad8] = '8';
1338 g_hid_key_table[kHIDUsage_Keypad9] = '9';
1339 g_hid_key_table[kHIDUsage_Keypad0] = '0';
1341 // ASCII keypad symbols
1342 g_hid_key_table[kHIDUsage_KeypadPeriod] = '.';
1343 g_hid_key_table[kHIDUsage_KeyboardNonUSBackslash] = '\\';
1344 g_hid_key_table[kHIDUsage_KeypadEqualSign] = '=';
1345 g_hid_key_table[kHIDUsage_KeypadComma] = ',';
1346 g_hid_key_table[kHIDUsage_KeypadEqualSignAS400] = '=';
1349 t_CKINT Hid_hwkey_to_ascii( t_CKINT hwkey )
1351 if( hwkey < 0 || hwkey >= sizeof( g_hid_key_table ) )
1352 return -1;
1354 return g_hid_key_table[hwkey];
1357 void Hid_init()
1359 Hid_key_table_init();
1361 rlHid = CFRunLoopGetCurrent();
1363 // set up notification for new added devices/removed devices
1364 CFRunLoopAddSource( rlHid,
1365 IONotificationPortGetRunLoopSource( newDeviceNotificationPort ),
1366 kCFRunLoopChuckHidMode );
1368 // add open/close operation source
1369 CFRunLoopSourceContext opSourceContext;
1370 opSourceContext.version = 0;
1371 opSourceContext.info = NULL;
1372 opSourceContext.retain = NULL;
1373 opSourceContext.release = NULL;
1374 opSourceContext.copyDescription = NULL;
1375 opSourceContext.equal = NULL;
1376 opSourceContext.hash = NULL;
1377 opSourceContext.schedule = NULL;
1378 opSourceContext.cancel = NULL;
1379 opSourceContext.perform = Hid_do_operation;
1380 CFRunLoopSourceRef _hidOpSource = CFRunLoopSourceCreate( kCFAllocatorDefault,
1382 &opSourceContext );
1383 CFRunLoopAddSource( rlHid, _hidOpSource, kCFRunLoopChuckHidMode );
1384 hidOpSource = _hidOpSource;
1386 Hid_do_operation( NULL );
1388 #ifdef __CK_HID_WIIREMOTE__
1389 // add wii remote source
1390 CFRunLoopSourceContext wrSourceContext;
1391 wrSourceContext.version = 0;
1392 wrSourceContext.info = NULL;
1393 wrSourceContext.retain = NULL;
1394 wrSourceContext.release = NULL;
1395 wrSourceContext.copyDescription = NULL;
1396 wrSourceContext.equal = NULL;
1397 wrSourceContext.hash = NULL;
1398 wrSourceContext.schedule = NULL;
1399 wrSourceContext.cancel = NULL;
1400 wrSourceContext.perform = WiiRemote_cfrl_callback;
1401 CFRunLoopSourceRef _cfrlWiiRemoteSource = CFRunLoopSourceCreate( kCFAllocatorDefault,
1403 &wrSourceContext );
1404 CFRunLoopAddSource( rlHid, _cfrlWiiRemoteSource, kCFRunLoopChuckHidMode );
1405 cfrlWiiRemoteSource = _cfrlWiiRemoteSource;
1407 WiiRemote_cfrl_callback( NULL );
1408 #endif
1411 void Hid_init2()
1413 // verify that the joystick system has not already been initialized
1414 if( g_hid_init == TRUE )
1415 return;
1417 g_hid_init = TRUE;
1419 IOReturn result = kIOReturnSuccess;
1420 io_iterator_t hidObjectIterator = 0;
1421 CFTypeRef refCF;
1422 t_CKINT filter_usage_page = kHIDPage_GenericDesktop;
1424 // allocate vectors of device records
1425 joysticks = new xvector< OSX_Hid_Device * >;
1426 mice = new xvector< OSX_Hid_Device * >;
1427 keyboards = new xvector< OSX_Hid_Device * >;
1429 joystick_names = new xmultimap< string, OSX_Hid_Device * >;
1430 mouse_names = new xmultimap< string, OSX_Hid_Device * >;
1431 keyboard_names = new xmultimap< string, OSX_Hid_Device * >;
1433 hid_operation_buffer = new CBufferSimple;
1434 hid_operation_buffer->initialize( 20, sizeof( OSX_Hid_op ) );
1436 CFMutableDictionaryRef hidMatchDictionary = IOServiceMatching( kIOHIDDeviceKey );
1437 if( !hidMatchDictionary )
1439 EM_log( CK_LOG_SEVERE, "hid: error: unable to retrieving hidMatchDictionary, unable to initialize" );
1440 return;
1443 /*refCF = ( CFTypeRef ) CFNumberCreate( kCFAllocatorDefault,
1444 kCFNumberLongType,
1445 &filter_usage_page );
1446 CFDictionarySetValue( hidMatchDictionary,
1447 CFSTR( kIOHIDPrimaryUsagePageKey ), refCF );*/
1449 newDeviceNotificationPort = IONotificationPortCreate( kIOMasterPortDefault );
1451 result = IOServiceAddMatchingNotification( newDeviceNotificationPort,
1452 kIOFirstMatchNotification,
1453 hidMatchDictionary,
1454 Hid_new_devices,
1455 NULL,
1456 &hidObjectIterator );
1458 if( result != kIOReturnSuccess || hidObjectIterator == 0 )
1460 EM_log( CK_LOG_SEVERE, "hid: error: unable to retrieving matching services, unable to initialize" );
1461 return;
1464 //CFRelease( refCF );
1466 Hid_new_devices( NULL, hidObjectIterator );
1469 static void Hid_new_devices( void * refcon, io_iterator_t iterator )
1471 EM_log( CK_LOG_INFO, "hid: new device(s) found" );
1473 char __temp[256];
1475 CFTypeRef refCF = NULL;
1477 io_object_t ioHIDDeviceObject = 0;
1478 t_CKINT usage, usage_page;
1479 t_CKINT joysticks_seen = joysticks->size(),
1480 mice_seen = mice->size(),
1481 keyboards_seen = keyboards->size();
1483 while( 1 )
1485 if( ioHIDDeviceObject )
1486 IOObjectRelease( ioHIDDeviceObject );
1488 ioHIDDeviceObject = IOIteratorNext( iterator );
1489 if( ioHIDDeviceObject == 0 )
1490 break;
1492 // ascertain device information
1494 // first, determine the device usage page and usage
1495 usage = usage_page = -1;
1497 refCF = IORegistryEntryCreateCFProperty( ioHIDDeviceObject,
1498 CFSTR( kIOHIDPrimaryUsagePageKey ),
1499 kCFAllocatorDefault,
1500 kNilOptions );
1501 if( !refCF )
1502 continue;
1504 CFNumberGetValue( ( CFNumberRef )refCF, kCFNumberLongType, &usage_page );
1505 CFRelease( refCF );
1507 refCF = IORegistryEntryCreateCFProperty( ioHIDDeviceObject,
1508 CFSTR( kIOHIDPrimaryUsageKey ),
1509 kCFAllocatorDefault,
1510 kNilOptions);
1511 if( !refCF )
1512 continue;
1514 CFNumberGetValue( ( CFNumberRef )refCF, kCFNumberLongType, &usage );
1515 CFRelease( refCF );
1517 if( usage_page != kHIDPage_GenericDesktop )
1519 // some sort of HID device we dont recognize
1520 // lets probe its input/output reports and try to categorize it
1521 EM_log( CK_LOG_INFO, "hid: device not recognized, attempting to determine HID type" );
1523 // allocate the device record, set usage page and usage
1524 OSX_Hid_Device * new_device = new OSX_Hid_Device;
1525 new_device->type = CK_HID_DEV_NONE;
1526 new_device->num = 0;
1527 new_device->usage_page = usage_page;
1528 new_device->usage = usage;
1530 if( !new_device->preconfigure( ioHIDDeviceObject ) && !new_device->configure() )
1532 if( new_device->hats > 0 || new_device->axes > 2 )
1533 // make it a joystick
1535 usage_page = kHIDPage_GenericDesktop;
1536 usage = kHIDUsage_GD_Joystick;
1539 else if( new_device->axes == 2 )
1540 // make it a mouse
1542 usage_page = kHIDPage_GenericDesktop;
1543 usage = kHIDUsage_GD_Mouse;
1546 else if( new_device->buttons > 0 )
1547 // make it a keyboard
1549 usage_page = kHIDPage_GenericDesktop;
1550 usage = kHIDUsage_GD_Keyboard;
1553 new_device->cleanup();
1556 else
1558 //EM_log( );
1561 delete new_device;
1564 if ( usage_page == kHIDPage_GenericDesktop )
1566 if( usage == kHIDUsage_GD_Joystick ||
1567 usage == kHIDUsage_GD_GamePad )
1568 // this is a joystick, create a new item in the joystick array
1570 // see if this an device that was disconnected being reconnected
1571 refCF = IORegistryEntryCreateCFProperty( ioHIDDeviceObject,
1572 CFSTR( kIOHIDProductKey ),
1573 kCFAllocatorDefault,
1574 kNilOptions);
1576 if( refCF )
1578 CFStringGetCString( ( CFStringRef ) refCF, __temp, 256,
1579 kCFStringEncodingASCII );
1580 CFRelease( refCF );
1583 else
1584 strncpy( __temp, "Joystick", 256 );
1586 pair< xmultimap< string, OSX_Hid_Device * >::const_iterator,
1587 xmultimap< string, OSX_Hid_Device * >::const_iterator > name_range
1588 = joystick_names->equal_range( string( __temp ) );
1590 xmultimap< string, OSX_Hid_Device * >::const_iterator
1591 start = name_range.first,
1592 end = name_range.second;
1594 t_CKBOOL match_found = FALSE;
1596 for( ; start != end; start++ )
1598 OSX_Hid_Device * old_device = ( *start ).second;
1599 if( !old_device->is_connected() &&
1600 old_device->is_same_as( ioHIDDeviceObject ) )
1602 match_found = TRUE;
1604 EM_log( CK_LOG_INFO, "hid: joystick %s reattached",
1605 old_device->name );
1607 if( old_device->preconfigure( ioHIDDeviceObject ) )
1609 EM_log( CK_LOG_INFO, "hid: error during reconfiguration of %s",
1610 old_device->name );
1611 break;
1614 if( old_device->refcount &&
1615 !old_device->start() )
1617 HidMsg msg;
1618 msg.device_type = old_device->type;
1619 msg.device_num = old_device->num;
1620 msg.type = CK_HID_DEVICE_CONNECTED;
1622 HidInManager::push_message( msg );
1625 break;
1629 if( match_found )
1630 continue;
1632 // create a new device record for the device
1633 EM_log( CK_LOG_INFO, "hid: preconfiguring joystick %i", joysticks_seen );
1634 EM_pushlog();
1636 // allocate the device record, set usage page and usage
1637 OSX_Hid_Device * new_device = new OSX_Hid_Device;
1638 new_device->type = CK_HID_DEV_JOYSTICK;
1639 new_device->num = joysticks->size();
1640 new_device->usage_page = usage_page;
1641 new_device->usage = usage;
1643 if( !new_device->preconfigure( ioHIDDeviceObject ) )
1645 joysticks->lock();
1646 joysticks->push_back( new_device );
1647 joysticks->unlock();
1649 joystick_names->lock();
1650 joystick_names->insert( pair< string, OSX_Hid_Device * >( string( new_device->name ),
1651 new_device ) );
1652 joystick_names->unlock();
1655 else
1657 EM_log( CK_LOG_INFO,
1658 "hid: ignoring %i, invalid joystick",
1659 joysticks_seen );
1660 delete new_device;
1663 joysticks_seen++;
1665 EM_poplog();
1668 if( usage == kHIDUsage_GD_Mouse )
1669 // this is a mouse
1671 // see if this an device that was disconnected being reconnected
1672 refCF = IORegistryEntryCreateCFProperty( ioHIDDeviceObject,
1673 CFSTR( kIOHIDProductKey ),
1674 kCFAllocatorDefault,
1675 kNilOptions);
1677 if( refCF )
1679 CFStringGetCString( ( CFStringRef ) refCF, __temp, 256,
1680 kCFStringEncodingASCII );
1681 CFRelease( refCF );
1684 else
1685 strncpy( __temp, "Mouse", 256 );
1687 pair< xmultimap< string, OSX_Hid_Device * >::const_iterator,
1688 xmultimap< string, OSX_Hid_Device * >::const_iterator > name_range
1689 = mouse_names->equal_range( string( __temp ) );
1691 xmultimap< string, OSX_Hid_Device * >::const_iterator
1692 start = name_range.first,
1693 end = name_range.second;
1695 t_CKBOOL match_found = FALSE;
1697 for( ; start != end; start++ )
1699 OSX_Hid_Device * old_device = ( *start ).second;
1700 if( !old_device->is_connected() &&
1701 old_device->is_same_as( ioHIDDeviceObject ) )
1703 match_found = TRUE;
1705 EM_log( CK_LOG_INFO, "hid: mouse %s reattached",
1706 old_device->name );
1708 if( old_device->preconfigure( ioHIDDeviceObject ) )
1710 EM_log( CK_LOG_INFO, "hid: error during reconfiguration of %s",
1711 old_device->name );
1712 break;
1715 if( old_device->refcount &&
1716 !old_device->start() )
1718 HidMsg msg;
1719 msg.device_type = old_device->type;
1720 msg.device_num = old_device->num;
1721 msg.type = CK_HID_DEVICE_CONNECTED;
1723 HidInManager::push_message( msg );
1726 break;
1730 if( match_found )
1731 continue;
1733 // create a new device record for the device
1734 EM_log( CK_LOG_INFO, "hid: preconfiguring mouse %i", mice_seen );
1735 EM_pushlog();
1737 // allocate the device record, set usage page and usage
1738 OSX_Hid_Device * new_device = new OSX_Hid_Device;
1739 new_device->type = CK_HID_DEV_MOUSE;
1740 new_device->num = mice->size();
1741 new_device->usage_page = usage_page;
1742 new_device->usage = usage;
1744 if( !new_device->preconfigure( ioHIDDeviceObject ) )
1746 mice->lock();
1747 mice->push_back( new_device );
1748 mice->unlock();
1750 mouse_names->lock();
1751 mouse_names->insert( pair< string, OSX_Hid_Device * >( string( new_device->name ),
1752 new_device ) );
1753 mouse_names->unlock();
1756 else
1758 EM_log( CK_LOG_INFO,
1759 "hid: ignoring %i, invalid mouse",
1760 mice_seen );
1761 delete new_device;
1764 mice_seen++;
1766 EM_poplog();
1769 if( usage == kHIDUsage_GD_Keyboard || usage == kHIDUsage_GD_Keypad )
1770 // this is a keyboard
1772 // see if this an device that was disconnected being reconnected
1773 refCF = IORegistryEntryCreateCFProperty( ioHIDDeviceObject,
1774 CFSTR( kIOHIDProductKey ),
1775 kCFAllocatorDefault,
1776 kNilOptions);
1778 if( refCF )
1780 CFStringGetCString( ( CFStringRef ) refCF, __temp, 256,
1781 kCFStringEncodingASCII );
1782 CFRelease( refCF );
1785 else
1786 strncpy( __temp, "Keyboard", 256 );
1788 pair< xmultimap< string, OSX_Hid_Device * >::const_iterator,
1789 xmultimap< string, OSX_Hid_Device * >::const_iterator > name_range
1790 = keyboard_names->equal_range( string( __temp ) );
1792 xmultimap< string, OSX_Hid_Device * >::const_iterator
1793 start = name_range.first,
1794 end = name_range.second;
1796 t_CKBOOL match_found = FALSE;
1798 for( ; start != end; start++ )
1800 OSX_Hid_Device * old_device = ( *start ).second;
1801 if( !old_device->is_connected() &&
1802 old_device->is_same_as( ioHIDDeviceObject ) )
1804 match_found = TRUE;
1806 EM_log( CK_LOG_INFO, "hid: keyboard %s reattached",
1807 old_device->name );
1809 if( old_device->preconfigure( ioHIDDeviceObject ) )
1811 EM_log( CK_LOG_INFO, "hid: error during reconfiguration of %s",
1812 old_device->name );
1813 break;
1816 if( old_device->refcount &&
1817 !old_device->start() )
1819 HidMsg msg;
1820 msg.device_type = old_device->type;
1821 msg.device_num = old_device->num;
1822 msg.type = CK_HID_DEVICE_CONNECTED;
1824 HidInManager::push_message( msg );
1827 break;
1831 if( match_found )
1832 continue;
1834 // create a new device record for the device
1835 EM_log( CK_LOG_INFO, "hid: preconfiguring keyboard %i",
1836 keyboards_seen );
1837 EM_pushlog();
1839 // allocate the device record, set usage page and usage
1840 OSX_Hid_Device * new_device = new OSX_Hid_Device;
1841 new_device->type = CK_HID_DEV_KEYBOARD;
1842 new_device->num = keyboards->size();
1843 new_device->usage_page = usage_page;
1844 new_device->usage = usage;
1846 if( !new_device->preconfigure( ioHIDDeviceObject ) )
1848 keyboards->lock();
1849 keyboards->push_back( new_device );
1850 keyboards->unlock();
1852 keyboard_names->lock();
1853 keyboard_names->insert( pair< string, OSX_Hid_Device * >( string( new_device->name ),
1854 new_device ) );
1855 keyboard_names->unlock();
1858 else
1860 EM_log( CK_LOG_INFO,
1861 "hid: ignoring %i, invalid keyboard",
1862 keyboards_seen );
1863 delete new_device;
1866 keyboards_seen++;
1868 EM_poplog();
1874 void Hid_device_removed( void * refcon, io_service_t service,
1875 natural_t messageType, void * messageArgument )
1877 if( messageType == kIOMessageServiceIsTerminated && refcon )
1879 OSX_Hid_Device * device = ( OSX_Hid_Device * ) refcon;
1881 if( device->is_connected() )
1883 EM_log( CK_LOG_INFO, "hid: %s disconnected", device->name );
1885 device->lock();
1886 device->disconnect();
1887 device->unlock();
1889 HidMsg msg;
1890 msg.device_type = device->type;
1891 msg.device_num = device->num;
1892 msg.type = CK_HID_DEVICE_DISCONNECTED;
1894 HidInManager::push_message( msg );
1899 #ifdef __CK_HID_WIIREMOTE__
1901 #include "objc/objc.h"
1902 #include "objc/objc-runtime.h"
1903 #include "objc/objc-class.h"
1905 static id createAutoReleasePool()
1907 Class NSAutoreleasePoolClass = ( Class ) objc_getClass( "NSAutoreleasePool" );
1909 id autoReleasePool = class_createInstance( NSAutoreleasePoolClass, 0 );
1911 SEL initSelector = sel_registerName( "init" );
1913 autoReleasePool = objc_msgSend( autoReleasePool, initSelector );
1915 return autoReleasePool;
1918 static void releaseAutoReleasePool()
1923 #endif
1925 void Hid_poll()
1927 #ifdef __CK_HID_WIIREMOTE__
1928 id auto_release_pool = createAutoReleasePool();
1929 #endif
1931 CFRunLoopRunInMode( kCFRunLoopChuckHidMode, 60 * 60 * 24, false );
1934 void Hid_callback( void * target, IOReturn result,
1935 void * refcon, void * sender)
1937 OSX_Hid_Device * device = ( OSX_Hid_Device * ) refcon;
1938 OSX_Hid_Device_Element * element;
1939 AbsoluteTime atZero = { 0, 0 };
1940 IOHIDEventStruct event;
1941 HidMsg msg;
1943 while( result == kIOReturnSuccess )
1945 result = ( *( device->queue ) )->getNextEvent( device->queue,
1946 &event, atZero, 0 );
1947 if( result == kIOReturnUnderrun )
1948 break;
1949 if( result != kIOReturnSuccess )
1951 EM_log( CK_LOG_INFO, "hid: warning: getNextEvent failed" );
1952 break;
1955 element = ( *( device->elements ) )[event.elementCookie];
1957 msg.clear();
1958 msg.device_type = device->type;
1959 msg.device_num = device->num;
1960 msg.type = element->type;
1961 msg.eid = element->num;
1963 switch( msg.type )
1965 case CK_HID_JOYSTICK_AXIS:
1966 // scale the value to [-1.0, 1.0]
1967 msg.fdata[0] = ((t_CKFLOAT)(event.value - element->min)) * 2.0 / ((t_CKFLOAT)(element->max - element->min)) - 1.0;
1968 break;
1970 case CK_HID_JOYSTICK_HAT:
1971 msg.idata[0] = event.value;
1972 break;
1974 case CK_HID_MOUSE_MOTION:
1975 { // gewang: added
1976 msg.eid = 0;
1977 if( element->usage == kHIDUsage_GD_X )
1979 msg.idata[0] = event.value;
1980 msg.idata[1] = 0;
1983 else
1985 msg.idata[0] = 0;
1986 msg.idata[1] = event.value;
1989 #ifndef __CK_HID_CURSOR_TRACK__
1990 Point p;
1991 GetGlobalMouse( &p );
1993 msg.idata[2] = p.h;
1994 msg.idata[3] = p.v;
1996 CGDirectDisplayID display;
1997 CGDisplayCount displayCount;
1999 CGPoint cgp;
2000 cgp.x = p.h;
2001 cgp.y = p.v;
2003 if( CGGetDisplaysWithPoint( cgp, 1, &display, &displayCount ) ==
2004 kCGErrorSuccess )
2006 CGRect bounds = CGDisplayBounds( display );
2008 msg.fdata[0] = ( ( t_CKFLOAT ) ( p.h - bounds.origin.x ) ) /
2009 ( bounds.size.width - 1 );
2010 msg.fdata[1] = ( ( t_CKFLOAT ) ( p.v - bounds.origin.y ) ) /
2011 ( bounds.size.height - 1 );
2013 #else
2014 msg.idata[2] = cursorX;
2015 msg.idata[3] = cursorY;
2016 msg.fdata[0] = scaledCursorX;
2017 msg.fdata[1] = scaledCursorY;
2018 #endif // __CK_HID_CURSOR_TRACK__
2019 } // gewang: added
2020 break;
2022 case CK_HID_MOUSE_WHEEL:
2023 msg.eid = 0;
2025 if( element->num == 1 ) // "X" wheel motion
2027 msg.idata[0] = event.value;
2028 msg.idata[1] = 0;
2031 else // "Y" wheel motion - the default for single wheel systems
2033 msg.idata[0] = 0;
2034 msg.idata[1] = event.value;
2037 break;
2039 case CK_HID_BUTTON_DOWN:
2040 if( event.value == 0 )
2041 msg.type = CK_HID_BUTTON_UP;
2043 msg.idata[0] = event.value;
2045 if( msg.device_type == CK_HID_DEV_KEYBOARD )
2047 msg.eid = element->usage;
2048 msg.idata[1] = element->usage;
2049 msg.idata[2] = Hid_hwkey_to_ascii( element->usage );
2052 break;
2055 HidInManager::push_message( msg );
2059 static void Hid_do_operation( void * info )
2061 OSX_Hid_op op;
2063 while( hid_operation_buffer->get( &op, 1 ) )
2066 OSX_Hid_Device * device = NULL;
2068 switch( op.type )
2070 case OSX_Hid_op::open:
2071 if( op.index >= op.v->size() )
2073 EM_log( CK_LOG_SEVERE, "hid: error: no such device %i", op.index );
2074 return;
2077 device = op.v->at( op.index );
2079 if( device->refcount == 0 )
2080 device->start();
2082 device->refcount++;
2084 break;
2086 case OSX_Hid_op::close:
2087 if( op.index >= op.v->size() )
2089 EM_log( CK_LOG_SEVERE, "hid: error: no such device %i", op.index );
2090 return;
2093 device = op.v->at( op.index );
2095 device->refcount--;
2097 if( device->refcount == 0 )
2098 device->stop();
2100 break;
2105 void Hid_quit()
2107 // stop the run loop; since thread_going is FALSE, the poll thread will exit
2108 if( rlHid )
2109 CFRunLoopStop( rlHid );
2111 if( hidOpSource )
2113 CFRunLoopRemoveSource( rlHid, hidOpSource, kCFRunLoopChuckHidMode );
2114 CFRelease( hidOpSource );
2115 hidOpSource = NULL;
2118 if( newDeviceNotificationPort )
2120 IONotificationPortDestroy( newDeviceNotificationPort );
2121 newDeviceNotificationPort = NULL;
2124 rlHid = NULL;
2126 #ifdef __CK_HID_CURSOR_TRACK__
2127 Mouse_stop_cursor_track();
2128 #endif // __CK_HID_CURSOR_TRACK__
2131 void Hid_quit2()
2133 if( g_hid_init == FALSE )
2134 return;
2135 g_hid_init = FALSE;
2137 delete hid_operation_buffer;
2139 xvector< OSX_Hid_Device * >::size_type i, len;
2141 len = joysticks->size();
2142 for( i = 0; i < len; i++ )
2143 delete joysticks->at( i );
2145 delete joysticks;
2146 joysticks = NULL;
2148 len = mice->size();
2149 for( i = 0; i < len; i++ )
2150 delete mice->at( i );
2152 delete mice;
2153 mice = NULL;
2155 len = keyboards->size();
2156 for( i = 0; i < len; i++ )
2157 delete keyboards->at( i );
2159 delete keyboards;
2160 keyboards = NULL;
2162 // TODO: delete xmultimaps
2165 int Hid_count( xvector< OSX_Hid_Device * > * v )
2167 if( v == NULL )
2168 return 0;
2170 v->lock();
2172 int count = v->size();
2174 v->unlock();
2176 return count;
2179 int Hid_count_elements( xvector< OSX_Hid_Device * > * v, int i, int type )
2181 if( v == NULL || i < 0 )
2182 return -1;
2184 int r = 0;
2186 v->lock();
2188 if( i >= v->size() )
2190 v->unlock();
2191 return -1;
2194 OSX_Hid_Device * device = v->at( i );
2196 device->lock();
2198 v->unlock();
2200 switch( type )
2202 case CK_HID_JOYSTICK_AXIS:
2203 if( device->type == CK_HID_DEV_JOYSTICK )
2204 r = device->axes;
2205 break;
2207 case CK_HID_BUTTON_DOWN:
2208 case CK_HID_BUTTON_UP:
2209 r = device->buttons;
2210 break;
2212 case CK_HID_JOYSTICK_HAT:
2213 r = device->hats;
2214 break;
2216 case CK_HID_JOYSTICK_BALL:
2217 r = 0;
2218 break;
2220 case CK_HID_MOUSE_MOTION:
2221 if( device->type == CK_HID_DEV_MOUSE )
2222 r = device->axes;
2223 break;
2225 case CK_HID_MOUSE_WHEEL:
2226 if( device->type == CK_HID_DEV_MOUSE )
2227 r = device->wheels;
2228 break;
2230 case CK_HID_LED:
2231 if( ( *device->outputs )[type] )
2232 r = ( *device->outputs )[type]->size();
2233 break;
2236 device->unlock();
2238 return r;
2241 int Hid_open( xvector< OSX_Hid_Device * > * v, int i )
2243 if( v == NULL || i < 0 )
2244 return -1;
2246 v->lock();
2248 if( i >= v->size() )
2250 v->unlock();
2251 return -1;
2254 v->unlock();
2256 OSX_Hid_op op;
2257 op.type = OSX_Hid_op::open;
2258 op.v = v;
2259 op.index = i;
2261 hid_operation_buffer->put( &op, 1 );
2263 if( hidOpSource && rlHid )
2265 CFRunLoopSourceSignal( hidOpSource );
2266 CFRunLoopWakeUp( rlHid );
2269 return 0;
2272 int Hid_close( xvector< OSX_Hid_Device * > * v, int i )
2274 if( v == NULL || i < 0 )
2275 return -1;
2277 v->lock();
2279 if( i >= v->size() )
2281 v->unlock();
2282 return -1;
2285 v->unlock();
2287 OSX_Hid_op op;
2288 op.type = OSX_Hid_op::close;
2289 op.v = v;
2290 op.index = i;
2292 hid_operation_buffer->put( &op, 1 );
2294 if( hidOpSource && rlHid )
2296 CFRunLoopSourceSignal( hidOpSource );
2297 CFRunLoopWakeUp( rlHid );
2300 return 0;
2303 const char * Hid_name( xvector< OSX_Hid_Device * > * v, int i )
2305 if( v == NULL || i < 0 )
2306 return NULL;
2308 v->lock();
2310 if( i >= v->size() )
2312 v->unlock();
2313 return NULL;
2316 OSX_Hid_Device * device = v->at( i );
2318 v->unlock();
2320 device->lock();
2322 const char * name = device->name;
2324 device->unlock();
2326 return name;
2329 #pragma mark OS X Joystick support
2331 void Joystick_init()
2333 Hid_init2();
2336 void Joystick_poll()
2340 void Joystick_quit()
2342 Hid_quit2();
2345 int Joystick_count()
2347 return Hid_count( joysticks );
2350 int Joystick_count_elements( int js, int type )
2352 return Hid_count_elements( joysticks, js, type );
2355 int Joystick_open( int js )
2357 return Hid_open( joysticks, js );
2360 int Joystick_close( int js )
2362 return Hid_close( joysticks, js );
2365 const char * Joystick_name( int js )
2367 return Hid_name( joysticks, js );
2370 #pragma mark OS X Mouse support
2372 void Mouse_init()
2374 Hid_init2();
2377 void Mouse_poll()
2381 void Mouse_quit()
2383 Hid_quit2();
2386 int Mouse_count()
2388 return Hid_count( mice );
2391 int Mouse_count_elements( int m, int type )
2393 return Hid_count_elements( mice, m, type );
2396 int Mouse_open( int m )
2398 return Hid_open( mice, m );
2401 int Mouse_close( int m )
2403 return Hid_close( mice, m );
2406 const char * Mouse_name( int m )
2408 return Hid_name( mice, m );
2411 #ifdef __CK_HID_CURSOR_TRACK__
2412 CGPoint CGEventGetLocation(CGEventRef event);
2414 CGEventRef Mouse_cursor_track_cb( CGEventTapProxy proxy, CGEventType type,
2415 CGEventRef event, void * refcon )
2417 CGPoint p = CGEventGetLocation( event );
2419 cursorX = ( t_CKINT ) p.x;
2420 cursorY = ( t_CKINT ) p.y;
2422 CGDirectDisplayID display;
2423 CGDisplayCount displayCount;
2425 if( CGGetDisplaysWithPoint( p, 1, &display, &displayCount ) ==
2426 kCGErrorSuccess )
2428 CGRect bounds = CGDisplayBounds( display );
2430 scaledCursorX = ( ( t_CKFLOAT ) ( p.x - bounds.origin.x ) ) /
2431 ( bounds.size.width - 1 );
2432 scaledCursorY = ( ( t_CKFLOAT ) ( p.y - bounds.origin.y ) ) /
2433 ( bounds.size.height - 1 );
2436 return event;
2439 void * Mouse_cursor_track( void * )
2441 EM_log( CK_LOG_INFO, "hid: starting cursor track" );
2443 cursorX = 0;
2444 cursorY = 0;
2446 rlCursorTrack = CFRunLoopGetCurrent();
2448 CFMachPortRef machPort = CGEventTapCreate( kCGSessionEventTap,
2449 kCGHeadInsertEventTap,
2450 kCGEventTapOptionListenOnly,
2451 CGEventMaskBit( kCGEventMouseMoved ),
2452 Mouse_cursor_track_cb, NULL );
2453 CFRunLoopSourceRef tapSource;
2454 if( machPort != NULL )
2456 tapSource = CFMachPortCreateRunLoopSource( NULL, machPort, 0 );
2457 CFRunLoopAddSource( rlCursorTrack, tapSource, kCFRunLoopChuckHidMode );
2460 else
2462 EM_log( CK_LOG_WARNING, "hid: cursor position listener startup failed" );
2463 return 0;
2466 while( g_ct_go )
2467 CFRunLoopRunInMode( kCFRunLoopChuckHidMode, 60 * 60 * 24, FALSE );
2469 CFRelease( machPort );
2470 CFRelease( tapSource );
2471 rlCursorTrack = NULL;
2473 EM_log( CK_LOG_INFO, "hid: stopping cursor track" );
2475 return 0;
2478 #endif // __CK_HID_CURSOR_TRACK__
2480 int Mouse_start_cursor_track()
2482 #ifdef __CK_HID_CURSOR_TRACK__
2483 if( &CGEventTapCreate == NULL )
2484 return -1;
2486 if( g_ct_go )
2487 return 0;
2489 g_ct_go = TRUE;
2490 pthread_t ct_thread;
2492 if( pthread_create( &ct_thread, NULL, Mouse_cursor_track, NULL ) != 0 )
2494 EM_log( CK_LOG_WARNING, "hid: cursor track thread failed to start" );
2495 return -1;
2498 return 0;
2499 #else
2500 return -1;
2501 #endif // __CK_HID_CURSOR_TRACK__
2504 int Mouse_stop_cursor_track()
2506 #ifdef __CK_HID_CURSOR_TRACK__
2507 if( g_ct_go )
2509 g_ct_go = FALSE;
2511 if( rlCursorTrack )
2512 CFRunLoopStop( rlCursorTrack );
2514 cursorX = 0;
2515 cursorY = 0;
2517 #endif // __CK_HID_CURSOR_TRACK__
2518 return 0;
2521 //#endif /* __CK_HID_CURSORTRACK__ */
2523 #pragma mark OS X Keyboard support
2524 void Keyboard_init()
2526 Hid_init2();
2529 void Keyboard_poll()
2534 void Keyboard_quit()
2536 Hid_quit2();
2539 int Keyboard_count()
2541 return Hid_count( keyboards );
2544 int Keyboard_count_elements( int k, int type )
2546 return Hid_count_elements( keyboards, k, type );
2549 int Keyboard_open( int k )
2551 return Hid_open( keyboards, k );
2554 int Keyboard_close( int k )
2556 return Hid_close( keyboards, k );
2559 int Keyboard_send( int k, const HidMsg * msg )
2561 if( keyboards == NULL || k < 0 || k >= keyboards->size() )
2562 return -1;
2564 OSX_Hid_Device * keyboard = ( *keyboards )[k];
2566 if( keyboard->outputs == NULL || // no outputs
2567 keyboard->outputs->find( msg->type ) == keyboard->outputs->end() )
2568 // no outputs of that type
2569 return -1;
2571 if( msg->eid < 0 || msg->eid >= ( *( keyboard->outputs ) )[msg->type]->size() )
2572 // invalid output type element number
2573 return -1;
2575 OSX_Hid_Device_Element * output = ( *( *( keyboard->outputs ) )[msg->type] )[msg->eid];
2577 IOHIDEventStruct eventStruct;
2579 eventStruct.type = kIOHIDElementTypeOutput;
2580 eventStruct.elementCookie = output->cookie;
2581 eventStruct.timestamp = UpTime();
2582 eventStruct.longValueSize = 0;
2583 eventStruct.longValue = NULL;
2585 if( msg->idata[0] < output->min )
2586 eventStruct.value = output->min;
2587 else if( msg->idata[0] > output->max )
2588 eventStruct.value = output->max;
2589 else
2590 eventStruct.value = msg->idata[0];
2592 IOReturn result;
2593 result = ( *( keyboard->interface ) )->setElementValue( keyboard->interface,
2594 output->cookie,
2595 &eventStruct,
2596 0, 0, 0, 0 );
2597 if( result != kIOReturnSuccess )
2598 return -1;
2600 return 0;
2603 const char * Keyboard_name( int k )
2605 return Hid_name( keyboards, k );
2608 #pragma mark OS X Tilt/Sudden Motion Sensor support
2610 enum
2612 kSMSPowerbookDataType,
2613 kSMSMacBookProDataType
2616 static struct t_TiltSensor_data
2618 union
2620 struct t_powerbook
2622 int8_t x;
2623 int8_t y;
2624 int8_t z;
2625 int8_t pad[57];
2626 } powerbook;
2628 struct t_macbookpro
2630 int16_t x;
2631 int16_t y;
2632 int16_t z;
2633 int8_t pad[34];
2634 } macbookpro;
2635 } data;
2637 int kernFunc;
2638 char * servMatch;
2639 int dataType;
2640 io_connect_t dataPort;
2642 int detected; // 0 = detection not yet run, -1 = no sensor found, 1 = sensor found
2643 int refcount;
2645 t_TiltSensor_data()
2647 refcount = 0;
2648 kernFunc = 0;
2649 servMatch = NULL;
2650 dataType = -1;
2651 dataPort = 0;
2652 detected = 0;
2655 } TiltSensor_data;
2657 // ge: SMS multi-threading
2658 static int TiltSensor_do_read();
2660 //-----------------------------------------------------------------------------
2661 // name: class SMSManager
2662 // desc: ...
2663 //-----------------------------------------------------------------------------
2664 class SMSManager
2666 public:
2667 static t_CKBOOL init();
2668 static void shutdown();
2669 static void on();
2670 static void off();
2672 public:
2673 static t_CKINT the_onoff;
2674 static t_CKBOOL the_init;
2675 static t_CKINT wait_time;
2676 static XThread * the_thread;
2677 static t_CKINT x;
2678 static t_CKINT y;
2679 static t_CKINT z;
2682 // designate new poll rate
2683 t_CKINT TiltSensor_setPollRate( t_CKINT usec )
2685 // sanity
2686 assert( usec >= 0 );
2688 SMSManager::wait_time = usec;
2690 return SMSManager::wait_time;
2693 // query current poll rate
2694 t_CKINT TiltSensor_getPollRate( )
2696 return SMSManager::wait_time;
2699 // initialize
2700 t_CKINT SMSManager::the_onoff = 0;
2701 t_CKBOOL SMSManager::the_init = FALSE;
2702 t_CKINT SMSManager::wait_time = 3000;
2703 XThread * SMSManager::the_thread = NULL;
2704 t_CKINT SMSManager::x = 0;
2705 t_CKINT SMSManager::y = 0;
2706 t_CKINT SMSManager::z = 0;
2709 #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)
2710 static void * sms_loop( void * )
2711 #else
2712 static unsigned int __stdcall sms_loop( void * )
2713 #endif
2715 t_CKINT c;
2716 EM_log( CK_LOG_INFO, "starting SMS multi-threaded loop..." );
2718 // go
2719 while( SMSManager::the_init )
2721 // if on
2722 if( SMSManager::the_onoff )
2724 // poll SMS
2725 TiltSensor_do_read();
2727 // save data
2728 if( TiltSensor_data.dataType == kSMSPowerbookDataType )
2730 SMSManager::x = TiltSensor_data.data.powerbook.x;
2731 SMSManager::y = TiltSensor_data.data.powerbook.y;
2732 SMSManager::z = TiltSensor_data.data.powerbook.z;
2735 else if( TiltSensor_data.dataType == kSMSMacBookProDataType )
2737 SMSManager::x = TiltSensor_data.data.macbookpro.x;
2738 SMSManager::y = TiltSensor_data.data.macbookpro.y;
2739 SMSManager::z = TiltSensor_data.data.macbookpro.z;
2741 // wait
2742 usleep( SMSManager::wait_time );
2744 else
2746 // wait
2747 usleep( 1000 );
2751 // TODO: hack!
2752 SAFE_DELETE( SMSManager::the_thread );
2754 return 0;
2758 // init
2759 t_CKBOOL SMSManager::init()
2761 // sanity
2762 if( the_thread != NULL )
2763 return FALSE;
2765 EM_log( CK_LOG_INFO, "initializing SMSManager..." );
2767 the_onoff = 0;
2768 the_init = TRUE;
2769 the_thread = new XThread;
2770 the_thread->start( sms_loop, NULL );
2772 return TRUE;
2776 // shutdown
2777 void SMSManager::shutdown()
2779 EM_log( CK_LOG_INFO, "shutting down SMSManager..." );
2781 the_onoff = 0;
2782 the_init = FALSE;
2786 // on()
2787 void SMSManager::on()
2789 the_onoff++;
2793 // off()
2794 void SMSManager::off()
2796 the_onoff--;
2800 static int TiltSensor_test( int kernFunc, char * servMatch, int dataType )
2802 kern_return_t result;
2803 io_iterator_t iterator;
2804 io_object_t aDevice;
2805 io_connect_t dataPort;
2807 IOItemCount structureInputSize;
2808 IOByteCount structureOutputSize;
2810 // log
2811 EM_log( CK_LOG_FINE, "testing for SMS sensor..." );
2813 CFMutableDictionaryRef matchingDictionary = IOServiceMatching( servMatch );
2815 result = IOServiceGetMatchingServices( kIOMasterPortDefault, matchingDictionary, &iterator );
2817 if( result != KERN_SUCCESS )
2818 return 0;
2820 aDevice = IOIteratorNext( iterator );
2821 IOObjectRelease( iterator );
2823 if( aDevice == 0 )
2824 return 0;
2826 result = IOServiceOpen( aDevice, mach_task_self(), 0, &dataPort );
2827 IOObjectRelease( aDevice );
2829 if ( result != KERN_SUCCESS )
2830 return 0;
2832 switch( dataType )
2834 case kSMSPowerbookDataType:
2835 structureInputSize = sizeof( TiltSensor_data.data.powerbook );
2836 structureOutputSize = sizeof( TiltSensor_data.data.powerbook );
2837 break;
2839 case kSMSMacBookProDataType:
2840 structureInputSize = sizeof( TiltSensor_data.data.macbookpro );
2841 structureOutputSize = sizeof( TiltSensor_data.data.macbookpro );
2842 break;
2844 default:
2845 IOServiceClose( dataPort );
2846 return 0;
2849 memset( &TiltSensor_data.data, 0, sizeof( TiltSensor_data.data ) );
2850 memset( &TiltSensor_data.data, 0, sizeof( TiltSensor_data.data ) );
2852 result = IOConnectMethodStructureIStructureO( dataPort,
2853 kernFunc,
2854 structureInputSize,
2855 &structureOutputSize,
2856 &TiltSensor_data.data,
2857 &TiltSensor_data.data );
2859 if ( result != KERN_SUCCESS )
2861 IOServiceClose( dataPort );
2862 return 0;
2865 // leave dataPort open for future use
2866 TiltSensor_data.dataPort = dataPort;
2868 return 1;
2871 static int TiltSensor_do_read()
2873 kern_return_t result;
2874 IOItemCount structureInputSize;
2875 IOByteCount structureOutputSize;
2877 // log
2878 EM_log( CK_LOG_FINE, "reading SMS sensor..." );
2880 switch( TiltSensor_data.dataType )
2882 case kSMSPowerbookDataType:
2883 structureInputSize = sizeof( TiltSensor_data.data.powerbook );
2884 structureOutputSize = sizeof( TiltSensor_data.data.powerbook );
2885 break;
2887 case kSMSMacBookProDataType:
2888 structureInputSize = sizeof( TiltSensor_data.data.macbookpro );
2889 structureOutputSize = sizeof( TiltSensor_data.data.macbookpro );
2890 break;
2892 default:
2893 return 0;
2896 memset( &TiltSensor_data.data, 0, sizeof( TiltSensor_data.data ) );
2897 memset( &TiltSensor_data.data, 0, sizeof( TiltSensor_data.data ) );
2899 result = IOConnectMethodStructureIStructureO( TiltSensor_data.dataPort,
2900 TiltSensor_data.kernFunc,
2901 structureInputSize,
2902 &structureOutputSize,
2903 &TiltSensor_data.data,
2904 &TiltSensor_data.data );
2906 return 1;
2909 static int TiltSensor_detect()
2911 // try different interfaces until we find one that works
2913 SInt32 osx_version;
2914 int powerbookKernFunc = 0;
2916 // log
2917 EM_log( CK_LOG_FINE, "detecting SMS sensor..." );
2919 Gestalt( gestaltSystemVersion, &osx_version );
2921 if( osx_version == 0x1040 ||
2922 osx_version == 0x1041 ||
2923 osx_version == 0x1042 ||
2924 osx_version == 0x1043 )
2925 powerbookKernFunc = 24;
2927 else
2928 powerbookKernFunc = 21;
2930 fprintf( stdout, "osx_version = %d \n", osx_version );
2932 // ibook/powerbook (OS X 10.4.x) tilt sensor interface
2933 if( TiltSensor_test( powerbookKernFunc, "IOI2CMotionSensor", kSMSPowerbookDataType ) )
2935 TiltSensor_data.kernFunc = powerbookKernFunc;
2936 TiltSensor_data.dataType = kSMSPowerbookDataType;
2937 TiltSensor_data.detected = 1;
2938 return 1;
2941 // hi resolution powerbook tilt sensor interface
2942 if( TiltSensor_test( powerbookKernFunc, "PMUMotionSensor", kSMSPowerbookDataType ) )
2944 TiltSensor_data.kernFunc = powerbookKernFunc;
2945 TiltSensor_data.dataType = kSMSPowerbookDataType;
2946 TiltSensor_data.detected = 1;
2947 return 1;
2950 // mac book (pro) tilt sensor interface
2951 if( TiltSensor_test( 5, "SMCMotionSensor", kSMSMacBookProDataType ) )
2953 TiltSensor_data.kernFunc = 5;
2954 TiltSensor_data.dataType = kSMSMacBookProDataType;
2955 TiltSensor_data.detected = 1;
2956 return 1;
2959 TiltSensor_data.detected = -1;
2961 return 0;
2964 void TiltSensor_init()
2969 void TiltSensor_quit()
2971 // log
2972 EM_log( CK_LOG_FINE, "quiting SMS bridge..." );
2974 if( TiltSensor_data.dataPort == 0 )
2975 IOServiceClose( TiltSensor_data.dataPort );
2978 void TiltSensor_probe()
2983 int TiltSensor_count()
2985 // log
2986 EM_log( CK_LOG_FINE, "counting SMS sensors..." );
2988 if( TiltSensor_data.detected == 0 )
2989 TiltSensor_detect();
2991 if( TiltSensor_data.detected == -1 )
2992 return 0;
2994 else if( TiltSensor_data.detected == 1 )
2995 return 1;
2997 return 0;
3000 int TiltSensor_open( int ts )
3002 // log
3003 EM_log( CK_LOG_FINE, "opening SMS sensor..." );
3004 EM_pushlog();
3006 if( TiltSensor_data.detected == 0 )
3007 TiltSensor_detect();
3009 if( TiltSensor_data.detected == -1 )
3010 return -1;
3012 TiltSensor_data.refcount++;
3014 // ge: init manager
3015 SMSManager::init();
3016 SMSManager::on();
3018 // log
3019 EM_poplog();
3021 return 0;
3024 int TiltSensor_close( int ts )
3026 TiltSensor_data.refcount--;
3028 // ge: notify
3029 SMSManager::off();
3030 if( TiltSensor_data.refcount <= 0 )
3031 SMSManager::shutdown();
3033 return 0;
3036 const char * TiltSensor_name( int ts )
3038 return "Apple Sudden Motion Sensor";
3041 int TiltSensor_read( int ts, int type, int num, HidMsg * msg )
3043 if( type != CK_HID_ACCELEROMETER )
3044 return -1;
3046 if( TiltSensor_data.detected == -1 )
3047 return -1;
3049 // ge: no longer need in the multi-threaded case...
3050 // if( !TiltSensor_do_read() )
3051 // return -1;
3053 // ge: use sms multi-threaded
3054 msg->idata[0] = SMSManager::x;
3055 msg->idata[1] = SMSManager::y;
3056 msg->idata[2] = SMSManager::z;
3058 return 0;
3061 #ifdef __CK_HID_WIIREMOTE__
3062 #pragma mark OS X Wii Remote support
3064 class Bluetooth_Device : public lockable
3066 public:
3067 Bluetooth_Device()
3069 device = NULL;
3070 memset( &address, 0, sizeof( address ) );
3071 strncpy( name, "Bluetooth Device", 256 );
3072 interrupt_channel = NULL;
3073 control_channel = NULL;
3074 disconnect_notification = NULL;
3076 type = 0;
3077 num = -1;
3079 refcount = 0;
3082 virtual ~Bluetooth_Device()
3084 close();
3087 virtual t_CKINT open() { return -1; }
3088 virtual t_CKINT connect() { return -1; }
3089 virtual t_CKINT control_init() { return -1; }
3090 virtual t_CKINT interrupt_init() { return -1; }
3091 virtual t_CKINT disconnect() { return -1; }
3092 virtual t_CKINT close() { return -1; }
3093 virtual t_CKBOOL is_connected() { return FALSE; }
3095 virtual void control_receive( void * data, size_t size ) {};
3096 virtual void interrupt_receive( void * data, size_t size ) {};
3098 virtual void control_send( void * data, size_t size ) {};
3099 virtual void interrupt_send( void * data, size_t size ) {};
3101 IOBluetoothDeviceRef device;
3102 BluetoothDeviceAddress address;
3103 IOBluetoothL2CAPChannelRef interrupt_channel;
3104 IOBluetoothL2CAPChannelRef control_channel;
3105 IOBluetoothUserNotificationRef disconnect_notification;
3106 char name[256];
3108 t_CKUINT type;
3109 t_CKINT num;
3110 t_CKUINT refcount;
3113 class WiiRemote : public Bluetooth_Device
3115 protected:
3116 enum Extension
3118 ExtensionNone,
3119 ExtensionNunchuk,
3120 ExtensionClassicController
3123 public:
3125 WiiRemote()
3127 force_feedback_enabled = FALSE;
3128 motion_sensor_enabled = FALSE;
3129 ir_sensor_enabled = FALSE;
3130 ir_sensor_initialized = FALSE;
3131 extension_enabled = FALSE;
3132 led1 = led2 = led3 = led4 = FALSE;
3133 speaker_enabled = FALSE;
3134 connected_extension = ExtensionNone;
3135 timer = NULL;
3136 audio_buffer = NULL;
3137 memset( &buttons, 0, sizeof( buttons ) );
3138 memset( &accels, 0, sizeof( accels ) );
3139 memset( &ir, 0xff, sizeof( ir ) );
3140 memset( &extension, 0, sizeof( extension ) );
3141 extension[5] |= 0x03;
3144 virtual t_CKINT open();
3145 virtual t_CKINT connect();
3146 virtual t_CKINT control_init();
3147 virtual t_CKINT disconnect();
3148 virtual t_CKINT close();
3149 virtual t_CKBOOL is_connected();
3151 virtual void control_receive( void * data, size_t size );
3152 virtual void interrupt_receive( void * data, size_t size );
3154 virtual void control_send( const void * data, unsigned int size );
3155 virtual void write_memory( const void * data, unsigned int size, unsigned int address );
3156 virtual void read_memory( unsigned int address, unsigned int size );
3158 virtual void check_extension();
3160 virtual void enable_peripherals( t_CKBOOL force_feedback,
3161 t_CKBOOL motion_sensor,
3162 t_CKBOOL ir_sensor,
3163 t_CKBOOL extension );
3164 virtual void enable_force_feedback( t_CKBOOL enable );
3165 virtual void enable_motion_sensor( t_CKBOOL enable );
3166 virtual void enable_ir_sensor( t_CKBOOL enable );
3167 virtual void enable_extension( t_CKBOOL enable );
3168 virtual void enable_leds( t_CKBOOL l1, t_CKBOOL l2,
3169 t_CKBOOL l3, t_CKBOOL l4 );
3170 virtual void set_led( t_CKUINT led, t_CKBOOL state );
3171 virtual void enable_speaker( t_CKBOOL enable );
3173 CFRunLoopTimerRef timer;
3174 CBufferSimple * audio_buffer;
3175 virtual void send_audio_data();
3177 protected:
3179 virtual void initialize_ir_sensor();
3181 t_CKBOOL force_feedback_enabled;
3182 t_CKBOOL motion_sensor_enabled;
3183 t_CKBOOL ir_sensor_enabled;
3184 t_CKBOOL ir_sensor_initialized;
3185 t_CKBOOL extension_enabled;
3186 t_CKBOOL led1, led2, led3, led4;
3187 t_CKBOOL speaker_enabled;
3189 t_CKBYTE buttons[2];
3190 t_CKBYTE accels[3];
3191 t_CKBYTE ir[12];
3192 t_CKBYTE extension[6];
3194 Extension connected_extension;
3196 enum Button
3198 ButtonHome = 0,
3199 Button1,
3200 Button2,
3201 ButtonPlus,
3202 ButtonMinus,
3203 ButtonA,
3204 ButtonB,
3205 ButtonUp,
3206 ButtonRight,
3207 ButtonDown,
3208 ButtonLeft,
3209 ButtonC,
3210 ButtonZ
3214 bool operator< ( BluetoothDeviceAddress a, BluetoothDeviceAddress b )
3216 for( int i = 0; i < 6; i++ )
3218 if( a.data[i] < b.data[i] )
3219 return true;
3220 if( a.data[i] > b.data[i] )
3221 return false;
3224 return false;
3227 static xvector< WiiRemote * > * wiiremotes = NULL;
3229 /* the user can open a wiimote with any id number he chooses without an error,
3230 and will then be sent the appropriate message when that wiimote is connected.
3231 The nth wiimote is simply the nth wiimote that chuck hid detects, and since
3232 there is absolutely no way to determine how many wiimotes will connect in
3233 advance, this number must be left unbounded. To support this, WiiRemote_open
3234 will add empty WiiRemotes to wiiremotes with refcount of 1 if it the wiimote
3235 its opening isnt there yet, and hope it will be opened at some future time.
3236 Thus when discovering wiimotes we have to distinguish between wiimotes that have
3237 been discovered and wiimotes that have been opened but have not yet been
3238 discovered. all wiimotes with indices less than g_wr_next_real_wiimote have
3239 actually been discovered, and anything at or above that index in wiiremotes has
3240 not actually been discovered, but (if its non-NULL) has been opened. */
3241 static xvector< WiiRemote * >::size_type g_next_real_wiimote = 0;
3243 static map< BluetoothDeviceAddress, WiiRemote * > * wr_addresses = NULL;
3244 static t_CKBOOL g_bt_query_active = FALSE; // is a query currently active?
3246 void Bluetooth_device_connected( void * userRefCon,
3247 IOBluetoothDeviceRef deviceRef,
3248 IOReturn status )
3250 if( status == noErr )
3251 ( ( Bluetooth_Device * ) userRefCon )->connect();
3252 else
3253 EM_log( CK_LOG_WARNING, "hid: error: opening Wii Remote Controller connection" );
3256 void Bluetooth_device_control_event( IOBluetoothL2CAPChannelRef l2capChannel,
3257 void * refCon,
3258 IOBluetoothL2CAPChannelEvent * event )
3260 switch( event->eventType )
3262 case kIOBluetoothL2CAPChannelEventTypeOpenComplete:
3263 ( ( Bluetooth_Device * ) refCon )->control_init();
3264 break;
3266 case kIOBluetoothL2CAPChannelEventTypeData:
3267 ( ( Bluetooth_Device * ) refCon )->control_receive( event->u.data.dataPtr,
3268 event->u.data.dataSize );
3269 break;
3271 case kIOBluetoothL2CAPChannelEventTypeWriteComplete:
3272 if( event->status != noErr )
3274 EM_log( CK_LOG_WARNING, "hid: error: writing data to control L2CAP channel for '%s'",
3275 ( ( Bluetooth_Device * ) refCon )->name );
3278 break;
3280 case kIOBluetoothL2CAPChannelEventTypeClosed:
3281 EM_log( CK_LOG_FINE, "hid: error: control L2CAP channel for '%s' closed",
3282 ( ( Bluetooth_Device * ) refCon )->name );
3283 break;
3287 void Bluetooth_device_interrupt_event( IOBluetoothL2CAPChannelRef l2capChannel,
3288 void * refCon,
3289 IOBluetoothL2CAPChannelEvent * event )
3291 switch( event->eventType )
3293 case kIOBluetoothL2CAPChannelEventTypeData:
3294 ( ( Bluetooth_Device * ) refCon )->control_receive( event->u.data.dataPtr,
3295 event->u.data.dataSize );
3296 break;
3300 void Bluetooth_device_disconnected( void * userRefCon,
3301 IOBluetoothUserNotificationRef inRef,
3302 IOBluetoothObjectRef objectRef )
3304 ( ( Bluetooth_Device * ) userRefCon )->disconnect();
3307 void WiiRemote_send_audio_data( CFRunLoopTimerRef timer, void *info )
3309 WiiRemote * wr = ( WiiRemote * ) info;
3310 wr->send_audio_data();
3313 t_CKINT WiiRemote::open()
3315 // see if its already connected, reconnect if so
3316 if( IOBluetoothDeviceIsConnected( device ) )
3318 if( IOBluetoothDeviceCloseConnection( device ) != noErr )
3320 EM_log( CK_LOG_WARNING, "hid: error: closing Wii Remote Controller connection" );
3321 return -1;
3325 if( IOBluetoothDeviceOpenConnection( device, Bluetooth_device_connected,
3326 this ) != noErr)
3328 EM_log( CK_LOG_WARNING, "hid: error: opening Wii Remote Controller connection" );
3329 return -1;
3332 return 0;
3335 t_CKINT WiiRemote::connect()
3337 disconnect_notification = IOBluetoothDeviceRegisterForDisconnectNotification( device,
3338 Bluetooth_device_disconnected,
3339 this );
3341 if( disconnect_notification == NULL )
3343 EM_log( CK_LOG_WARNING, "hid: error: registering for Wii Remote Controller disconnection notification" );
3344 return -1;
3347 if( IOBluetoothDeviceOpenL2CAPChannelAsync( device, &control_channel, 17,
3348 Bluetooth_device_control_event,
3349 this ) != kIOReturnSuccess )
3351 EM_log( CK_LOG_WARNING, "hid: error: opening Wii Remote Controller L2CAP connection" );
3352 return -1;
3354 //fprintf( stderr, "l2cap control channel ref: 0x%x\n", control_channel );
3355 if( IOBluetoothDeviceOpenL2CAPChannelAsync( device, &interrupt_channel, 19,
3356 Bluetooth_device_interrupt_event,
3357 this ) != kIOReturnSuccess )
3359 EM_log( CK_LOG_WARNING, "hid: error: opening Wii Remote Controller L2CAP connection" );
3360 return -1;
3363 return 0;
3366 t_CKINT WiiRemote::control_init()
3368 check_extension();
3370 //enable_motion_sensor( TRUE );
3371 //enable_ir_sensor( TRUE );
3372 //enable_force_feedback( FALSE );
3373 //enable_extension( TRUE );
3375 enable_peripherals( FALSE, TRUE, TRUE, TRUE );
3377 usleep( 1000 );
3378 enable_leds( ( num % 4 ) == 0, ( ( num - 1 ) % 4 ) == 0,
3379 ( ( num - 2 ) % 4 ) == 0, ( ( num - 3 ) % 4 ) == 0 );
3381 usleep( 1000 );
3382 enable_speaker( FALSE );
3384 HidMsg msg;
3386 msg.device_num = num;
3387 msg.device_type = CK_HID_DEV_WIIREMOTE;
3388 msg.type = CK_HID_DEVICE_CONNECTED;
3390 HidInManager::push_message( msg );
3392 return 0;
3395 t_CKINT WiiRemote::disconnect()
3397 if( interrupt_channel )
3399 IOBluetoothL2CAPChannelCloseChannel( interrupt_channel );
3400 IOBluetoothObjectRelease( interrupt_channel );
3401 interrupt_channel = NULL;
3404 if( control_channel )
3406 IOBluetoothL2CAPChannelCloseChannel( control_channel );
3407 IOBluetoothObjectRelease( control_channel );
3408 control_channel = NULL;
3411 if( device )
3413 IOBluetoothDeviceCloseConnection( device );
3414 IOBluetoothObjectRelease( device );
3415 device = NULL;
3418 if( disconnect_notification )
3420 IOBluetoothUserNotificationUnregister( disconnect_notification );
3421 disconnect_notification = NULL;
3424 HidMsg msg;
3426 msg.device_num = num;
3427 msg.device_type = CK_HID_DEV_WIIREMOTE;
3428 msg.type = CK_HID_DEVICE_DISCONNECTED;
3430 HidInManager::push_message( msg );
3432 return 0;
3435 t_CKINT WiiRemote::close()
3437 if( is_connected() )
3438 disconnect();
3439 return 0;
3442 t_CKBOOL WiiRemote::is_connected()
3444 if( device == NULL )
3445 return FALSE;
3446 return TRUE;
3449 static inline unsigned char wii_remote_extension_decrypt( unsigned char b )
3451 return ( ( b ^ 0x17 ) + 0x17 & 0xFF );
3454 void WiiRemote::control_receive( void * data, size_t size )
3456 unsigned char * d = ( unsigned char * ) data;
3457 HidMsg msg;
3459 if( ( d[1] & 0xf0 ) == 0x30 )
3461 /* buttons */
3463 if( ( d[3] & 0x80 ) ^ ( buttons[1] & 0x80 ) )
3465 msg.device_num = num;
3466 msg.device_type = CK_HID_DEV_WIIREMOTE;
3467 msg.type = ( d[3] & 0x80 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3468 msg.eid = ButtonHome;
3470 HidInManager::push_message( msg );
3472 msg.clear();
3475 if( ( d[3] & 0x02 ) ^ ( buttons[1] & 0x02 ) )
3477 msg.device_num = num;
3478 msg.device_type = CK_HID_DEV_WIIREMOTE;
3479 msg.type = ( d[3] & 0x02 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3480 msg.eid = Button1;
3482 HidInManager::push_message( msg );
3484 msg.clear();
3487 if( ( d[3] & 0x01 ) ^ ( buttons[1] & 0x01 ) )
3489 msg.device_num = num;
3490 msg.device_type = CK_HID_DEV_WIIREMOTE;
3491 msg.type = ( d[3] & 0x01 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3492 msg.eid = Button2;
3494 HidInManager::push_message( msg );
3496 msg.clear();
3499 if( ( d[2] & 0x10 ) ^ ( buttons[0] & 0x10 ) )
3501 msg.device_num = num;
3502 msg.device_type = CK_HID_DEV_WIIREMOTE;
3503 msg.type = ( d[2] & 0x10 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3504 msg.eid = ButtonPlus;
3506 HidInManager::push_message( msg );
3508 msg.clear();
3511 if( ( d[3] & 0x10 ) ^ ( buttons[1] & 0x10 ) )
3513 msg.device_num = num;
3514 msg.device_type = CK_HID_DEV_WIIREMOTE;
3515 msg.type = ( d[3] & 0x10 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3516 msg.eid = ButtonMinus;
3518 HidInManager::push_message( msg );
3520 msg.clear();
3523 if( ( d[3] & 0x08 ) ^ ( buttons[1] & 0x08 ) )
3525 msg.device_num = num;
3526 msg.device_type = CK_HID_DEV_WIIREMOTE;
3527 msg.type = ( d[3] & 0x08 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3528 msg.eid = ButtonA;
3530 HidInManager::push_message( msg );
3532 msg.clear();
3535 if( ( d[3] & 0x04 ) ^ ( buttons[1] & 0x04 ) )
3537 msg.device_num = num;
3538 msg.device_type = CK_HID_DEV_WIIREMOTE;
3539 msg.type = ( d[3] & 0x04 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3540 msg.eid = ButtonB;
3542 HidInManager::push_message( msg );
3544 msg.clear();
3547 if( ( d[2] & 0x08 ) ^ ( buttons[0] & 0x08 ) )
3549 msg.device_num = num;
3550 msg.device_type = CK_HID_DEV_WIIREMOTE;
3551 msg.type = ( d[2] & 0x08 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3552 msg.eid = ButtonUp;
3554 HidInManager::push_message( msg );
3556 msg.clear();
3559 if( ( d[2] & 0x02 ) ^ ( buttons[0] & 0x02 ) )
3561 msg.device_num = num;
3562 msg.device_type = CK_HID_DEV_WIIREMOTE;
3563 msg.type = ( d[2] & 0x02 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3564 msg.eid = ButtonRight;
3566 HidInManager::push_message( msg );
3568 msg.clear();
3571 if( ( d[2] & 0x04 ) ^ ( buttons[0] & 0x04 ) )
3573 msg.device_num = num;
3574 msg.device_type = CK_HID_DEV_WIIREMOTE;
3575 msg.type = ( d[2] & 0x04 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3576 msg.eid = ButtonDown;
3578 HidInManager::push_message( msg );
3580 msg.clear();
3583 if( ( d[2] & 0x01 ) ^ ( buttons[0] & 0x01 ) )
3585 msg.device_num = num;
3586 msg.device_type = CK_HID_DEV_WIIREMOTE;
3587 msg.type = ( d[2] & 0x01 ) ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
3588 msg.eid = ButtonLeft;
3590 HidInManager::push_message( msg );
3592 msg.clear();
3595 memcpy( buttons, d + 2, sizeof( buttons ) );
3597 /* accelerometers */
3598 if( d[1] & 0x01 )
3600 if( d[4] ^ accels[0] ||
3601 d[5] ^ accels[1] ||
3602 d[6] ^ accels[2] )
3604 msg.device_num = num;
3605 msg.device_type = CK_HID_DEV_WIIREMOTE;
3606 msg.type = CK_HID_ACCELEROMETER;
3607 msg.eid = 0;
3608 msg.idata[0] = d[4];
3609 msg.idata[1] = d[5];
3610 msg.idata[2] = d[6];
3612 HidInManager::push_message( msg );
3614 msg.clear();
3617 memcpy( accels, d + 4, sizeof( accels ) );
3620 /* ir sensor */
3621 if( d[1] & 0x02 )
3623 unsigned i;
3625 if( !( d[1] & 0x04 ) )
3627 for( i = 0; i < 4; i++ )
3629 // 12 byte extended IR data format
3630 // available when there are no extension bytes
3631 if( ( d[7 + i * 3] ^ ir[i * 3] ||
3632 d[7 + i * 3 + 1] ^ ir[i * 3 + 1] ||
3633 d[7 + i * 3 + 2] ^ ir[i * 3 + 2] ) &&
3634 ( d[7 + i * 3] != 0xff ) &&
3635 ( d[7 + i * 3 + 1] != 0xff ) &&
3636 ( d[7 + i * 3 + 2] != 0xff ) )
3638 msg.device_num = num;
3639 msg.device_type = CK_HID_DEV_WIIREMOTE;
3640 msg.type = CK_HID_WIIREMOTE_IR;
3641 msg.eid = i;
3642 // x
3643 msg.idata[0] = d[7 + 3 * i] + ( ( d[7 + 3 * i + 2] << 4 ) & 0x300 );
3644 // y
3645 msg.idata[1] = d[7 + 3 * i + 1] + ( ( d[7 + 3 * i + 2] << 2 ) & 0x300 );
3646 // size
3647 msg.idata[2] = d[7 + 3 * i + 2] & 0xffff;
3649 HidInManager::push_message( msg );
3651 msg.clear();
3656 else
3658 // 9 byte basic IR data format
3659 // available when there are no extension bytes
3662 memcpy( ir, d + 7, sizeof( ir ) );
3665 /* extension */
3666 if( d[1] & 0x04 )
3668 for( int i = 0; i < 6; i++ )
3669 d[17 + i] = wii_remote_extension_decrypt( d[17 + i] );
3671 switch( connected_extension )
3673 case ExtensionNunchuk:
3674 if( d[17] ^ extension[0] ||
3675 d[18] ^ extension[1] )
3676 // joystick axis
3678 msg.device_num = num;
3679 msg.device_type = CK_HID_DEV_WIIREMOTE;
3680 msg.type = CK_HID_JOYSTICK_AXIS;
3681 msg.eid = 0;
3683 // x axis
3684 msg.idata[0] = d[17];
3685 // y axis
3686 msg.idata[1] = d[18];
3688 HidInManager::push_message( msg );
3690 msg.clear();
3693 if( d[19] ^ extension[2] ||
3694 d[20] ^ extension[3] ||
3695 d[21] ^ extension[4] )
3697 msg.device_num = num;
3698 msg.device_type = CK_HID_DEV_WIIREMOTE;
3699 msg.type = CK_HID_ACCELEROMETER;
3700 msg.eid = 1;
3702 // x axis
3703 msg.idata[0] = d[19];
3704 // y axis
3705 msg.idata[1] = d[20];
3706 // z axis
3707 msg.idata[2] = d[21];
3709 HidInManager::push_message( msg );
3711 msg.clear();
3714 if( ( d[22] & ( 1 << 0 ) ) ^ ( extension[5] & ( 1 << 0 ) ) )
3716 msg.device_num = num;
3717 msg.device_type = CK_HID_DEV_WIIREMOTE;
3718 msg.type = ( d[22] & ( 1 << 0 ) ) ? CK_HID_BUTTON_UP : CK_HID_BUTTON_DOWN;
3719 msg.eid = ButtonZ;
3721 HidInManager::push_message( msg );
3723 msg.clear();
3726 if( ( d[22] & ( 1 << 1 ) ) ^ ( extension[5] & ( 1 << 1 ) ) )
3728 msg.device_num = num;
3729 msg.device_type = CK_HID_DEV_WIIREMOTE;
3730 msg.type = ( d[22] & ( 1 << 1 ) ) ? CK_HID_BUTTON_UP : CK_HID_BUTTON_DOWN;
3731 msg.eid = ButtonC;
3733 HidInManager::push_message( msg );
3735 msg.clear();
3738 break;
3741 memcpy( extension, d + 17, 6 );
3745 else if( d[1] == 0x21 )
3747 int i = 0;
3748 i++;
3749 // read memory packet
3750 if( ( d[4] & 0xf0 ) == 0x10 && // size = 2
3751 d[5] == 0x00 && // address = 0x04a400fe (but we only have the first 2 bytes of that here)
3752 d[6] == 0xfe )
3754 unsigned char ext0 = wii_remote_extension_decrypt( d[7] );
3755 unsigned char ext1 = wii_remote_extension_decrypt( d[8] );
3757 if( ext0 == 0x00 && ext1 == 0x00 )
3758 connected_extension = ExtensionNunchuk;
3759 else if( ext0 == 0x01 && ext1 == 0x01 )
3760 connected_extension = ExtensionClassicController;
3761 else
3762 connected_extension = ExtensionNone;
3767 void WiiRemote::interrupt_receive( void * data, size_t size )
3772 void WiiRemote::control_send( const void * data, unsigned int size )
3774 assert( size <= 22 );
3776 unsigned char buf[23];
3778 memset( buf, 0, 23 );
3779 buf[0] = 0x52;
3780 memcpy( buf+1, data, size );
3782 size++;
3784 //printf( "send (%i):", size );
3785 //for( int i = 0; i < size; i++ )
3786 // printf( " %02x", buf[i] );
3787 //printf( "\n" );
3788 //fprintf( stderr, "l2cap control channel ref: 0x%x\n", control_channel );
3790 IOReturn result;
3791 result = IOBluetoothL2CAPChannelWriteSync( control_channel, buf, size );
3792 //IOReturn result = IOBluetoothL2CAPChannelWriteAsync( control_channel, buf, size, NULL );
3794 if( result != kIOReturnSuccess )
3795 EM_log( CK_LOG_WARNING,
3796 "hid: error: sending data to Wii Remote Controller %i (error 0x%x)",
3797 num, result );
3800 void WiiRemote::write_memory( const void * data, unsigned int size,
3801 unsigned int address )
3803 assert( size <= 16 );
3805 unsigned char cmd[22];
3807 memset( cmd, 0, 22 );
3809 cmd[0] = 0x16;
3810 cmd[1] = ( address >> 24 ) & 0xff;
3811 cmd[2] = ( address >> 16 ) & 0xff;
3812 cmd[3] = ( address >> 8 ) & 0xff;
3813 cmd[4] = address & 0xff;
3814 cmd[5] = size;
3816 memcpy( cmd + 6, data, size );
3818 if( force_feedback_enabled )
3819 cmd[1] |= 0x01;
3821 control_send( cmd, 22 );
3824 void WiiRemote::read_memory( unsigned int address, unsigned int size )
3826 unsigned char cmd[7];
3828 cmd[0] = 0x17;
3829 cmd[1] = ( address >> 24 ) & 0xff;
3830 cmd[2] = ( address >> 16 ) & 0xff;
3831 cmd[3] = ( address >> 8 ) & 0xff;
3832 cmd[4] = address & 0xff;
3833 cmd[5] = ( size >> 8 ) & 0xff;
3834 cmd[6] = ( size >> 0 ) & 0xff;
3836 if( force_feedback_enabled )
3837 cmd[1] |= 0x01;
3839 control_send( cmd, 7 );
3842 void WiiRemote::check_extension()
3844 unsigned char key[] = { 0x00 };
3846 // write key
3847 write_memory( key, 1, 0x04a40040 );
3849 // read extension id
3850 read_memory( 0x04a400fe, 2 );
3853 void WiiRemote::enable_peripherals( t_CKBOOL force_feedback,
3854 t_CKBOOL motion_sensor,
3855 t_CKBOOL ir_sensor,
3856 t_CKBOOL extension )
3858 force_feedback_enabled = force_feedback;
3859 motion_sensor_enabled = motion_sensor;
3860 ir_sensor_enabled = ir_sensor;
3861 extension_enabled = extension;
3863 unsigned char cmd[] = { 0x12, 0x00, 0x30 };
3864 if( motion_sensor_enabled )
3865 cmd[2] |= 0x01;
3866 if( ir_sensor_enabled )
3867 cmd[2] |= 0x02;
3868 if( extension_enabled )
3869 cmd[2] |= 0x04;
3870 if( force_feedback_enabled )
3871 cmd[1] |= 0x01;
3873 control_send( cmd, 3 );
3875 unsigned char cmd2[] = { 0x13, 0x00 };
3876 if( force_feedback_enabled )
3877 cmd2[1] |= 0x01;
3878 if( ir_sensor_enabled )
3879 cmd2[1] |= 0x04;
3881 control_send( cmd2, 2 );
3883 if( ir_sensor_enabled && !ir_sensor_initialized )
3884 initialize_ir_sensor();
3887 void WiiRemote::enable_force_feedback( t_CKBOOL enable )
3889 force_feedback_enabled = enable;
3891 unsigned char cmd[] = { 0x13, 0x00 };
3892 if( enable )
3893 cmd[1] |= 0x01;
3894 if( ir_sensor_enabled )
3895 cmd[1] |= 0x04;
3897 control_send( cmd, 2 );
3900 void WiiRemote::enable_motion_sensor( t_CKBOOL enable )
3902 motion_sensor_enabled = enable;
3904 unsigned char cmd[] = { 0x12, 0x00, 0x30 };
3905 if( motion_sensor_enabled )
3906 cmd[2] |= 0x01;
3907 if( ir_sensor_enabled )
3908 cmd[2] |= 0x02;
3909 if( extension_enabled )
3910 cmd[2] |= 0x04;
3911 if( force_feedback_enabled )
3912 cmd[1] |= 0x01;
3914 control_send( cmd, 3 );
3917 void WiiRemote::enable_ir_sensor( t_CKBOOL enable )
3919 ir_sensor_enabled = enable;
3921 enable_motion_sensor( motion_sensor_enabled );
3922 enable_force_feedback( force_feedback_enabled );
3924 unsigned char cmd[] = { 0x1a, 0x00 };
3925 if( ir_sensor_enabled )
3926 cmd[1] |= 0x04;
3927 if( force_feedback_enabled )
3928 cmd[1] |= 0x01;
3930 control_send( cmd, 2 );
3932 if( ir_sensor_enabled && !ir_sensor_initialized )
3933 initialize_ir_sensor();
3936 void WiiRemote::initialize_ir_sensor()
3938 ir_sensor_initialized = TRUE;
3940 unsigned char en0[] = { 0x01 };
3941 write_memory( en0, 1, 0x04b00030 );
3942 //usleep(10000);
3944 unsigned char en[] = { 0x08 };
3945 write_memory( en, 1, 0x04b00030 );
3946 //usleep(10000);
3948 unsigned char sensitivity_block1[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0xc0 };
3949 write_memory( sensitivity_block1, 9, 0x04b00000 );
3950 //usleep(10000);
3952 unsigned char sensitivity_block2[] = { 0x40, 0x00 };
3953 write_memory( sensitivity_block2, 2, 0x04b0001a );
3954 //usleep(10000);
3956 unsigned char mode[] = { 0x03 };
3957 write_memory( mode, 1, 0x04b00033 );
3958 // usleep(10000);
3960 unsigned char what[] = { 0x08 };
3961 write_memory( what, 1, 0x04b00030 );
3962 //usleep(10000);
3965 void WiiRemote::enable_extension( t_CKBOOL enable )
3967 extension_enabled = enable;
3969 enable_motion_sensor( motion_sensor_enabled );
3972 void WiiRemote::set_led( t_CKUINT led, t_CKBOOL state )
3974 switch( led )
3976 case 0:
3977 led1 = state;
3978 break;
3980 case 1:
3981 led2 = state;
3982 break;
3984 case 2:
3985 led3 = state;
3986 break;
3988 case 3:
3989 led4 = state;
3990 break;
3993 enable_leds( led1, led2, led3, led4 );
3996 void WiiRemote::enable_leds( t_CKBOOL l1, t_CKBOOL l2,
3997 t_CKBOOL l3, t_CKBOOL l4 )
3999 led1 = l1;
4000 led2 = l2;
4001 led3 = l3;
4002 led4 = l4;
4004 unsigned char cmd[] = { 0x11, 0x00 };
4005 if( force_feedback_enabled )
4006 cmd[1] |= 0x01;
4007 if( l1 )
4008 cmd[1] |= 0x10;
4009 if( l2 )
4010 cmd[1] |= 0x20;
4011 if( l3 )
4012 cmd[1] |= 0x40;
4013 if( l4 )
4014 cmd[1] |= 0x80;
4016 control_send( cmd, 2 );
4019 void WiiRemote::enable_speaker( t_CKBOOL enable )
4021 speaker_enabled = enable;
4023 if( speaker_enabled )
4025 // enable speaker
4026 unsigned char cmd[] = { 0x14, 0x04 };
4027 if( force_feedback_enabled )
4028 cmd[1] |= 0x01;
4029 control_send( cmd, 2 );
4031 // mute
4032 unsigned char mute[] = { 0x19, 0x04 };
4033 if( force_feedback_enabled )
4034 mute[1] |= 0x01;
4035 control_send( mute, 2 );
4037 unsigned char mem1[] = { 0x01 };
4038 write_memory( mem1, 1, 0x04a20009 );
4040 unsigned char mem2[] = { 0x08 };
4041 write_memory( mem2, 1, 0x04a20001 );
4043 // config
4044 unsigned char config[] = { 0x00, 0x00, 0x00, 0x0c, 0x40, 0x00, 0x00 };
4045 // description
4046 // byte 0: unknown
4047 // 1: unknown
4048 // 2: unknown
4049 // 3: sample rate -{ 0x0B = 4200 Hz
4050 // 0x0C = 3920 Hz
4051 // 0x0D = 3640 Hz
4052 // 0x0E = 3360 Hz
4053 // 0x0F = 3080 Hz
4054 // 4: speaker volume
4055 // 5: unknown
4056 // 6: unknown
4058 write_memory( config, 7, 0x04a20001 );
4060 unsigned char mem3[] = { 0x01 };
4061 write_memory( mem3, 1, 0x04a20008 );
4063 // unmute
4064 unsigned char unmute[] = { 0x19, 0x00 };
4065 if( force_feedback_enabled )
4066 unmute[1] |= 0x01;
4067 control_send( unmute, 2 );
4069 // initialize audio buffer, if necessary
4070 if( audio_buffer == NULL )
4072 audio_buffer = new CBufferSimple;
4073 audio_buffer->initialize( 20 * 5, sizeof( SAMPLE ) );
4076 // set up audio timer callback, if necessary
4077 if( timer == NULL )
4079 CFRunLoopTimerContext timerContext = { 0, this, NULL, NULL, NULL };
4080 timer = CFRunLoopTimerCreate( NULL, CFAbsoluteTimeGetCurrent(),
4081 1.0 / 3920.0, 0, 0,
4082 WiiRemote_send_audio_data,
4083 &timerContext );
4084 CFRunLoopAddTimer( rlHid, timer, kCFRunLoopChuckHidMode );
4089 void WiiRemote::send_audio_data()
4091 unsigned char cmd[] = { 0x18, ( 20 << 3 ),
4092 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
4093 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
4095 if( force_feedback_enabled )
4096 cmd[1] |= 0x01;
4098 control_send( cmd, 22 );
4101 void Bluetooth_inquiry_device_found( void * userRefCon,
4102 IOBluetoothDeviceInquiryRef inquiryRef,
4103 IOBluetoothDeviceRef deviceRef )
4105 CFStringRef device_name = IOBluetoothDeviceGetName( deviceRef );
4106 const BluetoothDeviceAddress * address = IOBluetoothDeviceGetAddress( deviceRef );
4108 if( device_name == NULL )
4109 return;
4111 // is device Nintendo Wii Remote controller?
4112 if( CFStringCompare( device_name, CFSTR( "Nintendo RVL-CNT-01" ), 0 ) == 0 )
4114 // has this already been detected?
4115 if( wr_addresses->find( *address ) == wr_addresses->end() )
4117 WiiRemote * wr;
4119 if( g_next_real_wiimote >= wiiremotes->size() )
4121 wr = new WiiRemote;
4122 wiiremotes->push_back( wr );
4125 else if( ( *wiiremotes )[g_next_real_wiimote] == NULL )
4127 wr = new WiiRemote;
4128 ( *wiiremotes )[g_next_real_wiimote] = wr;
4131 else
4133 wr = ( *wiiremotes )[g_next_real_wiimote];
4136 // set member data
4137 wr->device = deviceRef;
4138 memcpy( &wr->address, address, sizeof( BluetoothDeviceAddress ) );
4139 strncpy( wr->name, "Nintendo RVL-CNT-01", 256 );
4140 wr->num = g_next_real_wiimote;
4141 wr->type = CK_HID_DEV_WIIREMOTE;
4143 ( *wr_addresses )[wr->address] = wr;
4145 EM_log( CK_LOG_INFO, "hid: found Wii Remote Controller" );
4147 if( wr->refcount )
4148 wr->open();
4150 g_next_real_wiimote++;
4153 else if( !( *wr_addresses )[*address]->is_connected() )
4155 WiiRemote * wr = ( *wr_addresses )[*address];
4157 if( wr->refcount )
4158 wr->open();
4162 else
4164 EM_log( CK_LOG_INFO, "hid: found bluetooth device" );
4168 int WiiRemote_query();
4170 void Bluetooth_inquiry_complete( void * userRefCon,
4171 IOBluetoothDeviceInquiryRef inquiryRef,
4172 IOReturn error,
4173 Boolean aborted )
4175 g_bt_query_active = FALSE;
4177 IOBluetoothDeviceInquiryDelete( inquiryRef );
4179 t_CKBOOL do_query = FALSE;
4180 WiiRemote * wiiremote;
4182 for( int i = 0; i < wiiremotes->size(); i++ )
4184 wiiremote = ( *wiiremotes )[i];
4185 if( wiiremote->refcount > 0 && !wiiremote->is_connected() )
4187 do_query = TRUE;
4188 break;
4192 if( do_query )
4194 EM_log( CK_LOG_INFO, "hid: continuing bluetooth query" );
4195 WiiRemote_query();
4197 else
4198 EM_log( CK_LOG_INFO, "hid: ending bluetooth query" );
4201 int WiiRemote_query()
4203 EM_log( CK_LOG_INFO, "hid: performing bluetooth query" );
4204 if( g_bt_query_active )
4205 return 0;
4207 if( IOBluetoothLocalDeviceAvailable() == FALSE )
4209 EM_log( CK_LOG_WARNING, "hid: error: bluetooth unavailable" );
4210 return -1;
4213 IOBluetoothDeviceInquiryRef btDeviceInquiry = IOBluetoothDeviceInquiryCreateWithCallbackRefCon( NULL );
4215 if( btDeviceInquiry == NULL )
4217 EM_log( CK_LOG_WARNING, "hid: error: creating bluetooth device inquiry" );
4218 return -1;
4221 if( IOBluetoothDeviceInquirySetDeviceFoundCallback( btDeviceInquiry,
4222 Bluetooth_inquiry_device_found )
4223 != noErr )
4225 EM_log( CK_LOG_WARNING, "hid: error: setting bluetooth device inquiry callback" );
4226 return -1;
4229 if( IOBluetoothDeviceInquirySetCompleteCallback( btDeviceInquiry,
4230 Bluetooth_inquiry_complete )
4231 != noErr )
4233 EM_log( CK_LOG_WARNING, "hid: error: setting bluetooth device inquiry completion callback" );
4234 return -1;
4237 if( IOBluetoothDeviceInquirySetUpdateNewDeviceNames( btDeviceInquiry,
4238 TRUE )
4239 != noErr )
4241 EM_log( CK_LOG_WARNING, "hid: error: setting bluetooth device inquiry name update flag" );
4242 return -1;
4245 if( IOBluetoothDeviceInquirySetInquiryLength( btDeviceInquiry, 20 ) != noErr )
4247 EM_log( CK_LOG_WARNING, "hid: error: setting bluetooth inquiry length" );
4248 return -1;
4251 if( IOBluetoothDeviceInquiryStart( btDeviceInquiry ) != noErr )
4253 EM_log( CK_LOG_WARNING, "hid: error: starting bluetooth device inquiry" );
4254 return -1;
4257 g_bt_query_active = TRUE;
4259 return 0;
4262 /* -- WiiRemote inter-thread communication --
4263 Every IOBluetooth function needs to be called from the HID thread because none
4264 of it is thread safe, and it also requires a CFRunLoop to be running at some
4265 point. So, WiiRemote_open and _close, which are called from the VM thread, put
4266 open and close messages into a thread-safe circular buffer and then signal the
4267 hid thread to indicate that new info is available on the cbuf. The hid thread
4268 will then process any pending open/close messages in the cbuf.
4270 send messages now also are carried out in the hid thread through this mechanism.
4273 struct WiiRemoteOp
4275 enum
4277 open,
4278 close,
4279 send
4280 } op;
4282 int index;
4285 static CBufferSimple * WiiRemoteOp_cbuf = NULL;
4286 static CBufferSimple * WiiRemoteOp_msgbuf = NULL;
4288 static void WiiRemote_cfrl_callback( void * info )
4290 WiiRemoteOp wro;
4291 HidMsg msg;
4292 t_CKBOOL do_query = FALSE;
4294 while( WiiRemoteOp_cbuf->get( &wro, 1 ) )
4296 switch( wro.op )
4298 case WiiRemoteOp::open:
4299 // does it exist already?
4300 if( wro.index < wiiremotes->size() )
4301 // yes
4303 // is the device connected?
4304 if( !( *wiiremotes )[wro.index]->is_connected() )
4305 // no, so do a query
4306 do_query = TRUE;
4309 else
4310 // no, so create it
4312 while( wro.index > wiiremotes->size() )
4313 wiiremotes->push_back( NULL );
4314 wiiremotes->push_back( new WiiRemote );
4316 do_query = TRUE;
4319 ( *wiiremotes )[wro.index]->refcount++;
4321 break;
4323 case WiiRemoteOp::close:
4324 if( wro.index < wiiremotes->size() )
4326 if( --( *wiiremotes )[wro.index]->refcount == 0 )
4327 ( *wiiremotes )[wro.index]->close();
4330 break;
4332 case WiiRemoteOp::send:
4333 if( WiiRemoteOp_msgbuf->get( &msg, 1 ) )
4335 // double-check remote number for validity
4336 if( wro.index < 0 || wro.index >= wiiremotes->size() ||
4337 ( *wiiremotes )[wro.index] == NULL ||
4338 !( *wiiremotes )[wro.index]->is_connected() )
4340 // report failure
4341 HidInManager::push_message( msg );
4342 break;
4345 switch( msg.type )
4347 case CK_HID_LED:
4348 if( msg.eid < 0 || msg.eid >= 4 )
4350 // report failure
4351 HidInManager::push_message( msg );
4352 break;
4355 ( *wiiremotes )[wro.index]->set_led( msg.eid, msg.idata[0] );
4357 break;
4359 case CK_HID_FORCE_FEEDBACK:
4360 ( *wiiremotes )[wro.index]->enable_force_feedback( msg.idata[0] );
4362 break;
4366 break;
4370 if( do_query )
4371 WiiRemote_query();
4374 static void WiiRemote_signal()
4376 if( cfrlWiiRemoteSource && rlHid )
4378 CFRunLoopSourceSignal( cfrlWiiRemoteSource );
4379 CFRunLoopWakeUp( rlHid );
4383 #endif // __CK_HID_WIIREMOTE
4385 void WiiRemote_init()
4387 #ifdef __CK_HID_WIIREMOTE__
4388 Hid_init2();
4390 wiiremotes = new xvector< WiiRemote * >;
4391 wr_addresses = new map< BluetoothDeviceAddress, WiiRemote * >;
4393 WiiRemoteOp_cbuf = new CBufferSimple;
4394 WiiRemoteOp_cbuf->initialize( 20, sizeof( WiiRemoteOp ) );
4396 WiiRemoteOp_msgbuf = new CBufferSimple;
4397 WiiRemoteOp_msgbuf->initialize( 100, sizeof( HidMsg ) );
4398 #endif // __CK_HID_WIIREMOTE
4401 void WiiRemote_poll()
4405 void WiiRemote_quit()
4407 #ifdef __CK_HID_WIIREMOTE__
4408 for( xvector< WiiRemote * >::size_type i = 0; i < wiiremotes->size(); i++ )
4410 if( ( *wiiremotes )[i] )
4411 SAFE_DELETE( ( *wiiremotes )[i] );
4413 SAFE_DELETE( wiiremotes );
4414 SAFE_DELETE( wr_addresses );
4415 SAFE_DELETE( WiiRemoteOp_cbuf );
4417 Hid_quit2();
4418 #endif // __CK_HID_WIIREMOTE__
4421 int WiiRemote_count()
4423 #ifdef __CK_HID_WIIREMOTE__
4424 return wiiremotes->size();
4425 #else
4426 return 0;
4427 #endif // __CK_HID_WIIREMOTE__
4430 int WiiRemote_open( int wr )
4432 #ifdef __CK_HID_WIIREMOTE__
4433 WiiRemoteOp wro;
4434 wro.op = WiiRemoteOp::open;
4435 wro.index = wr;
4437 WiiRemoteOp_cbuf->put( &wro, 1 );
4439 WiiRemote_signal();
4441 return 0;
4442 #else
4443 return -1;
4444 #endif // __CK_HID_WIIREMOTE__
4447 int WiiRemote_close( int wr )
4449 #ifdef __CK_HID_WIIREMOTE__
4450 WiiRemoteOp wro;
4451 wro.op = WiiRemoteOp::close;
4452 wro.index = wr;
4454 WiiRemoteOp_cbuf->put( &wro, 1 );
4456 WiiRemote_signal();
4458 return 0;
4459 #else
4460 return -1;
4461 #endif // __CK_HID_WIIREMOTE__
4464 int WiiRemote_send( int wr, const HidMsg * msg )
4466 #ifdef __CK_HID_WIIREMOTE__
4467 wiiremotes->lock();
4469 if( wr < 0 || wr >= wiiremotes->size() )
4471 wiiremotes->unlock();
4472 return -1;
4475 WiiRemote * wiiremote = ( *wiiremotes )[wr];
4477 wiiremotes->unlock();
4479 if( wiiremote == NULL )
4480 return -1;
4483 wiiremote->lock();
4485 if( !wiiremote->is_connected() )
4487 wiiremote->unlock();
4488 return -1;
4491 wiiremote->unlock();
4493 WiiRemoteOp_msgbuf->put( ( void * )msg, 1 );
4495 WiiRemoteOp wro;
4496 wro.op = WiiRemoteOp::send;
4497 wro.index = wr;
4499 WiiRemoteOp_cbuf->put( &wro, 1 );
4501 WiiRemote_signal();
4503 return 0;
4504 #else
4505 return -1;
4506 #endif
4509 const char * WiiRemote_name( int wr )
4511 #ifdef __CK_HID_WIIREMOTE__
4512 return "Nintendo Wii Remote";
4513 #else
4514 return " ";
4515 #endif // __CK_HID_WIIREMOTE__
4518 #elif ( defined( __PLATFORM_WIN32__ ) || defined( __WINDOWS_PTHREAD__ ) ) && !defined( USE_RAWINPUT )
4519 /*****************************************************************************
4520 Windows general HID support
4521 *****************************************************************************/
4522 #pragma mark Windows general HID support
4524 #include <windows.h>
4525 #define DIRECTINPUT_VERSION 0x0500
4526 #include <dinput.h>
4528 /* for performance, we use device event notifications to tell us when a device
4529 has new data. However, serial devices don't seem to support event
4530 notification, so we have to fall back to the periodic sleeping and polling.
4531 g_wait_function is usually set to call WaitForSingleObject (wait for event
4532 notification), but devices for which SetEventNotification fails will replace
4533 it with a wrapper around usleep. */
4534 static HANDLE g_device_event = NULL;
4535 static void (*g_wait_function)() = NULL;
4537 static void Hid_wait_usleep()
4539 usleep( 10 );
4542 static void Hid_wait_event()
4544 WaitForSingleObject( g_device_event, INFINITE );
4547 void Hid_init()
4549 if( g_device_event != NULL )
4550 return;
4552 g_device_event = CreateEvent( NULL, FALSE, FALSE, NULL );
4553 if( g_device_event == NULL )
4554 EM_log( CK_LOG_SEVERE, "hid: error: unable to create event (win32 error %i)", GetLastError() );
4555 g_wait_function = Hid_wait_event;
4558 void Hid_poll()
4560 g_wait_function();
4561 Joystick_poll();
4562 Keyboard_poll();
4563 Mouse_poll();
4566 void Hid_quit()
4568 SetEvent( g_device_event );
4569 /*if( g_device_event != NULL )
4571 CloseHandle( g_device_event );
4572 g_device_event = NULL;
4579 /*****************************************************************************
4580 ge: Windows tiltsensor non-support
4581 *****************************************************************************/
4582 // designate new poll rate
4583 t_CKINT TiltSensor_setPollRate( t_CKINT usec )
4585 // sanity
4586 assert( usec >= 0 );
4587 // not supported
4588 fprintf( stderr, "TiltSensor - setPollRate is not (yet) supported on this platform...\n" );
4589 return -1;
4592 // query current poll rate
4593 t_CKINT TiltSensor_getPollRate( )
4595 // not supported
4596 fprintf( stderr, "TiltSensor - getPollRate is not (yet) supported on this platform...\n" );
4597 return -1;
4603 /*****************************************************************************
4604 Windows joystick support
4605 *****************************************************************************/
4606 #pragma mark Windows joystick support
4608 static LPDIRECTINPUT lpdi = NULL;
4610 struct win32_joystick
4612 win32_joystick()
4614 lpdiJoystick = NULL;
4615 refcount = 0;
4616 needs_close = FALSE;
4617 strncpy( name, "Joystick", MAX_PATH );
4618 memset( &last_state, 0, sizeof( last_state ) );
4619 memset( &caps, 0, sizeof( caps ) );
4620 caps.dwSize = sizeof( DIDEVCAPS );
4623 LPDIRECTINPUTDEVICE2 lpdiJoystick;
4624 DIJOYSTATE2 last_state;
4625 DIDEVCAPS caps;
4627 char name[MAX_PATH];
4629 t_CKUINT refcount;
4630 t_CKBOOL needs_close;
4634 const static LONG axis_min = -32767;
4635 const static LONG axis_max = 32767;
4638 static vector< win32_joystick * > * joysticks;
4640 static BOOL CALLBACK DIEnumJoystickProc( LPCDIDEVICEINSTANCE lpddi,
4641 LPVOID pvRef )
4643 GUID guid = lpddi->guidInstance;
4644 win32_joystick * js = new win32_joystick;
4646 EM_log( CK_LOG_INFO, "found %s", lpddi->tszProductName );
4648 strncpy( js->name, lpddi->tszProductName, MAX_PATH );
4650 if( lpdi->CreateDevice( guid, ( LPDIRECTINPUTDEVICE * )&js->lpdiJoystick, NULL ) != DI_OK )
4652 delete js;
4653 return DIENUM_CONTINUE;
4656 joysticks->push_back( js );
4658 return DIENUM_CONTINUE;
4661 static BOOL CALLBACK DIEnumJoystickObjectsProc( LPCDIDEVICEOBJECTINSTANCE lpdidoi,
4662 LPVOID pvRef )
4664 LPDIRECTINPUTDEVICE lpdiJoystick = ( LPDIRECTINPUTDEVICE ) pvRef;
4666 DIPROPRANGE diprg;
4668 // set axis minimum and maximum range
4669 diprg.diph.dwSize = sizeof(DIPROPRANGE);
4670 diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
4671 diprg.diph.dwHow = DIPH_BYID;
4672 diprg.diph.dwObj = lpdidoi->dwType;
4673 diprg.lMin = axis_min;
4674 diprg.lMax = axis_max;
4676 if( lpdiJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) != DI_OK )
4681 return DIENUM_CONTINUE;
4684 void Joystick_init()
4686 if( joysticks != NULL )
4687 return;
4689 EM_log( CK_LOG_INFO, "initializing joystick" );
4690 EM_pushlog();
4692 HINSTANCE hInstance = GetModuleHandle( NULL );
4694 if( lpdi == NULL )
4696 if( DirectInputCreate( hInstance, DIRECTINPUT_VERSION,
4697 &lpdi, NULL) != DI_OK )
4699 lpdi = NULL;
4700 EM_log( CK_LOG_SEVERE, "error: unable to initialize DirectInput, initialization failed" );
4701 EM_poplog();
4702 return;
4706 joysticks = new vector< win32_joystick * >;
4707 if( lpdi->EnumDevices( DIDEVTYPE_JOYSTICK, DIEnumJoystickProc,
4708 NULL, DIEDFL_ATTACHEDONLY ) != DI_OK )
4710 delete joysticks;
4711 joysticks = NULL;
4712 lpdi->Release();
4713 lpdi = NULL;
4714 EM_log( CK_LOG_SEVERE, "error: unable to enumerate devices, initialization failed" );
4715 EM_poplog();
4716 return;
4719 EM_poplog();
4722 t_CKINT Joystick_translate_POV( DWORD v )
4724 #define CK_POV_CENTER 0
4725 #define CK_POV_UP 1
4726 #define CK_POV_RIGHT 2
4727 #define CK_POV_DOWN 4
4728 #define CK_POV_LEFT 8
4730 #define CK_POV_UP_LBORDER 29250
4731 #define CK_POV_UP_RBORDER 6750
4732 #define CK_POV_RIGHT_LBORDER 2250
4733 #define CK_POV_RIGHT_RBORDER 15750
4734 #define CK_POV_DOWN_LBORDER 11250
4735 #define CK_POV_DOWN_RBORDER 24750
4736 #define CK_POV_LEFT_LBORDER 20250
4737 #define CK_POV_LEFT_RBORDER 33750
4739 t_CKINT r = 0;
4741 if( LOWORD(v) == 0xffff )
4742 return CK_POV_CENTER;
4744 if( v > CK_POV_UP_LBORDER || v < CK_POV_UP_RBORDER )
4745 r |= CK_POV_UP;
4746 if( v > CK_POV_RIGHT_LBORDER && v < CK_POV_RIGHT_RBORDER )
4747 r |= CK_POV_RIGHT;
4748 if( v > CK_POV_DOWN_LBORDER && v < CK_POV_DOWN_RBORDER )
4749 r |= CK_POV_DOWN;
4750 if( v > CK_POV_LEFT_LBORDER && v < CK_POV_LEFT_RBORDER )
4751 r |= CK_POV_LEFT;
4753 return r;
4756 void Joystick_poll()
4758 if( !joysticks )
4759 return;
4761 win32_joystick * joystick;
4762 HidMsg msg;
4763 vector< win32_joystick * >::size_type i, len = joysticks->size();
4764 int j;
4765 for( i = 0; i < len; i++ )
4767 joystick = joysticks->at( i );
4768 if( joystick->refcount )
4770 // TODO: convert this to buffered input, or maybe notifications
4771 DIJOYSTATE2 state;
4773 joystick->lpdiJoystick->Poll();
4775 if( joystick->lpdiJoystick->GetDeviceState( sizeof( DIJOYSTATE2 ), &state )
4776 != DI_OK )
4778 EM_log( CK_LOG_WARNING, "joystick: GetDeviceState failed for %s", joystick->name );
4779 continue;
4782 if( state.lX != joystick->last_state.lX )
4784 msg.clear();
4785 msg.device_num = i;
4786 msg.device_type = CK_HID_DEV_JOYSTICK;
4787 msg.eid = 0;
4788 msg.type = CK_HID_JOYSTICK_AXIS;
4789 msg.fdata[0] = ((float)state.lX)/((float)axis_max);
4790 HidInManager::push_message( msg );
4793 if( state.lY != joystick->last_state.lY )
4795 msg.clear();
4796 msg.device_num = i;
4797 msg.device_type = CK_HID_DEV_JOYSTICK;
4798 msg.eid = 1;
4799 msg.type = CK_HID_JOYSTICK_AXIS;
4800 msg.fdata[0] = ((float)state.lY)/((float)axis_max);
4801 HidInManager::push_message( msg );
4804 if( state.lZ != joystick->last_state.lZ )
4806 msg.clear();
4807 msg.device_num = i;
4808 msg.device_type = CK_HID_DEV_JOYSTICK;
4809 msg.eid = 2;
4810 msg.type = CK_HID_JOYSTICK_AXIS;
4811 msg.fdata[0] = ((float)state.lZ)/((float)axis_max);
4812 HidInManager::push_message( msg );
4815 if( state.lRx != joystick->last_state.lRx )
4817 msg.clear();
4818 msg.device_num = i;
4819 msg.device_type = CK_HID_DEV_JOYSTICK;
4820 msg.eid = 3;
4821 msg.type = CK_HID_JOYSTICK_AXIS;
4822 msg.fdata[0] = ((float)state.lRx)/((float)axis_max);
4823 HidInManager::push_message( msg );
4826 if( state.lRy != joystick->last_state.lRy )
4828 msg.clear();
4829 msg.device_num = i;
4830 msg.device_type = CK_HID_DEV_JOYSTICK;
4831 msg.eid = 4;
4832 msg.type = CK_HID_JOYSTICK_AXIS;
4833 msg.fdata[0] = ((float)state.lRy)/((float)axis_max);
4834 HidInManager::push_message( msg );
4837 if( state.lRz != joystick->last_state.lRz )
4839 msg.clear();
4840 msg.device_num = i;
4841 msg.device_type = CK_HID_DEV_JOYSTICK;
4842 msg.eid = 5;
4843 msg.type = CK_HID_JOYSTICK_AXIS;
4844 msg.fdata[0] = ((float)state.lRz)/((float)axis_max);
4845 HidInManager::push_message( msg );
4848 for( j = 0; j < 2; j++ )
4850 if( state.rglSlider[j] != joystick->last_state.rglSlider[j] )
4852 msg.clear();
4853 msg.device_num = i;
4854 msg.device_type = CK_HID_DEV_JOYSTICK;
4855 msg.eid = 5 + j;
4856 msg.type = CK_HID_JOYSTICK_AXIS;
4857 msg.fdata[0] = ((float)state.rglSlider[j])/((float)axis_max);
4858 HidInManager::push_message( msg );
4862 for( j = 0; j < joystick->caps.dwPOVs && j < 4; j++ )
4864 if( state.rgdwPOV[j] != joystick->last_state.rgdwPOV[j] )
4866 msg.clear();
4867 msg.device_num = i;
4868 msg.device_type = CK_HID_DEV_JOYSTICK;
4869 msg.eid = j;
4870 msg.type = CK_HID_JOYSTICK_HAT;
4871 msg.idata[0] = Joystick_translate_POV( state.rgdwPOV[j] );
4872 msg.fdata[0] = (t_CKFLOAT)state.rgdwPOV[j];
4873 HidInManager::push_message( msg );
4877 for( j = 0; j < joystick->caps.dwButtons && j < 128; j++ )
4879 if( ( state.rgbButtons[j] & 0x80 ) ^
4880 ( joystick->last_state.rgbButtons[j] & 0x80 ) )
4882 msg.clear();
4883 msg.device_num = i;
4884 msg.device_type = CK_HID_DEV_JOYSTICK;
4885 msg.eid = j;
4886 msg.type = ( state.rgbButtons[j] & 0x80 ) ? CK_HID_BUTTON_DOWN :
4887 CK_HID_BUTTON_UP;
4888 msg.idata[0] = ( state.rgbButtons[j] & 0x80 ) ? 1 : 0;
4889 HidInManager::push_message( msg );
4893 joystick->last_state = state;
4896 else if( joystick->needs_close )
4898 joystick->needs_close = FALSE;
4899 joystick->lpdiJoystick->Unacquire();
4900 joystick->lpdiJoystick->SetEventNotification( NULL );
4906 void Joystick_quit()
4908 if( joysticks )
4910 win32_joystick * joystick;
4911 vector< win32_joystick * >::size_type i, len = joysticks->size();
4912 for( i = 0; i < len; i++ )
4914 joystick = joysticks->at( i );
4916 if( joystick->refcount > 0 || joystick->needs_close)
4918 joystick->needs_close = FALSE;
4919 joystick->refcount = 0;
4920 joystick->lpdiJoystick->Unacquire();
4921 joystick->lpdiJoystick->SetEventNotification( NULL );
4924 joystick->lpdiJoystick->Release();
4925 delete joystick;
4928 delete joysticks;
4929 joysticks = NULL;
4932 if( lpdi )
4934 lpdi->Release();
4935 lpdi = NULL;
4939 int Joystick_count()
4941 if( !joysticks )
4942 return 0;
4943 return joysticks->size();
4946 int Joystick_open( int js )
4948 if( !joysticks || js < 0 || js >= joysticks->size() )
4949 return -1;
4951 win32_joystick * joystick = joysticks->at( js );
4953 if( joystick->refcount == 0 )
4955 if( joystick->lpdiJoystick->EnumObjects( DIEnumJoystickObjectsProc,
4956 joystick->lpdiJoystick,
4957 DIDFT_AXIS ) != DI_OK )
4959 return -1;
4962 if( joystick->lpdiJoystick->GetCapabilities( &joystick->caps ) != DI_OK )
4964 return -1;
4967 if( joystick->lpdiJoystick->SetDataFormat( &c_dfDIJoystick2 ) != DI_OK )
4969 return -1;
4972 if( joystick->lpdiJoystick->SetEventNotification( g_device_event ) != DI_OK )
4974 // fallback to sleep+poll mode
4975 g_wait_function = Hid_wait_usleep;
4976 SetEvent( g_device_event );
4979 if( joystick->lpdiJoystick->Acquire() != DI_OK )
4981 joystick->lpdiJoystick->SetEventNotification( NULL );
4982 return -1;
4986 joystick->refcount++;
4988 return 0;
4991 int Joystick_close( int js )
4993 if( !joysticks || js < 0 || js >= joysticks->size() )
4994 return -1;
4996 win32_joystick * joystick = joysticks->at( js );
4998 joystick->refcount--;
5000 if( joystick->refcount < 1 )
5001 joystick->needs_close = TRUE;
5003 return 0;
5006 const char * Joystick_name( int js )
5008 if( !joysticks || js < 0 || js >= joysticks->size() )
5009 return NULL;
5011 return joysticks->at( js )->name;
5014 /*****************************************************************************
5015 Windows keyboard support
5016 *****************************************************************************/
5017 #pragma mark Windows keyboards support
5019 #define DINPUT_KEYBUFFER_SIZE 256
5021 struct win32_keyboard
5023 win32_keyboard()
5025 lpdiKeyboard = NULL;
5026 refcount = 0;
5027 needs_close = FALSE;
5028 strncpy( name, "Keyboard", MAX_PATH );
5029 memset( &last_state, 0, DINPUT_KEYBUFFER_SIZE );
5032 LPDIRECTINPUTDEVICE2 lpdiKeyboard;
5033 char last_state[DINPUT_KEYBUFFER_SIZE];
5034 DIDEVCAPS caps;
5036 char name[MAX_PATH];
5038 t_CKUINT refcount;
5039 t_CKBOOL needs_close;
5043 static vector< win32_keyboard * > * keyboards;
5045 // table to translate DirectInput keybuffer offsets to ASCII and USB usages
5046 // first element is ASCII
5047 // second element is USB usage
5048 static unsigned short kb_translation_table[DINPUT_KEYBUFFER_SIZE];
5050 static void Keyboard_init_translation_table()
5052 memset( &kb_translation_table, 0, sizeof( kb_translation_table ) );
5054 kb_translation_table[DIK_0] = '0';
5055 kb_translation_table[DIK_0] |= 0x27 << 8;
5057 kb_translation_table[DIK_1] = '1';
5058 kb_translation_table[DIK_1] |= 0x1e << 8;
5060 kb_translation_table[DIK_2] = '2';
5061 kb_translation_table[DIK_2] |= 0x1f << 8;
5063 kb_translation_table[DIK_3] = '3';
5064 kb_translation_table[DIK_3] |= 0x20 << 8;
5066 kb_translation_table[DIK_4] = '4';
5067 kb_translation_table[DIK_4] |= 0x21 << 8;
5069 kb_translation_table[DIK_5] = '5';
5070 kb_translation_table[DIK_5] |= 0x22 << 8;
5072 kb_translation_table[DIK_6] = '6';
5073 kb_translation_table[DIK_6] |= 0x23 << 8;
5075 kb_translation_table[DIK_7] = '7';
5076 kb_translation_table[DIK_7] |= 0x24 << 8;
5078 kb_translation_table[DIK_8] = '8';
5079 kb_translation_table[DIK_8] |= 0x25 << 8;
5081 kb_translation_table[DIK_9] = '9';
5082 kb_translation_table[DIK_9] |= 0x26 << 8;
5084 kb_translation_table[DIK_A] = 'A';
5085 kb_translation_table[DIK_A] |= 0x04 << 8;
5087 kb_translation_table[DIK_B] = 'B';
5088 kb_translation_table[DIK_B] |= 0x05 << 8;
5090 kb_translation_table[DIK_C] = 'C';
5091 kb_translation_table[DIK_C] |= 0x06 << 8;
5093 kb_translation_table[DIK_D] = 'D';
5094 kb_translation_table[DIK_D] |= 0x07 << 8;
5096 kb_translation_table[DIK_E] = 'E';
5097 kb_translation_table[DIK_E] |= 0x08 << 8;
5099 kb_translation_table[DIK_F] = 'F';
5100 kb_translation_table[DIK_F] |= 0x09 << 8;
5102 kb_translation_table[DIK_G] = 'G';
5103 kb_translation_table[DIK_G] |= 0x0a << 8;
5105 kb_translation_table[DIK_H] = 'H';
5106 kb_translation_table[DIK_H] |= 0x0b << 8;
5108 kb_translation_table[DIK_I] = 'I';
5109 kb_translation_table[DIK_I] |= 0x0c << 8;
5111 kb_translation_table[DIK_J] = 'J';
5112 kb_translation_table[DIK_J] |= 0x0d << 8;
5114 kb_translation_table[DIK_K] = 'K';
5115 kb_translation_table[DIK_K] |= 0x0e << 8;
5117 kb_translation_table[DIK_L] = 'L';
5118 kb_translation_table[DIK_L] |= 0x0f << 8;
5120 kb_translation_table[DIK_M] = 'M';
5121 kb_translation_table[DIK_M] |= 0x10 << 8;
5123 kb_translation_table[DIK_N] = 'N';
5124 kb_translation_table[DIK_N] |= 0x11 << 8;
5126 kb_translation_table[DIK_O] = 'O';
5127 kb_translation_table[DIK_O] |= 0x12 << 8;
5129 kb_translation_table[DIK_P] = 'P';
5130 kb_translation_table[DIK_P] |= 0x13 << 8;
5132 kb_translation_table[DIK_Q] = 'Q';
5133 kb_translation_table[DIK_Q] |= 0x14 << 8;
5135 kb_translation_table[DIK_R] = 'R';
5136 kb_translation_table[DIK_R] |= 0x15 << 8;
5138 kb_translation_table[DIK_S] = 'S';
5139 kb_translation_table[DIK_S] |= 0x16 << 8;
5141 kb_translation_table[DIK_T] = 'T';
5142 kb_translation_table[DIK_T] |= 0x17 << 8;
5144 kb_translation_table[DIK_U] = 'U';
5145 kb_translation_table[DIK_U] |= 0x18 << 8;
5147 kb_translation_table[DIK_V] = 'V';
5148 kb_translation_table[DIK_V] |= 0x19 << 8;
5150 kb_translation_table[DIK_W] = 'W';
5151 kb_translation_table[DIK_W] |= 0x1a << 8;
5153 kb_translation_table[DIK_X] = 'X';
5154 kb_translation_table[DIK_X] |= 0x1b << 8;
5156 kb_translation_table[DIK_Y] = 'Y';
5157 kb_translation_table[DIK_Y] |= 0x1c << 8;
5159 kb_translation_table[DIK_Z] = 'Z';
5160 kb_translation_table[DIK_Z] |= 0x1d << 8;
5162 kb_translation_table[DIK_ADD] = '+';
5163 kb_translation_table[DIK_ADD] |= 0x57 << 8;
5165 kb_translation_table[DIK_APOSTROPHE] = '\'';
5166 kb_translation_table[DIK_APOSTROPHE] |= 0x34 << 8;
5168 kb_translation_table[DIK_APPS] = 0;
5169 kb_translation_table[DIK_APPS] |= 0x65 << 8;
5171 kb_translation_table[DIK_BACK] = '\b';
5172 kb_translation_table[DIK_BACK] |= 0x2a << 8;
5174 kb_translation_table[DIK_BACKSLASH] = '\\';
5175 kb_translation_table[DIK_BACKSLASH] |= 0x31 << 8;
5177 kb_translation_table[DIK_CAPITAL] = 0;
5178 kb_translation_table[DIK_CAPITAL] |= 0x39 << 8;
5180 kb_translation_table[DIK_COLON] = ':';
5181 kb_translation_table[DIK_COLON] |= 0x00 << 8;
5183 kb_translation_table[DIK_COMMA] = ',';
5184 kb_translation_table[DIK_COMMA] |= 0x36 << 8;
5186 kb_translation_table[DIK_DECIMAL] = '.';
5187 kb_translation_table[DIK_DECIMAL] |= 0x63 << 8;
5189 kb_translation_table[DIK_DELETE] = 0x7f;
5190 kb_translation_table[DIK_DELETE] |= 0x4c << 8;
5192 kb_translation_table[DIK_DIVIDE] = '/';
5193 kb_translation_table[DIK_DIVIDE] |= 0x54 << 8;
5195 kb_translation_table[DIK_DOWN] = 0;
5196 kb_translation_table[DIK_DOWN] |= 0x51 << 8;
5198 kb_translation_table[DIK_END] = 0;
5199 kb_translation_table[DIK_END] |= 0x4d << 8;
5201 kb_translation_table[DIK_EQUALS] = '=';
5202 kb_translation_table[DIK_EQUALS] |= 0x2e << 8;
5204 kb_translation_table[DIK_ESCAPE] = 0x1b;
5205 kb_translation_table[DIK_ESCAPE] |= 0x29 << 8;
5207 kb_translation_table[DIK_F1] = 0;
5208 kb_translation_table[DIK_F1] |= 0x3a << 8;
5210 kb_translation_table[DIK_F2] = 0;
5211 kb_translation_table[DIK_F2] |= 0x3b << 8;
5213 kb_translation_table[DIK_F3] = 0;
5214 kb_translation_table[DIK_F3] |= 0x3c << 8;
5216 kb_translation_table[DIK_F4] = 0;
5217 kb_translation_table[DIK_F4] |= 0x3d << 8;
5219 kb_translation_table[DIK_F5] = 0;
5220 kb_translation_table[DIK_F5] |= 0x3e << 8;
5222 kb_translation_table[DIK_F6] = 0;
5223 kb_translation_table[DIK_F6] |= 0x3f << 8;
5225 kb_translation_table[DIK_F7] = 0;
5226 kb_translation_table[DIK_F7] |= 0x40 << 8;
5228 kb_translation_table[DIK_F8] = 0;
5229 kb_translation_table[DIK_F8] |= 0x41 << 8;
5231 kb_translation_table[DIK_F9] = 0;
5232 kb_translation_table[DIK_F9] |= 0x42 << 8;
5234 kb_translation_table[DIK_F10] = 0;
5235 kb_translation_table[DIK_F10] |= 0x43 << 8;
5237 kb_translation_table[DIK_F11] = 0;
5238 kb_translation_table[DIK_F11] |= 0x44 << 8;
5240 kb_translation_table[DIK_F12] = 0;
5241 kb_translation_table[DIK_F12] |= 0x45 << 8;
5243 kb_translation_table[DIK_F13] = 0;
5244 kb_translation_table[DIK_F13] |= 0x68 << 8;
5246 kb_translation_table[DIK_F14] = 0;
5247 kb_translation_table[DIK_F14] |= 0x69 << 8;
5249 kb_translation_table[DIK_F15] = 0;
5250 kb_translation_table[DIK_F15] |= 0x6a << 8;
5252 kb_translation_table[DIK_GRAVE] = '`';
5253 kb_translation_table[DIK_GRAVE] |= 0x35 << 8;
5255 kb_translation_table[DIK_HOME] = 0;
5256 kb_translation_table[DIK_HOME] |= 0x4a << 8;
5258 kb_translation_table[DIK_INSERT] = 0;
5259 kb_translation_table[DIK_INSERT] |= 0x49 << 8;
5261 kb_translation_table[DIK_LBRACKET] = '[';
5262 kb_translation_table[DIK_LBRACKET] |= 0x2f << 8;
5264 kb_translation_table[DIK_LCONTROL] = 0;
5265 kb_translation_table[DIK_LCONTROL] |= 0xe0 << 8;
5267 kb_translation_table[DIK_LEFT] = 0;
5268 kb_translation_table[DIK_LEFT] |= 0x50 << 8;
5270 kb_translation_table[DIK_LMENU] = 0;
5271 kb_translation_table[DIK_LMENU] |= 0xe2 << 8;
5273 kb_translation_table[DIK_LSHIFT] = 0;
5274 kb_translation_table[DIK_LSHIFT] |= 0xe1 << 8;
5276 kb_translation_table[DIK_LWIN] = 0;
5277 kb_translation_table[DIK_LWIN] |= 0xe3 << 8;
5279 kb_translation_table[DIK_MINUS] = '-';
5280 kb_translation_table[DIK_MINUS] |= 0x2d << 8;
5282 kb_translation_table[DIK_MULTIPLY] = '*';
5283 kb_translation_table[DIK_MULTIPLY] |= 0x55 << 8;
5285 // kb_translation_table[DIK_MUTE] = 0;
5286 // kb_translation_table[DIK_MUTE] |= 0x7f;
5288 kb_translation_table[DIK_NEXT] = 0;
5289 kb_translation_table[DIK_NEXT] |= 0x4e << 8;
5291 kb_translation_table[DIK_NUMLOCK] = 0;
5292 kb_translation_table[DIK_NUMLOCK] |= 0x53 << 8;
5294 kb_translation_table[DIK_NUMPAD0] = '0';
5295 kb_translation_table[DIK_NUMPAD0] |= 0x62 << 8;
5297 kb_translation_table[DIK_NUMPAD1] = '1';
5298 kb_translation_table[DIK_NUMPAD1] |= 0x59 << 8;
5300 kb_translation_table[DIK_NUMPAD2] = '2';
5301 kb_translation_table[DIK_NUMPAD2] |= 0x5a << 8;
5303 kb_translation_table[DIK_NUMPAD3] = '3';
5304 kb_translation_table[DIK_NUMPAD3] |= 0x5b << 8;
5306 kb_translation_table[DIK_NUMPAD4] = '4';
5307 kb_translation_table[DIK_NUMPAD4] |= 0x5c << 8;
5309 kb_translation_table[DIK_NUMPAD5] = '5';
5310 kb_translation_table[DIK_NUMPAD5] |= 0x5d << 8;
5312 kb_translation_table[DIK_NUMPAD6] = '6';
5313 kb_translation_table[DIK_NUMPAD6] |= 0x5e << 8;
5315 kb_translation_table[DIK_NUMPAD7] = '7';
5316 kb_translation_table[DIK_NUMPAD7] |= 0x5f << 8;
5318 kb_translation_table[DIK_NUMPAD8] = '8';
5319 kb_translation_table[DIK_NUMPAD8] |= 0x60 << 8;
5321 kb_translation_table[DIK_NUMPAD9] = '9';
5322 kb_translation_table[DIK_NUMPAD9] |= 0x61 << 8;
5324 kb_translation_table[DIK_NUMPADCOMMA] = ',';
5325 kb_translation_table[DIK_NUMPADCOMMA] |= 0x85 << 8;
5327 kb_translation_table[DIK_NUMPADENTER] = '\n';
5328 kb_translation_table[DIK_NUMPADENTER] |= 0x58 << 8;
5330 kb_translation_table[DIK_NUMPADEQUALS] = '=';
5331 kb_translation_table[DIK_NUMPADEQUALS] |= 0x67 << 8;
5333 kb_translation_table[DIK_PAUSE] = 0;
5334 kb_translation_table[DIK_PAUSE] |= 0x48 << 8;
5336 kb_translation_table[DIK_PERIOD] = '.';
5337 kb_translation_table[DIK_PERIOD] |= 0x37 << 8;
5339 kb_translation_table[DIK_POWER] = 0;
5340 kb_translation_table[DIK_POWER] |= 0x66 << 8;
5342 kb_translation_table[DIK_PRIOR] = 0;
5343 kb_translation_table[DIK_PRIOR] |= 0x4b << 8;
5345 kb_translation_table[DIK_RBRACKET] = ']';
5346 kb_translation_table[DIK_RBRACKET] |= 0x30 << 8;
5348 kb_translation_table[DIK_RCONTROL] = 0;
5349 kb_translation_table[DIK_RCONTROL] |= 0xe4 << 8;
5351 kb_translation_table[DIK_RETURN] = '\n';
5352 kb_translation_table[DIK_RETURN] |= 0x28 << 8;
5354 kb_translation_table[DIK_RIGHT] = 0;
5355 kb_translation_table[DIK_RIGHT] |= 0x4f << 8;
5357 kb_translation_table[DIK_RMENU] = 0;
5358 kb_translation_table[DIK_RMENU] |= 0xe6 << 8;
5360 kb_translation_table[DIK_RSHIFT] = 0;
5361 kb_translation_table[DIK_RSHIFT] |= 0xe5 << 8;
5363 kb_translation_table[DIK_RWIN] = 0;
5364 kb_translation_table[DIK_RWIN] |= 0xe7 << 8;
5366 kb_translation_table[DIK_SCROLL] = 0;
5367 kb_translation_table[DIK_SCROLL] |= 0x47 << 8;
5369 kb_translation_table[DIK_SEMICOLON] = ';';
5370 kb_translation_table[DIK_SEMICOLON] |= 0x33 << 8;
5372 kb_translation_table[DIK_SLASH] = '/';
5373 kb_translation_table[DIK_SLASH] |= 0x38 << 8;
5375 kb_translation_table[DIK_SPACE] = ' ';
5376 kb_translation_table[DIK_SPACE] |= 0x2c << 8;
5378 kb_translation_table[DIK_STOP] = 0;
5379 kb_translation_table[DIK_STOP] |= 0x78 << 8;
5381 kb_translation_table[DIK_SUBTRACT] = '-';
5382 kb_translation_table[DIK_SUBTRACT] |= 0x56 << 8;
5384 kb_translation_table[DIK_SYSRQ] = 0;
5385 kb_translation_table[DIK_SYSRQ] |= 0x46 << 8;
5387 kb_translation_table[DIK_TAB] = '\t';
5388 kb_translation_table[DIK_TAB] |= 0x2b << 8;
5390 kb_translation_table[DIK_UP] = 0;
5391 kb_translation_table[DIK_UP] |= 0x52 << 8;
5393 // kb_translation_table[DIK_VOLUMEDOWN] = 0;
5394 // kb_translation_table[DIK_VOLUMEDOWN] |= 0x81;
5396 // kb_translation_table[DIK_VOLUMEUP] = 0;
5397 // kb_translation_table[DIK_VOLUMEUP] |= 0x80;
5400 static void Keyboard_dikey_to_ascii_and_usb( unsigned char dikey,
5401 long & ascii,
5402 long & usb )
5404 unsigned short tr = kb_translation_table[dikey];
5406 ascii = tr & 0xff;
5407 usb = ( tr >> 8 ) & 0xff;
5410 static BOOL CALLBACK DIEnumKeyboardProc( LPCDIDEVICEINSTANCE lpddi,
5411 LPVOID pvRef )
5413 GUID guid = lpddi->guidInstance;
5414 win32_keyboard * keyboard = new win32_keyboard;
5416 EM_log( CK_LOG_INFO, "found %s", lpddi->tszProductName );
5418 strncpy( keyboard->name, lpddi->tszProductName, MAX_PATH );
5420 if( lpdi->CreateDevice( guid,
5421 ( LPDIRECTINPUTDEVICE * ) &keyboard->lpdiKeyboard,
5422 NULL ) != DI_OK )
5424 delete keyboard;
5425 EM_log( CK_LOG_WARNING, "error: unable to initialize device %s",
5426 lpddi->tszProductName );
5427 return DIENUM_CONTINUE;
5430 keyboards->push_back( keyboard );
5432 return DIENUM_CONTINUE;
5435 void Keyboard_init()
5437 if( keyboards != NULL )
5438 return;
5440 EM_log( CK_LOG_INFO, "initializing keyboard" );
5441 EM_pushlog();
5443 HINSTANCE hInstance = GetModuleHandle( NULL );
5445 if( lpdi == NULL )
5447 if( DirectInputCreate( hInstance, DIRECTINPUT_VERSION,
5448 &lpdi, NULL) != DI_OK )
5450 lpdi = NULL;
5451 EM_log( CK_LOG_SEVERE, "error: unable to initialize DirectInput, initialization failed" );
5452 EM_poplog();
5453 return;
5457 keyboards = new vector< win32_keyboard * >;
5458 if( lpdi->EnumDevices( DIDEVTYPE_KEYBOARD, DIEnumKeyboardProc,
5459 NULL, DIEDFL_ATTACHEDONLY ) != DI_OK )
5461 delete keyboards;
5462 keyboards = NULL;
5463 lpdi->Release();
5464 lpdi = NULL;
5465 EM_log( CK_LOG_SEVERE, "error: unable to enumerate devices, initialization failed" );
5466 EM_poplog();
5467 return;
5470 Keyboard_init_translation_table();
5472 EM_poplog();
5475 void Keyboard_poll()
5477 if( !keyboards )
5478 return;
5480 win32_keyboard * keyboard;
5481 HidMsg msg;
5482 vector< win32_keyboard * >::size_type i, len = keyboards->size();
5483 for( i = 0; i < len; i++ )
5485 keyboard = keyboards->at( i );
5486 if( keyboard->refcount )
5488 // TODO: convert this to buffered input, or maybe notifications
5489 char state[DINPUT_KEYBUFFER_SIZE];
5491 keyboard->lpdiKeyboard->Poll();
5493 if( keyboard->lpdiKeyboard->GetDeviceState( DINPUT_KEYBUFFER_SIZE, state )
5494 != DI_OK )
5496 EM_log( CK_LOG_WARNING, "keyboard: GetDeviceState failed for %s",
5497 keyboard->name );
5498 continue;
5501 for( int j = 0; j < DINPUT_KEYBUFFER_SIZE; j++ )
5503 if( ( state[j] & 0x80 ) ^ ( keyboard->last_state[j] & 0x80 ) )
5505 msg.clear();
5506 msg.device_num = i;
5507 msg.device_type = CK_HID_DEV_KEYBOARD;
5508 msg.type = ( state[j] & 0x80 ) ? CK_HID_BUTTON_DOWN :
5509 CK_HID_BUTTON_UP;
5510 msg.eid = j;
5511 msg.idata[0] = ( state[j] & 0x80 ) ? 1 : 0;
5512 Keyboard_dikey_to_ascii_and_usb( j, msg.idata[2], msg.idata[1] );
5513 HidInManager::push_message( msg );
5517 memcpy( keyboard->last_state, state, DINPUT_KEYBUFFER_SIZE );
5520 else if( keyboard->needs_close )
5522 keyboard->needs_close = FALSE;
5523 keyboard->lpdiKeyboard->Unacquire();
5524 keyboard->lpdiKeyboard->SetEventNotification( NULL );
5529 void Keyboard_quit()
5531 if( keyboards )
5533 win32_keyboard * keyboard;
5534 vector< win32_keyboard * >::size_type i, len = keyboards->size();
5535 for( i = 0; i < len; i++ )
5537 keyboard = keyboards->at( i );
5539 if( keyboard->refcount > 0 || keyboard->needs_close)
5541 keyboard->needs_close = FALSE;
5542 keyboard->refcount = 0;
5543 keyboard->lpdiKeyboard->Unacquire();
5544 keyboard->lpdiKeyboard->SetEventNotification( NULL );
5547 keyboard->lpdiKeyboard->Release();
5548 delete keyboard;
5551 delete keyboards;
5552 keyboards = NULL;
5555 if( lpdi )
5557 lpdi->Release();
5558 lpdi = NULL;
5562 int Keyboard_count()
5564 if( !keyboards )
5565 return 0;
5566 return keyboards->size();
5569 int Keyboard_open( int k )
5571 if( !keyboards || k < 0 || k >= keyboards->size() )
5572 return -1;
5574 win32_keyboard * keyboard = keyboards->at( k );
5576 if( keyboard->refcount == 0 )
5578 if( keyboard->lpdiKeyboard->SetDataFormat( &c_dfDIKeyboard ) != DI_OK )
5580 return -1;
5583 if( keyboard->lpdiKeyboard->SetEventNotification( g_device_event ) != DI_OK )
5585 // fallback to sleep+poll mode
5586 g_wait_function = Hid_wait_usleep;
5587 SetEvent( g_device_event );
5590 if( keyboard->lpdiKeyboard->Acquire() != DI_OK )
5592 keyboard->lpdiKeyboard->SetEventNotification( NULL );
5593 return -1;
5597 keyboard->refcount++;
5599 return 0;
5602 int Keyboard_close( int k )
5604 if( !keyboards || k < 0 || k >= keyboards->size() )
5605 return -1;
5607 win32_keyboard * keyboard = keyboards->at( k );
5609 keyboard->refcount--;
5611 if( keyboard->refcount < 1 )
5612 keyboard->needs_close = TRUE;
5614 return 0;
5617 const char * Keyboard_name( int kb )
5619 if( !keyboards || kb < 0 || kb >= keyboards->size() )
5620 return NULL;
5622 return keyboards->at( kb )->name;
5625 /*****************************************************************************
5626 Windows mouse support
5627 *****************************************************************************/
5628 #pragma mark Windows mouse support
5630 struct win32_mouse
5632 win32_mouse()
5634 refcount = 0;
5635 needs_close = FALSE;
5637 lpdiMouse = NULL;
5638 memset( &last_state, 0, sizeof( last_state ) );
5641 LPDIRECTINPUTDEVICE2 lpdiMouse;
5642 DIMOUSESTATE last_state;
5644 char name[MAX_PATH];
5646 t_CKUINT refcount;
5647 t_CKBOOL needs_close;
5650 static vector< win32_mouse * > * mice;
5652 static BOOL CALLBACK DIEnumMouseProc( LPCDIDEVICEINSTANCE lpddi,
5653 LPVOID pvRef )
5655 GUID guid = lpddi->guidInstance;
5656 win32_mouse * mouse = new win32_mouse;
5658 EM_log( CK_LOG_INFO, "found %s", lpddi->tszProductName );
5660 strncpy( mouse->name, lpddi->tszProductName, MAX_PATH );
5662 if( lpdi->CreateDevice( guid, ( LPDIRECTINPUTDEVICE * ) &mouse->lpdiMouse,
5663 NULL ) != DI_OK )
5665 delete mouse;
5666 return DIENUM_CONTINUE;
5669 mice->push_back( mouse );
5671 return DIENUM_CONTINUE;
5674 void Mouse_init()
5676 if( mice != NULL )
5677 return;
5679 EM_log( CK_LOG_INFO, "initializing mouse" );
5680 EM_pushlog();
5682 HINSTANCE hInstance = GetModuleHandle( NULL );
5684 if( lpdi == NULL )
5686 if( DirectInputCreate( hInstance, DIRECTINPUT_VERSION,
5687 &lpdi, NULL) != DI_OK )
5689 lpdi = NULL;
5690 EM_poplog();
5691 return;
5695 mice = new vector< win32_mouse * >;
5696 if( lpdi->EnumDevices( DIDEVTYPE_MOUSE, DIEnumMouseProc,
5697 NULL, DIEDFL_ATTACHEDONLY ) != DI_OK )
5699 delete mice;
5700 mice = NULL;
5701 lpdi->Release();
5702 lpdi = NULL;
5703 EM_poplog();
5704 return;
5707 EM_poplog();
5710 void Mouse_poll()
5712 if( !mice )
5713 return;
5715 win32_mouse * mouse;
5716 HidMsg msg;
5717 vector< win32_mouse * >::size_type i, len = mice->size();
5718 for( i = 0; i < len; i++ )
5720 mouse = mice->at( i );
5721 if( mouse->refcount )
5723 // TODO: convert this to buffered input, or maybe notifications
5724 DIMOUSESTATE state;
5726 mouse->lpdiMouse->Poll();
5728 if( mouse->lpdiMouse->GetDeviceState( sizeof( DIMOUSESTATE ), &state )
5729 != DI_OK )
5731 EM_log( CK_LOG_WARNING, "mouse: GetDeviceState failed for %s", mouse->name );
5732 continue;
5735 if( state.lX != 0 || state.lY != 0 )
5737 msg.clear();
5738 msg.device_num = i;
5739 msg.device_type = CK_HID_DEV_MOUSE;
5740 msg.type = CK_HID_MOUSE_MOTION;
5741 msg.eid = 0;
5742 msg.idata[0] = state.lX;
5743 msg.idata[1] = state.lY;
5744 HidInManager::push_message( msg );
5747 if( state.lZ != 0 )
5749 msg.clear();
5750 msg.device_num = i;
5751 msg.device_type = CK_HID_DEV_MOUSE;
5752 msg.type = CK_HID_MOUSE_WHEEL;
5753 msg.eid = 0;
5754 msg.idata[0] = 0;
5755 msg.idata[1] = state.lZ;
5756 HidInManager::push_message( msg );
5759 for( int j = 0; j < 4; j++ )
5761 if( ( state.rgbButtons[j] & 0x80 ) ^
5762 ( mouse->last_state.rgbButtons[j] & 0x80 ) )
5764 msg.clear();
5765 msg.device_num = i;
5766 msg.device_type = CK_HID_DEV_MOUSE;
5767 msg.type = ( state.rgbButtons[j] & 0x80 ) ? CK_HID_BUTTON_DOWN :
5768 CK_HID_BUTTON_UP;
5769 msg.eid = j;
5770 msg.idata[0] = ( state.rgbButtons[j] & 0x80 ) ? 1 : 0;
5771 HidInManager::push_message( msg );
5775 mouse->last_state = state;
5778 else if( mouse->needs_close )
5780 mouse->needs_close = FALSE;
5781 mouse->lpdiMouse->Unacquire();
5782 mouse->lpdiMouse->SetEventNotification( NULL );
5787 void Mouse_quit()
5789 if( mice )
5791 win32_mouse * mouse;
5792 vector< win32_mouse * >::size_type i, len = mice->size();
5793 for( i = 0; i < len; i++ )
5795 mouse = mice->at( i );
5797 if( mouse->refcount > 0 || mouse->needs_close)
5799 mouse->needs_close = FALSE;
5800 mouse->refcount = 0;
5801 mouse->lpdiMouse->Unacquire();
5802 mouse->lpdiMouse->SetEventNotification( NULL );
5805 mouse->lpdiMouse->Release();
5806 delete mouse;
5809 delete mice;
5810 mice = NULL;
5813 if( lpdi )
5815 lpdi->Release();
5816 lpdi = NULL;
5820 int Mouse_count()
5822 if( !mice )
5823 return 0;
5824 return mice->size();
5827 int Mouse_open( int m )
5829 if( !mice || m < 0 || m >= mice->size() )
5830 return -1;
5832 win32_mouse * mouse = mice->at( m );
5834 if( mouse->refcount == 0 )
5836 if( mouse->lpdiMouse->SetDataFormat( &c_dfDIMouse ) != DI_OK )
5838 return -1;
5841 if( mouse->lpdiMouse->SetEventNotification( g_device_event ) != DI_OK )
5843 // fallback to sleep+poll mode
5844 g_wait_function = Hid_wait_usleep;
5845 SetEvent( g_device_event );
5848 if( mouse->lpdiMouse->Acquire() != DI_OK )
5850 mouse->lpdiMouse->SetEventNotification( g_device_event );
5851 return -1;
5855 mouse->refcount++;
5857 return 0;
5860 int Mouse_close( int m )
5862 if( !mice || m < 0 || m >= mice->size() )
5863 return -1;
5865 win32_mouse * mouse = mice->at( m );
5867 mouse->refcount--;
5869 if( mouse->refcount < 1 )
5870 mouse->needs_close = TRUE; // let the polling thread take care of it
5872 return 0;
5875 const char * Mouse_name( int m )
5877 if( !mice || m < 0 || m >= mice->size() )
5878 return NULL;
5880 return mice->at( m )->name;
5883 #elif defined( __PLATFORM_WIN32__ ) || defined( __WINDOWS_PTHREAD__ ) && defined( USE_RAWINPUT )
5885 void Joystick_init()
5890 void Joystick_poll()
5895 void Joystick_quit()
5900 int Joystick_count()
5902 return 0;
5905 int Joystick_open( int js )
5907 return -1;
5910 int Joystick_close( int js )
5912 return -1;
5915 void Mouse_init()
5920 void Mouse_poll()
5925 void Mouse_quit()
5930 int Mouse_count()
5932 return 0;
5935 int Mouse_open( int js )
5937 return -1;
5940 int Mouse_close( int js )
5942 return -1;
5945 void Keyboard_init()
5950 void Keyboard_poll()
5955 void Keyboard_quit()
5960 int Keyboard_count()
5962 return 0;
5965 int Keyboard_open( int js )
5967 return -1;
5970 int Keyboard_close( int js )
5972 return -1;
5977 #elif defined( __LINUX_ALSA__ ) || defined( __LINUX_OSS__ ) || defined( __LINUX_JACK__ )
5978 /*****************************************************************************
5979 Linux general HID support
5980 *****************************************************************************/
5981 #pragma mark Linux general HID support
5983 #include <sys/types.h>
5984 #include <sys/stat.h>
5985 #include <unistd.h>
5986 #include <dirent.h>
5987 #include <linux/unistd.h>
5988 #include <string.h>
5989 #include <fcntl.h>
5990 #include <sys/poll.h>
5991 #include <sys/ioctl.h>
5992 #include <errno.h>
5993 #include <linux/joystick.h>
5994 #include <linux/input.h>
5996 #define CK_HID_DIR ("/dev/input")
5997 #define CK_HID_MOUSEFILE ("mouse%d")
5998 #define CK_HID_JOYSTICKFILE ("js%d")
5999 #define CK_HID_EVDEVFILE ("event%d")
6000 #define CK_HID_STRBUFSIZE (1024)
6001 #define CK_HID_NAMESIZE (128)
6003 class linux_device
6005 public:
6006 linux_device()
6008 fd = -1;
6009 num = -1;
6010 refcount = 0;
6011 pollfds_i = 0;
6012 filename[0] = '\0';
6013 needs_open = needs_close = FALSE;
6014 strncpy( name, "(name unknown)", CK_HID_NAMESIZE );
6017 virtual void callback() = 0;
6019 int fd; // file descriptor
6020 t_CKINT num; // index in the respective device vector {joysticks, mice, keyboards}
6022 t_CKUINT refcount;
6024 size_t pollfds_i;
6026 t_CKBOOL needs_open;
6027 t_CKBOOL needs_close;
6029 char filename[CK_HID_STRBUFSIZE];
6030 char name[CK_HID_NAMESIZE];
6033 class linux_joystick : public linux_device
6035 public:
6036 linux_joystick() : linux_device()
6038 js_num = -1;
6041 virtual void callback()
6043 js_event event;
6044 HidMsg msg;
6045 ssize_t len;
6047 while( ( len = read( fd, &event, sizeof( event ) ) ) > 0 )
6049 if( len < sizeof( event ) )
6051 EM_log( CK_LOG_WARNING, "joystick: read event from %s smaller than expected, ignoring", name );
6052 continue;
6055 if( event.type == JS_EVENT_INIT )
6056 continue;
6058 msg.device_type = CK_HID_DEV_JOYSTICK;
6059 msg.device_num = num;
6060 msg.eid = event.number;
6062 switch( event.type )
6064 case JS_EVENT_BUTTON:
6065 msg.type = event.value ? CK_HID_BUTTON_DOWN :
6066 CK_HID_BUTTON_UP;
6067 msg.idata[0] = event.value;
6068 break;
6069 case JS_EVENT_AXIS:
6070 msg.type = CK_HID_JOYSTICK_AXIS;
6071 msg.fdata[0] = ((t_CKFLOAT)event.value) / ((t_CKFLOAT) SHRT_MAX);
6072 break;
6073 default:
6074 EM_log( CK_LOG_WARNING, "joystick: unknown event type from %s, ignoring", name );
6075 continue;
6078 HidInManager::push_message( msg );
6082 int js_num; // /dev/input/js# <-- the #
6085 #define __LITTLE_ENDIAN__
6086 struct ps2_mouse_event
6088 #ifdef __BIG_ENDIAN__
6089 unsigned unused:2;
6090 unsigned y_reverse_motion:1;
6091 unsigned x_reverse_motion:1;
6092 unsigned always1:1;
6093 unsigned button3:1;
6094 unsigned button2:1;
6095 unsigned button1:1;
6096 signed dx:8;
6097 signed dy:8;
6098 #elif defined( __LITTLE_ENDIAN__ )
6099 unsigned button1:1;
6100 unsigned button2:1;
6101 unsigned button3:1;
6102 unsigned always1:1;
6103 unsigned x_reverse_motion:1;
6104 unsigned y_reverse_motion:1;
6105 unsigned unused:2;
6106 signed dx:8;
6107 signed dy:8;
6108 #else
6109 #error unknown endian mode (both __LITTLE_ENDIAN__ and __BIG_ENDIAN__ undefined)
6110 #endif
6111 } __attribute__ ((__packed__));
6113 class linux_mouse : public linux_device
6115 public:
6116 linux_mouse() : linux_device()
6118 m_num = -1;
6119 memset( &last_event, 0, sizeof( last_event ) );
6122 virtual void callback()
6124 //ps2_mouse_event event;
6125 input_event event;
6126 HidMsg msg;
6127 ssize_t len;
6129 while( ( len = read( fd, &event, sizeof( event ) ) ) > 0 )
6131 if( len < sizeof( event ) )
6133 EM_log( CK_LOG_WARNING, "mouse: read event from mouse %i smaller than expected (%i), ignoring", num, len );
6134 continue;
6137 switch( event.type )
6139 case EV_KEY:
6140 if( event.code & BTN_MOUSE )
6142 msg.clear();
6143 msg.device_type = CK_HID_DEV_MOUSE;
6144 msg.device_num = num;
6145 msg.eid = event.code - BTN_MOUSE;
6146 msg.type = event.value ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
6147 msg.idata[0] = event.value;
6148 HidInManager::push_message( msg );
6151 break;
6153 case EV_REL:
6154 msg.clear();
6155 msg.device_type = CK_HID_DEV_MOUSE;
6156 msg.device_num = num;
6158 switch( event.code )
6160 case REL_X:
6161 msg.type = CK_HID_MOUSE_MOTION;
6162 msg.idata[0] = event.value;
6163 msg.idata[1] = 0;
6164 break;
6166 case REL_Y:
6167 msg.type = CK_HID_MOUSE_MOTION;
6168 msg.idata[0] = 0;
6169 msg.idata[1] = event.value;
6170 break;
6172 case REL_HWHEEL:
6173 msg.type = CK_HID_MOUSE_WHEEL;
6174 msg.idata[0] = event.value;
6175 msg.idata[1] = 0;
6176 break;
6178 case REL_Z:
6179 case REL_WHEEL:
6180 msg.type = CK_HID_MOUSE_WHEEL;
6181 msg.idata[0] = 0;
6182 msg.idata[1] = event.value;
6183 break;
6186 HidInManager::push_message( msg );
6188 break;
6193 if( event.dx || event.dy )
6195 msg.device_type = CK_HID_DEV_MOUSE;
6196 msg.device_num = num;
6197 msg.eid = 0;
6198 msg.type = CK_HID_MOUSE_MOTION;
6199 msg.idata[0] = event.dx;
6200 msg.idata[1] = event.dy;
6201 HidInManager::push_message( msg );
6204 if( event.button1 ^ last_event.button1 )
6206 msg.device_type = CK_HID_DEV_MOUSE;
6207 msg.device_num = num;
6208 msg.eid = 0;
6209 msg.type = event.button1 ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
6210 msg.idata[0] = event.button1;
6211 HidInManager::push_message( msg );
6214 if( event.button2 ^ last_event.button2 )
6216 msg.device_type = CK_HID_DEV_MOUSE;
6217 msg.device_num = num;
6218 msg.eid = 2;
6219 msg.type = event.button2 ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
6220 msg.idata[0] = event.button2;
6221 HidInManager::push_message( msg );
6224 if( event.button3 ^ last_event.button3 )
6226 msg.device_type = CK_HID_DEV_MOUSE;
6227 msg.device_num = num;
6228 msg.eid = 3;
6229 msg.type = event.button3 ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
6230 msg.idata[0] = event.button3;
6231 HidInManager::push_message( msg );
6234 memcpy( &last_event, &event, sizeof( last_event ) );
6238 int m_num; // /dev/input/mouse# <-- the #
6239 //ps2_mouse_event last_event;
6240 input_event last_event;
6243 static unsigned short kb_translation_table[KEY_UNKNOWN];
6245 static void Keyboard_init_translation_table()
6247 memset( kb_translation_table, 0, sizeof( kb_translation_table ) );
6249 kb_translation_table[KEY_ESC] = '\e';
6250 kb_translation_table[KEY_ESC] |= 0x29 << 8;
6252 kb_translation_table[KEY_1] = '1';
6253 kb_translation_table[KEY_1] |= 0x1e << 8;
6255 kb_translation_table[KEY_2] = '2';
6256 kb_translation_table[KEY_2] |= 0x1f << 8;
6258 kb_translation_table[KEY_3] = '3';
6259 kb_translation_table[KEY_3] |= 0x20 << 8;
6261 kb_translation_table[KEY_4] = '4';
6262 kb_translation_table[KEY_4] |= 0x21 << 8;
6264 kb_translation_table[KEY_5] = '5';
6265 kb_translation_table[KEY_5] |= 0x22 << 8;
6267 kb_translation_table[KEY_6] = '6';
6268 kb_translation_table[KEY_6] |= 0x23 << 8;
6270 kb_translation_table[KEY_7] = '7';
6271 kb_translation_table[KEY_7] |= 0x24 << 8;
6273 kb_translation_table[KEY_8] = '8';
6274 kb_translation_table[KEY_8] |= 0x25 << 8;
6276 kb_translation_table[KEY_9] = '9';
6277 kb_translation_table[KEY_9] |= 0x26 << 8;
6279 kb_translation_table[KEY_0] = '0';
6280 kb_translation_table[KEY_0] |= 0x27 << 8;
6282 kb_translation_table[KEY_MINUS] = '-';
6283 kb_translation_table[KEY_MINUS] |= 0x2d << 8;
6285 kb_translation_table[KEY_EQUAL] = '=';
6286 kb_translation_table[KEY_EQUAL] |= 0x2e << 8;
6288 kb_translation_table[KEY_BACKSPACE] = '\b';
6289 kb_translation_table[KEY_BACKSPACE] |= 0x2a << 8;
6291 kb_translation_table[KEY_TAB] = '\t';
6292 kb_translation_table[KEY_TAB] |= 0x2b << 8;
6294 kb_translation_table[KEY_Q] = 'Q';
6295 kb_translation_table[KEY_Q] |= 0x14 << 8;
6297 kb_translation_table[KEY_W] = 'W';
6298 kb_translation_table[KEY_W] |= 0x1a << 8;
6300 kb_translation_table[KEY_E] = 'E';
6301 kb_translation_table[KEY_E] |= 0x08 << 8;
6303 kb_translation_table[KEY_R] = 'R';
6304 kb_translation_table[KEY_R] |= 0x15 << 8;
6306 kb_translation_table[KEY_T] = 'T';
6307 kb_translation_table[KEY_T] |= 0x17 << 8;
6309 kb_translation_table[KEY_Y] = 'Y';
6310 kb_translation_table[KEY_Y] |= 0x1c << 8;
6312 kb_translation_table[KEY_U] = 'U';
6313 kb_translation_table[KEY_U] |= 0x18 << 8;
6315 kb_translation_table[KEY_I] = 'I';
6316 kb_translation_table[KEY_I] |= 0x0c << 8;
6318 kb_translation_table[KEY_O] = 'O';
6319 kb_translation_table[KEY_O] |= 0x12 << 8;
6321 kb_translation_table[KEY_P] = 'P';
6322 kb_translation_table[KEY_P] |= 0x13 << 8;
6324 kb_translation_table[KEY_LEFTBRACE] = '[';
6325 kb_translation_table[KEY_LEFTBRACE] |= 0x2f << 8;
6327 kb_translation_table[KEY_RIGHTBRACE] = ']';
6328 kb_translation_table[KEY_RIGHTBRACE] |= 0x30 << 8;
6330 kb_translation_table[KEY_ENTER] = '\n';
6331 kb_translation_table[KEY_ENTER] |= 0x28 << 8;
6333 kb_translation_table[KEY_LEFTCTRL] = 0;
6334 kb_translation_table[KEY_LEFTCTRL] |= 0xe0 << 8;
6336 kb_translation_table[KEY_A] = 'A';
6337 kb_translation_table[KEY_A] |= 0x04 << 8;
6339 kb_translation_table[KEY_S] = 'S';
6340 kb_translation_table[KEY_S] |= 0x16 << 8;
6342 kb_translation_table[KEY_D] = 'D';
6343 kb_translation_table[KEY_D] |= 0x07 << 8;
6345 kb_translation_table[KEY_F] = 'F';
6346 kb_translation_table[KEY_F] |= 0x09 << 8;
6348 kb_translation_table[KEY_G] = 'G';
6349 kb_translation_table[KEY_G] |= 0x0a << 8;
6351 kb_translation_table[KEY_H] = 'H';
6352 kb_translation_table[KEY_H] |= 0x0b << 8;
6354 kb_translation_table[KEY_J] = 'J';
6355 kb_translation_table[KEY_J] |= 0x0d << 8;
6357 kb_translation_table[KEY_K] = 'K';
6358 kb_translation_table[KEY_K] |= 0x0e << 8;
6360 kb_translation_table[KEY_L] = 'L';
6361 kb_translation_table[KEY_L] |= 0x0f << 8;
6363 kb_translation_table[KEY_SEMICOLON] = ';';
6364 kb_translation_table[KEY_SEMICOLON] |= 0x33 << 8;
6366 kb_translation_table[KEY_APOSTROPHE] = '\'';
6367 kb_translation_table[KEY_APOSTROPHE] |= 0x34 << 8;
6369 kb_translation_table[KEY_GRAVE] = '`';
6370 kb_translation_table[KEY_GRAVE] |= 0x35 << 8;
6372 kb_translation_table[KEY_LEFTSHIFT] = 0;
6373 kb_translation_table[KEY_LEFTSHIFT] |= 0xe1 << 8;
6375 kb_translation_table[KEY_BACKSLASH] = '\\';
6376 kb_translation_table[KEY_BACKSLASH] |= 0x31 << 8;
6378 kb_translation_table[KEY_Z] = 'Z';
6379 kb_translation_table[KEY_Z] |= 0x1d << 8;
6381 kb_translation_table[KEY_X] = 'X';
6382 kb_translation_table[KEY_X] |= 0x1b << 8;
6384 kb_translation_table[KEY_C] = 'C';
6385 kb_translation_table[KEY_C] |= 0x06 << 8;
6387 kb_translation_table[KEY_V] = 'V';
6388 kb_translation_table[KEY_V] |= 0x19 << 8;
6390 kb_translation_table[KEY_B] = 'B';
6391 kb_translation_table[KEY_B] |= 0x05 << 8;
6393 kb_translation_table[KEY_N] = 'N';
6394 kb_translation_table[KEY_N] |= 0x11 << 8;
6396 kb_translation_table[KEY_M] = 'M';
6397 kb_translation_table[KEY_M] |= 0x10 << 8;
6399 kb_translation_table[KEY_COMMA] = ',';
6400 kb_translation_table[KEY_COMMA] |= 0x36 << 8;
6402 kb_translation_table[KEY_DOT] = '.';
6403 kb_translation_table[KEY_DOT] |= 0x37 << 8;
6405 kb_translation_table[KEY_SLASH] = '/';
6406 kb_translation_table[KEY_SLASH] |= 0x38 << 8;
6408 kb_translation_table[KEY_RIGHTSHIFT] = 0;
6409 kb_translation_table[KEY_RIGHTSHIFT] |= 0xe5 << 8;
6411 kb_translation_table[KEY_KPASTERISK] = '*';
6412 kb_translation_table[KEY_KPASTERISK] |= 0x55 << 8;
6414 kb_translation_table[KEY_LEFTALT] = 0;
6415 kb_translation_table[KEY_LEFTALT] |= 0xe2 << 8;
6417 kb_translation_table[KEY_SPACE] = ' ';
6418 kb_translation_table[KEY_SPACE] |= 0x2c << 8;
6420 kb_translation_table[KEY_CAPSLOCK] = 0;
6421 kb_translation_table[KEY_CAPSLOCK] |= 0x39 << 8;
6423 kb_translation_table[KEY_F1] = 0;
6424 kb_translation_table[KEY_F1] |= 0x3a << 8;
6426 kb_translation_table[KEY_F2] = 0;
6427 kb_translation_table[KEY_F2] |= 0x3b << 8;
6429 kb_translation_table[KEY_F3] = 0;
6430 kb_translation_table[KEY_F3] |= 0x3c << 8;
6432 kb_translation_table[KEY_F4] = 0;
6433 kb_translation_table[KEY_F4] |= 0x3d << 8;
6435 kb_translation_table[KEY_F5] = 0;
6436 kb_translation_table[KEY_F5] |= 0x3e << 8;
6438 kb_translation_table[KEY_F6] = 0;
6439 kb_translation_table[KEY_F6] |= 0x3f << 8;
6441 kb_translation_table[KEY_F7] = 0;
6442 kb_translation_table[KEY_F7] |= 0x40 << 8;
6444 kb_translation_table[KEY_F8] = 0;
6445 kb_translation_table[KEY_F8] |= 0x41 << 8;
6447 kb_translation_table[KEY_F9] = 0;
6448 kb_translation_table[KEY_F9] |= 0x42 << 8;
6450 kb_translation_table[KEY_F10] = 0;
6451 kb_translation_table[KEY_F10] |= 0x43 << 8;
6453 kb_translation_table[KEY_NUMLOCK] = 0;
6454 kb_translation_table[KEY_NUMLOCK] |= 0x53 << 8;
6456 kb_translation_table[KEY_SCROLLLOCK] = 0;
6457 kb_translation_table[KEY_SCROLLLOCK] |= 0x47 << 8;
6459 kb_translation_table[KEY_KP7] = '7';
6460 kb_translation_table[KEY_KP7] |= 0x5f << 8;
6462 kb_translation_table[KEY_KP8] = '8';
6463 kb_translation_table[KEY_KP8] |= 0x60 << 8;
6465 kb_translation_table[KEY_KP9] = '9';
6466 kb_translation_table[KEY_KP9] |= 0x61 << 8;
6468 kb_translation_table[KEY_KPMINUS] = '-';
6469 kb_translation_table[KEY_KPMINUS] |= 0x56 << 8;
6471 kb_translation_table[KEY_KP4] = '4';
6472 kb_translation_table[KEY_KP4] |= 0x5c << 8;
6474 kb_translation_table[KEY_KP5] = '5';
6475 kb_translation_table[KEY_KP5] |= 0x5d << 8;
6477 kb_translation_table[KEY_KP6] = '6';
6478 kb_translation_table[KEY_KP6] |= 0x5e << 8;
6480 kb_translation_table[KEY_KPPLUS] = '+';
6481 kb_translation_table[KEY_KPPLUS] |= 0x57 << 8;
6483 kb_translation_table[KEY_KP1] = '1';
6484 kb_translation_table[KEY_KP1] |= 0x59 << 8;
6486 kb_translation_table[KEY_KP2] = '2';
6487 kb_translation_table[KEY_KP2] |= 0x51 << 8;
6489 kb_translation_table[KEY_KP3] = '3';
6490 kb_translation_table[KEY_KP3] |= 0x52 << 8;
6492 kb_translation_table[KEY_KP0] = '0';
6493 kb_translation_table[KEY_KP0] |= 0x62 << 8;
6495 kb_translation_table[KEY_KPDOT] = '.';
6496 kb_translation_table[KEY_KPDOT] |= 0x63 << 8;
6498 kb_translation_table[KEY_F11] = 0;
6499 kb_translation_table[KEY_F11] |= 0x44 << 8;
6501 kb_translation_table[KEY_F12] = 0;
6502 kb_translation_table[KEY_F12] |= 0x45 << 8;
6504 // kb_translation_table[KEY_KPJPCOMMA] = ',';
6505 // kb_translation_table[KEY_KPJPCOMMA] |= 0x85 << 8;
6507 kb_translation_table[KEY_KPENTER] = '\n';
6508 kb_translation_table[KEY_KPENTER] |= 0x58 << 8;
6510 kb_translation_table[KEY_RIGHTCTRL] = 0;
6511 kb_translation_table[KEY_RIGHTCTRL] |= 0xe4 << 8;
6513 kb_translation_table[KEY_KPSLASH] = '/';
6514 kb_translation_table[KEY_KPSLASH] |= 0x54 << 8;
6516 kb_translation_table[KEY_SYSRQ] = 0;
6517 kb_translation_table[KEY_SYSRQ] |= 0x46 << 8;
6519 kb_translation_table[KEY_RIGHTALT] = 0;
6520 kb_translation_table[KEY_RIGHTALT] |= 0xe6 << 8;
6522 kb_translation_table[KEY_HOME] = 0;
6523 kb_translation_table[KEY_HOME] |= 0x4a << 8;
6525 kb_translation_table[KEY_UP] = 0;
6526 kb_translation_table[KEY_UP] |= 0x52 << 8;
6528 kb_translation_table[KEY_PAGEUP] = 0;
6529 kb_translation_table[KEY_PAGEUP] |= 0x4b << 8;
6531 kb_translation_table[KEY_LEFT] = 0;
6532 kb_translation_table[KEY_LEFT] |= 0x50 << 8;
6534 kb_translation_table[KEY_RIGHT] = 0;
6535 kb_translation_table[KEY_RIGHT] |= 0x4f << 8;
6537 kb_translation_table[KEY_END] = 0;
6538 kb_translation_table[KEY_END] |= 0x4d << 8;
6540 kb_translation_table[KEY_DOWN] = 0;
6541 kb_translation_table[KEY_DOWN] |= 0x51 << 8;
6543 kb_translation_table[KEY_PAGEDOWN] = 0;
6544 kb_translation_table[KEY_PAGEDOWN] |= 0x4e << 8;
6546 kb_translation_table[KEY_INSERT] = 0;
6547 kb_translation_table[KEY_INSERT] |= 0x49 << 8;
6549 kb_translation_table[KEY_DELETE] = 0x7f;
6550 kb_translation_table[KEY_DELETE] |= 0x4c << 8;
6552 kb_translation_table[KEY_MUTE] = 0;
6553 kb_translation_table[KEY_MUTE] |= 0x7f << 8;
6555 kb_translation_table[KEY_VOLUMEDOWN] = 0;
6556 kb_translation_table[KEY_VOLUMEDOWN] |= 0x81 << 8;
6558 kb_translation_table[KEY_VOLUMEUP] = 0;
6559 kb_translation_table[KEY_VOLUMEUP] |= 0x80 << 8;
6561 kb_translation_table[KEY_POWER] = 0;
6562 kb_translation_table[KEY_POWER] |= 0x66 << 8;
6564 kb_translation_table[KEY_KPEQUAL] = '=';
6565 kb_translation_table[KEY_KPEQUAL] |= 0x67 << 8;
6567 kb_translation_table[KEY_PAUSE] = 0;
6568 kb_translation_table[KEY_PAUSE] |= 0x48 << 8;
6570 kb_translation_table[KEY_KPCOMMA] = ',';
6571 kb_translation_table[KEY_KPCOMMA] |= 0x85 << 8;
6573 kb_translation_table[KEY_LEFTMETA] = 0;
6574 kb_translation_table[KEY_LEFTMETA] |= 0xe3 << 8;
6576 kb_translation_table[KEY_RIGHTMETA] = 0;
6577 kb_translation_table[KEY_RIGHTMETA] |= 0xe7 << 8;
6579 kb_translation_table[KEY_STOP] = 0;
6580 kb_translation_table[KEY_STOP] |= 0x78 << 8;
6582 kb_translation_table[KEY_AGAIN] = 0;
6583 kb_translation_table[KEY_AGAIN] |= 0x79 << 8;
6585 kb_translation_table[KEY_UNDO] = 0;
6586 kb_translation_table[KEY_UNDO] |= 0x7a << 8;
6588 kb_translation_table[KEY_COPY] = 0;
6589 kb_translation_table[KEY_COPY] |= 0x7c << 8;
6591 kb_translation_table[KEY_PASTE] = 0;
6592 kb_translation_table[KEY_PASTE] |= 0x7d << 8;
6594 kb_translation_table[KEY_FIND] = 0;
6595 kb_translation_table[KEY_FIND] |= 0x7e << 8;
6597 kb_translation_table[KEY_CUT] = 0;
6598 kb_translation_table[KEY_CUT] |= 0x7b << 8;
6600 kb_translation_table[KEY_HELP] = 0;
6601 kb_translation_table[KEY_HELP] |= 0x75 << 8;
6603 kb_translation_table[KEY_MENU] = 0;
6604 kb_translation_table[KEY_MENU] |= 0x76 << 8;
6606 kb_translation_table[KEY_F13] = 0;
6607 kb_translation_table[KEY_F13] |= 0x68 << 8;
6609 kb_translation_table[KEY_F14] = 0;
6610 kb_translation_table[KEY_F14] |= 0x69 << 8;
6612 kb_translation_table[KEY_F15] = 0;
6613 kb_translation_table[KEY_F15] |= 0x6a << 8;
6615 kb_translation_table[KEY_F16] = 0;
6616 kb_translation_table[KEY_F16] |= 0x6b << 8;
6618 kb_translation_table[KEY_F17] = 0;
6619 kb_translation_table[KEY_F17] |= 0x6c << 8;
6621 kb_translation_table[KEY_F18] = 0;
6622 kb_translation_table[KEY_F18] |= 0x6d << 8;
6624 kb_translation_table[KEY_F19] = 0;
6625 kb_translation_table[KEY_F19] |= 0x6e << 8;
6627 kb_translation_table[KEY_F20] = 0;
6628 kb_translation_table[KEY_F20] |= 0x6f << 8;
6630 kb_translation_table[KEY_F21] = 0;
6631 kb_translation_table[KEY_F21] |= 0x70 << 8;
6633 kb_translation_table[KEY_F22] = 0;
6634 kb_translation_table[KEY_F22] |= 0x71 << 8;
6636 kb_translation_table[KEY_F23] = 0;
6637 kb_translation_table[KEY_F23] |= 0x72 << 8;
6639 kb_translation_table[KEY_F24] = 0;
6640 kb_translation_table[KEY_F24] |= 0x73 << 8;
6643 static void Keyboard_translate_key( __u16 evdev_key, long & ascii, long & usb )
6645 unsigned short tr = kb_translation_table[evdev_key];
6647 ascii = tr & 0xff;
6648 usb = ( tr >> 8 ) & 0xff;
6651 class linux_keyboard : public linux_device
6653 public:
6654 linux_keyboard() : linux_device()
6658 virtual void callback()
6660 input_event event;
6661 HidMsg msg;
6662 ssize_t len;
6664 while( ( len = read( fd, &event, sizeof( event ) ) ) > 0 )
6666 if( len < sizeof( event ) )
6668 EM_log( CK_LOG_WARNING, "keyboard: read event from keyboard %i smaller than expected (%i), ignoring", num, len );
6669 continue;
6672 if( event.type != EV_KEY )
6673 continue;
6675 if( event.value == 2 )
6676 continue;
6678 msg.clear();
6679 msg.device_type = CK_HID_DEV_KEYBOARD;
6680 msg.device_num = num;
6681 msg.type = event.value ? CK_HID_BUTTON_DOWN : CK_HID_BUTTON_UP;
6682 msg.eid = event.code;
6683 msg.idata[0] = event.value;
6684 Keyboard_translate_key( event.code, msg.idata[2], msg.idata[1] );
6686 HidInManager::push_message( msg );
6691 static vector< linux_joystick * > * joysticks = NULL;
6692 static vector< linux_mouse * > * mice = NULL;
6693 static vector< linux_keyboard * > * keyboards = NULL;
6695 static map< int, linux_device * > * device_map = NULL;
6697 static pollfd * pollfds = NULL;
6698 #define DEFAULT_POLLFDS_SIZE (1)
6699 static size_t pollfds_size = 0;
6700 static size_t pollfds_end = 0;
6701 static int hid_channel_r = -1; // HID communications channel, read fd
6702 static int hid_channel_w = -1; // HID communications channel, write fd
6704 struct hid_channel_msg
6706 int action;
6707 #define HID_CHANNEL_OPEN 1
6708 #define HID_CHANNEL_CLOSE 0
6709 #define HID_CHANNEL_QUIT -1
6710 linux_device * device;
6713 static t_CKBOOL g_hid_init = FALSE;
6715 void Hid_init()
6717 if( g_hid_init )
6718 return;
6720 pollfds = new pollfd[DEFAULT_POLLFDS_SIZE];
6721 pollfds_size = DEFAULT_POLLFDS_SIZE;
6722 pollfds_end = 0;
6724 int filedes[2];
6725 if( pipe( filedes ) )
6727 EM_log( CK_LOG_SEVERE, "hid: unable to create pipe, initialization failed" );
6728 return;
6731 hid_channel_r = filedes[0];
6732 hid_channel_w = filedes[1];
6734 int fd_flags = fcntl( hid_channel_r, F_GETFL );
6735 fcntl( hid_channel_r, F_SETFL, fd_flags | O_NONBLOCK );
6737 /* right now, the hid_channel is just used as a dummy file descriptor
6738 passed to poll, such that poll will work/block even when there are
6739 no open devices. In the future hid_channel could also be used to
6740 communicate between the VM thread and the HID thread */
6741 pollfds[0].fd = hid_channel_r;
6742 pollfds[0].events = POLLIN;
6743 pollfds[0].revents = 0;
6744 pollfds_end = 1;
6746 device_map = new map< int, linux_device * >;
6748 Keyboard_init_translation_table();
6750 g_hid_init = TRUE;
6753 void Hid_poll()
6755 if( !g_hid_init )
6756 return;
6758 hid_channel_msg hcm;
6759 while( poll( pollfds, pollfds_end, -1 ) > 0 )
6761 for( int i = 1; i < pollfds_end; i++ )
6763 if( pollfds[i].revents & POLLIN )
6765 ( *device_map )[pollfds[i].fd]->callback();
6766 pollfds[i].revents = 0;
6770 if( pollfds[0].revents & POLLIN )
6772 while( read( hid_channel_r, &hcm, sizeof( hcm ) ) > 0 )
6774 if( hcm.action == HID_CHANNEL_OPEN )
6776 if( pollfds_end >= pollfds_size )
6778 pollfds_size = pollfds_end * 2;
6779 pollfd * t_pollfds = new pollfd[pollfds_size];
6780 memcpy( t_pollfds, pollfds, pollfds_end * sizeof( pollfd ) );
6781 delete[] pollfds;
6782 pollfds = t_pollfds;
6785 pollfds[pollfds_end].fd = hcm.device->fd;
6786 pollfds[pollfds_end].events = POLLIN;
6787 pollfds[pollfds_end].revents = 0;
6788 hcm.device->pollfds_i = pollfds_end;
6789 ( *device_map )[hcm.device->fd] = hcm.device;
6790 pollfds_end++;
6793 else if( hcm.action == HID_CHANNEL_CLOSE )
6795 // erase the closing entry by copying the last entry into it
6796 // this is okay even when joystick->pollfds_i == pollfds_end
6797 // because we decrement pollfds_end
6798 ( *device_map )[pollfds[pollfds_end - 1].fd]->pollfds_i = hcm.device->pollfds_i;
6799 pollfds[hcm.device->pollfds_i] = pollfds[pollfds_end - 1];
6800 pollfds_end--;
6801 close( hcm.device->fd );
6802 device_map->erase( hcm.device->fd );
6805 else if( hcm.action == HID_CHANNEL_QUIT )
6807 close( hid_channel_r );
6808 return;
6813 break;
6818 void Hid_quit()
6820 if( !g_hid_init )
6821 return;
6823 hid_channel_msg hcm = { HID_CHANNEL_QUIT, NULL };
6824 write( hid_channel_w, &hcm, sizeof( hcm ) );
6825 close( hid_channel_w );
6827 delete[] pollfds;
6828 delete device_map;
6830 g_hid_init = FALSE;
6833 int TiltSensor_read( t_CKINT * x, t_CKINT * y, t_CKINT * z )
6835 return 0;
6838 // designate new poll rate
6839 t_CKINT TiltSensor_setPollRate( t_CKINT usec )
6841 // sanity
6842 assert( usec >= 0 );
6843 // not supported
6844 fprintf( stderr, "TiltSensor - setPollRate is not (yet) supported on this platform...\n" );
6845 return -1;
6848 // query current poll rate
6849 t_CKINT TiltSensor_getPollRate( )
6851 // not supported
6852 fprintf( stderr, "TiltSensor - getPollRate is not (yet) supported on this platform...\n" );
6853 return -1;
6857 /*****************************************************************************
6858 Linux joystick support
6859 *****************************************************************************/
6860 #pragma mark Linux joystick support
6862 void Joystick_init()
6864 if( joysticks != NULL )
6865 return;
6867 EM_log( CK_LOG_INFO, "initializing joysticks" );
6868 EM_pushlog();
6870 joysticks = new vector< linux_joystick * >;
6872 DIR * dir_handle;
6873 struct dirent * dir_entity;
6874 struct stat stat_buf;
6875 int js_num, fd, i;
6876 uid_t uid = geteuid();
6877 gid_t gid = getegid();
6878 char buf[CK_HID_STRBUFSIZE];
6879 linux_joystick * js;
6881 dir_handle = opendir( CK_HID_DIR );
6882 if( dir_handle == NULL )
6884 if( errno == EACCES )
6885 EM_log( CK_LOG_WARNING, "hid: error opening %s, unable to initialize joystick", CK_HID_DIR );
6886 EM_poplog();
6887 return;
6890 while( dir_entity = readdir( dir_handle ) )
6892 if( sscanf( dir_entity->d_name, CK_HID_JOYSTICKFILE, &js_num ) )
6894 snprintf( buf, CK_HID_STRBUFSIZE, "%s/%s", CK_HID_DIR,
6895 dir_entity->d_name );
6896 if( ( fd = open( buf, O_RDONLY | O_NONBLOCK ) ) >= 0 ||
6897 errno == EACCES ) /* wait to report access errors until the
6898 device is actually opened */
6900 js = new linux_joystick;
6901 js->js_num = js_num;
6902 js->num = joysticks->size();
6903 if( fd >= 0 )
6905 ioctl( fd, JSIOCGNAME( CK_HID_NAMESIZE ), js->name );
6906 close( fd ); // no need to keep the file open
6908 strncpy( js->filename, buf, CK_HID_STRBUFSIZE );
6909 joysticks->push_back( js );
6910 EM_log( CK_LOG_INFO, "joystick: found device %s", js->name );
6915 closedir( dir_handle );
6916 EM_poplog();
6919 void Joystick_poll()
6924 void Joystick_quit()
6926 if( joysticks == NULL )
6927 return;
6929 vector< linux_joystick * >::size_type i, len = joysticks->size();
6931 for( i = 0; i < len; i++ )
6932 delete joysticks->at( i );
6934 delete joysticks;
6935 joysticks = NULL;
6938 int Joystick_count()
6940 if( joysticks == NULL )
6941 return 0;
6942 return joysticks->size();
6945 int Joystick_open( int js )
6947 if( joysticks == NULL || js < 0 || js >= joysticks->size() )
6948 return -1;
6950 linux_joystick * joystick = joysticks->at( js );
6952 if( joystick->refcount == 0 )
6954 if( ( joystick->fd = open( joystick->filename, O_RDONLY | O_NONBLOCK ) ) < 0 )
6956 EM_log( CK_LOG_SEVERE, "joystick: unable to open %s: %s", joystick->filename, strerror( errno ) );
6957 return -1;
6960 hid_channel_msg hcm = { HID_CHANNEL_OPEN, joystick };
6961 write( hid_channel_w, &hcm, sizeof( hcm ) );
6964 joystick->refcount++;
6966 return 0;
6969 int Joystick_close( int js )
6971 if( joysticks == NULL || js < 0 || js >= joysticks->size() )
6972 return -1;
6974 linux_joystick * joystick = joysticks->at( js );
6976 joystick->refcount--;
6978 if( joystick->refcount == 0 )
6980 hid_channel_msg hcm = { HID_CHANNEL_CLOSE, joystick };
6981 write( hid_channel_w, &hcm, sizeof( hcm ) );
6985 return 0;
6988 const char * Joystick_name( int js )
6990 if( joysticks == NULL || js < 0 || js >= joysticks->size() )
6991 return NULL;
6993 return joysticks->at( js )->name;
6996 #define test_bit( array, bit ) (array[bit/8] & (1<<(bit%8)))
6998 void Mouse_configure( const char * filename )
7000 struct stat statbuf;
7001 int fd, devmajor, devminor, is_mouse = 0;
7002 linux_mouse * mouse;
7003 unsigned char relcaps[(REL_MAX / 8) + 1];
7004 unsigned char abscaps[(ABS_MAX / 8) + 1];
7005 unsigned char keycaps[(KEY_MAX / 8) + 1];
7007 if( stat( filename, &statbuf ) == -1 )
7008 return;
7010 if( S_ISCHR( statbuf.st_mode ) == 0 )
7011 return; /* not a character device... */
7013 devmajor = ( statbuf.st_rdev & 0xFF00 ) >> 8;
7014 devminor = ( statbuf.st_rdev & 0x00FF );
7015 if ( ( devmajor != 13 ) || ( devminor < 64 ) || ( devminor > 96 ) )
7016 return; /* not an evdev. */
7018 if( ( fd = open( filename, O_RDONLY | O_NONBLOCK ) ) < 0 )
7019 return;
7021 memset( relcaps, 0, sizeof( relcaps ) );
7022 memset( abscaps, 0, sizeof( abscaps ) );
7023 memset( keycaps, 0, sizeof( keycaps ) );
7025 //int num_keys = 0;
7026 if( ioctl( fd, EVIOCGBIT( EV_KEY, sizeof( keycaps ) ), keycaps ) == -1 )
7027 return;
7029 is_mouse = 0;
7030 if( ioctl( fd, EVIOCGBIT( EV_REL, sizeof( relcaps ) ), relcaps ) != -1 )
7032 if( test_bit( relcaps, REL_X ) && test_bit( relcaps, REL_Y ) &&
7033 test_bit( keycaps, BTN_MOUSE ) )
7035 is_mouse = 1;
7038 if( test_bit( relcaps, REL_DIAL ) )
7039 is_mouse = 1;
7042 if( ioctl( fd, EVIOCGBIT( EV_ABS, sizeof( abscaps ) ), abscaps ) != -1 )
7044 if( test_bit( abscaps, ABS_X ) && test_bit( abscaps, ABS_Y ) &&
7045 ( test_bit( keycaps, BTN_MOUSE ) || test_bit( keycaps, BTN_TOUCH ) ) )
7046 is_mouse = 1;
7049 if( !is_mouse )
7050 return;
7052 mouse = new linux_mouse;
7053 mouse->num = mice->size();
7054 ioctl( fd, EVIOCGNAME( CK_HID_NAMESIZE ), mouse->name );
7055 if( fd >= 0 )
7056 close( fd ); // no need to keep the file open
7057 strncpy( mouse->filename, filename, CK_HID_STRBUFSIZE );
7058 mice->push_back( mouse );
7059 EM_log( CK_LOG_INFO, "mouse: found device %s", mouse->name );
7062 void Mouse_init()
7064 if( mice != NULL )
7065 return;
7067 EM_log( CK_LOG_INFO, "initializing mice" );
7068 EM_pushlog();
7070 mice = new vector< linux_mouse * >;
7072 DIR * dir_handle;
7073 struct dirent * dir_entity;
7074 struct stat stat_buf;
7075 int m_num, fd, i;
7076 char buf[CK_HID_STRBUFSIZE];
7077 linux_mouse * mouse;
7079 dir_handle = opendir( CK_HID_DIR );
7080 if( dir_handle == NULL )
7082 EM_log( CK_LOG_WARNING, "hid: error opening %s, unable to initialize mice", CK_HID_DIR );
7083 EM_poplog();
7084 return;
7087 while( dir_entity = readdir( dir_handle ) )
7089 if( sscanf( dir_entity->d_name, CK_HID_EVDEVFILE, &m_num ) )
7091 snprintf( buf, CK_HID_STRBUFSIZE, "%s/%s", CK_HID_DIR,
7092 dir_entity->d_name );
7093 Mouse_configure( buf );
7094 /* if( ( fd = open( buf, O_RDONLY | O_NONBLOCK ) ) >= 0 ||
7095 errno == EACCES ) *//* wait to report access errors until the
7096 device is actually opened *//*
7098 mouse = new linux_mouse;
7099 mouse->m_num = m_num;
7100 mouse->num = mice->size();
7101 //ioctl( fd, JSIOCGNAME( CK_HID_NAMESIZE ), js->name );
7102 if( fd >= 0 )
7103 close( fd ); // no need to keep the file open
7104 strncpy( mouse->filename, buf, CK_HID_STRBUFSIZE );
7105 mice->push_back( mouse );
7106 //EM_log( CK_LOG_INFO, "mouse: found device %s", mouse->name );
7111 closedir( dir_handle );
7112 EM_poplog();
7116 void Mouse_poll()
7121 void Mouse_quit()
7123 if( mice == NULL )
7124 return;
7126 vector< linux_mouse * >::size_type i, len = mice->size();
7128 for( i = 0; i < len; i++ )
7129 delete mice->at( i );
7131 delete mice;
7132 mice = NULL;
7135 int Mouse_count()
7137 if( !mice )
7138 return 0;
7139 return mice->size();
7142 int Mouse_open( int m )
7144 if( mice == NULL || m < 0 || m >= mice->size() )
7145 return -1;
7147 linux_mouse * mouse = mice->at( m );
7149 if( mouse->refcount == 0 )
7151 if( ( mouse->fd = open( mouse->filename, O_RDONLY | O_NONBLOCK ) ) < 0 )
7153 EM_log( CK_LOG_SEVERE, "mouse: unable to open %s: %s", mouse->filename, strerror( errno ) );
7154 return -1;
7157 mouse->needs_open = TRUE;
7158 hid_channel_msg hcm = { HID_CHANNEL_OPEN, mouse };
7159 write( hid_channel_w, &hcm, sizeof( hcm ) );
7162 mouse->refcount++;
7164 return 0;
7167 int Mouse_close( int m )
7169 if( mice == NULL || m < 0 || m >= mice->size() )
7170 return -1;
7172 linux_mouse * mouse = mice->at( m );
7174 mouse->refcount--;
7176 if( mouse->refcount == 0 )
7178 mouse->needs_close = TRUE;
7179 hid_channel_msg hcm = { HID_CHANNEL_CLOSE, mouse };
7180 write( hid_channel_w, &hcm, sizeof( hcm ) );
7183 return 0;
7186 const char * Mouse_name( int m )
7188 if( mice == NULL || m < 0 || m >= mice->size() )
7189 return NULL;
7190 return mice->at( m )->name;
7193 void Keyboard_configure( const char * filename )
7195 struct stat statbuf;
7196 int fd, devmajor, devminor;
7197 linux_keyboard * keyboard;
7198 unsigned char relcaps[(REL_MAX / 8) + 1];
7199 unsigned char abscaps[(ABS_MAX / 8) + 1];
7200 unsigned char keycaps[(KEY_MAX / 8) + 1];
7202 if( stat( filename, &statbuf ) == -1 )
7203 return;
7205 if( S_ISCHR( statbuf.st_mode ) == 0 )
7206 return; /* not a character device... */
7208 devmajor = ( statbuf.st_rdev & 0xFF00 ) >> 8;
7209 devminor = ( statbuf.st_rdev & 0x00FF );
7210 if ( ( devmajor != 13 ) || ( devminor < 64 ) || ( devminor > 96 ) )
7211 return; /* not an evdev. */
7213 if( ( fd = open( filename, O_RDONLY | O_NONBLOCK ) ) < 0 )
7214 return;
7216 memset( relcaps, 0, sizeof( relcaps ) );
7217 memset( abscaps, 0, sizeof( abscaps ) );
7218 memset( keycaps, 0, sizeof( keycaps ) );
7220 int num_keys = 0;
7221 if( ioctl( fd, EVIOCGBIT( EV_KEY, sizeof( keycaps ) ), keycaps ) == -1 )
7222 return;
7224 if( ioctl( fd, EVIOCGBIT( EV_REL, sizeof( relcaps ) ), relcaps ) != -1 )
7226 for( int i = 0; i < sizeof( relcaps ); i++ )
7227 if( relcaps[i] )
7228 return;
7231 if( ioctl( fd, EVIOCGBIT( EV_ABS, sizeof( abscaps ) ), abscaps ) != -1 )
7233 for( int i = 0; i < sizeof( abscaps ); i++ )
7234 if( abscaps[i] )
7235 return;
7238 for( int i = 0; i < sizeof( keycaps ); i++ )
7240 for( int j = 0; j < 8; j++ )
7242 if( keycaps[i] & ( 1 << j ) )
7243 num_keys++;
7247 keyboard = new linux_keyboard;
7248 keyboard->num = keyboards->size();
7249 ioctl( fd, EVIOCGNAME( CK_HID_NAMESIZE ), keyboard->name );
7250 if( fd >= 0 )
7251 close( fd ); // no need to keep the file open
7252 strncpy( keyboard->filename, filename, CK_HID_STRBUFSIZE );
7253 keyboards->push_back( keyboard );
7254 EM_log( CK_LOG_INFO, "keyboard: found device %s", keyboard->name );
7257 void Keyboard_init()
7259 if( keyboards != NULL )
7260 return;
7262 EM_log( CK_LOG_INFO, "initializing keyboards" );
7263 EM_pushlog();
7265 keyboards = new vector< linux_keyboard * >;
7267 DIR * dir_handle;
7268 struct dirent * dir_entity;
7270 int m_num;
7272 char buf[CK_HID_STRBUFSIZE];
7274 dir_handle = opendir( CK_HID_DIR );
7275 if( dir_handle == NULL )
7277 if( errno == EACCES )
7278 EM_log( CK_LOG_WARNING, "hid: error opening %s, unable to initialize keyboards", CK_HID_DIR );
7279 EM_poplog();
7280 return;
7283 while( dir_entity = readdir( dir_handle ) )
7285 if( sscanf( dir_entity->d_name, CK_HID_EVDEVFILE, &m_num ) )
7287 snprintf( buf, CK_HID_STRBUFSIZE, "%s/%s", CK_HID_DIR,
7288 dir_entity->d_name );
7289 Keyboard_configure( buf );
7293 closedir( dir_handle );
7294 EM_poplog();
7298 void Keyboard_poll()
7303 void Keyboard_quit()
7308 int Keyboard_count()
7310 if( keyboards == NULL )
7311 return 0;
7312 return keyboards->size();
7315 int Keyboard_open( int k )
7317 if( keyboards == NULL || k < 0 || k >= keyboards->size() )
7318 return -1;
7320 linux_keyboard * keyboard = keyboards->at( k );
7322 if( keyboard->refcount == 0 )
7324 if( ( keyboard->fd = open( keyboard->filename, O_RDONLY | O_NONBLOCK ) ) < 0 )
7326 EM_log( CK_LOG_SEVERE, "keyboard: unable to open %s: %s", keyboard->filename, strerror( errno ) );
7327 return -1;
7330 hid_channel_msg hcm = { HID_CHANNEL_OPEN, keyboard };
7331 write( hid_channel_w, &hcm, sizeof( hcm ) );
7334 keyboard->refcount++;
7336 return 0;
7339 int Keyboard_close( int k )
7341 if( keyboards == NULL || k < 0 || k >= keyboards->size() )
7342 return -1;
7344 linux_keyboard * keyboard = keyboards->at( k );
7346 keyboard->refcount--;
7348 if( keyboard->refcount == 0 )
7350 hid_channel_msg hcm = { HID_CHANNEL_CLOSE, keyboard };
7351 write( hid_channel_w, &hcm, sizeof( hcm ) );
7354 return 0;
7357 const char * Keyboard_name( int k )
7359 if( keyboards == NULL || k < 0 || k >= keyboards->size() )
7360 return NULL;
7362 return keyboards->at( k )->name;
7365 #endif
7368 #pragma mark Hid graveyard
7369 /***** empty functions for stuff that isn't yet cross platform *****/
7370 #ifndef __PLATFORM_MACOSX__
7371 /*** empty functions for Mac-only stuff ***/
7373 int Joystick_count_elements( int js, int type ) { return -1; }
7375 int Mouse_count_elements( int js, int type ) { return -1; }
7376 int Mouse_start_cursor_track(){ return -1; }
7377 int Mouse_stop_cursor_track(){ return -1; }
7379 int Keyboard_count_elements( int js, int type ) { return -1; }
7380 int Keyboard_send( int k, const HidMsg * msg ){ return -1; }
7382 int WiiRemote_send( int k, const HidMsg * msg ){ return -1; }
7384 void WiiRemote_init(){}
7385 void WiiRemote_poll(){}
7386 void WiiRemote_quit(){}
7387 void WiiRemote_probe(){}
7388 int WiiRemote_count(){ return 0; }
7389 int WiiRemote_open( int wr ){ return -1; }
7390 int WiiRemote_open( const char * name ){ return -1; }
7391 int WiiRemote_close( int wr ){ return -1; }
7392 int WiiRemote_send( const HidMsg * msg ){ return -1; }
7393 const char * WiiRemote_name( int wr ){ return NULL; }
7395 void TiltSensor_init(){}
7396 void TiltSensor_quit(){}
7397 void TiltSensor_probe(){}
7398 int TiltSensor_count(){ return 0; }
7399 int TiltSensor_open( int ts ){ return -1; }
7400 int TiltSensor_close( int ts ){ return -1; }
7401 int TiltSensor_read( int ts, int type, int num, HidMsg * msg ){ return -1; }
7402 const char * TiltSensor_name( int ts ){ return NULL; }
7405 #endif
7408 #ifdef __CHIP_MODE__
7410 // #include "util_iphone.h"
7412 extern int get_tilt_sensor_x();
7413 extern int get_tilt_sensor_y();
7414 extern int get_tilt_sensor_z();
7416 extern void start_hid_multi_touch();
7417 extern void stop_hid_multi_touch();
7419 void Hid_init(){}
7420 void Hid_poll(){}
7421 void Hid_quit(){}
7423 void Mouse_init(){}
7424 void Mouse_poll(){}
7425 void Mouse_quit(){}
7426 void Mouse_probe(){}
7428 int Mouse_count()
7430 return 1;
7433 int Mouse_count_elements( int js, int type )
7435 return -1;
7438 int Mouse_open( int m )
7440 if(m >= 0 && m < 1)
7442 start_hid_multi_touch();
7443 return 0;
7445 else
7446 return -1;
7449 int Mouse_open( const char * name )
7451 return -1;
7454 int Mouse_close( int m )
7456 if(m >= 0 && m < 1)
7458 stop_hid_multi_touch();
7459 return 0;
7461 else
7462 return -1;
7465 int Mouse_send( int m, const HidMsg * msg )
7467 return -1;
7470 const char * Mouse_name( int m )
7472 if(m == 0)
7473 return "iPhone Multitouch";
7474 else
7475 return NULL;
7478 int Mouse_buttons( int m )
7480 if(m == 0)
7481 return 2;
7482 else
7483 return -1;
7486 int Mouse_start_cursor_track()
7488 return -1;
7491 int Mouse_stop_cursor_track()
7493 return -1;
7496 void TiltSensor_init(){}
7497 void TiltSensor_quit(){}
7498 void TiltSensor_probe(){}
7500 int TiltSensor_count()
7502 return 1;
7505 int TiltSensor_open( int ts )
7507 if(ts == 0)
7508 return 0;
7509 return -1;
7512 int TiltSensor_close( int ts )
7514 if(ts == 0)
7515 return 0;
7516 return -1;
7519 int TiltSensor_read( int ts, int type, int num, HidMsg * msg )
7521 if( type != CK_HID_ACCELEROMETER )
7522 return -1;
7524 msg->idata[0] = get_tilt_sensor_x();
7525 msg->idata[1] = get_tilt_sensor_y();
7526 msg->idata[2] = get_tilt_sensor_z();
7528 return 0;
7531 const char * TiltSensor_name( int ts )
7533 if(ts == 0)
7534 return "iPhone Accelerometer";
7535 else
7536 return NULL;
7539 // ge: SMS multi-thread poll rate
7540 t_CKINT TiltSensor_setPollRate( t_CKINT usec )
7542 return -1;
7545 t_CKINT TiltSensor_getPollRate( )
7547 return -1;
7550 void Joystick_init(){}
7552 void Joystick_poll(){}
7554 void Joystick_quit(){}
7556 void Joystick_probe(){}
7558 int Joystick_count()
7560 return 0;
7563 int Joystick_count_elements( int js, int type )
7565 return -1;
7568 int Joystick_open( int js )
7570 return -1;
7573 int Joystick_open_async( int js )
7575 return -1;
7578 int Joystick_open( const char * name )
7580 return -1;
7583 int Joystick_close( int js )
7585 return -1;
7588 int Joystick_send( int js, const HidMsg * msg )
7590 return -1;
7594 const char * Joystick_name( int js )
7596 return NULL;
7599 int Joystick_axes( int js )
7601 return -1;
7604 int Joystick_buttons( int js )
7606 return -1;
7609 int Joystick_hats( int js )
7611 return -1;
7614 void Keyboard_init(){}
7615 void Keyboard_poll(){}
7616 void Keyboard_quit(){}
7617 void Keyboard_probe(){}
7619 int Keyboard_count()
7621 return 0;
7624 int Keyboard_count_elements( int js, int type )
7626 return -1;
7629 int Keyboard_open( int kb )
7631 return -1;
7634 int Keyboard_open( const char * name )
7636 return -1;
7639 int Keyboard_close( int kb )
7641 return -1;
7644 int Keyboard_send( int kb, const HidMsg * msg )
7646 return -1;
7649 const char * Keyboard_name( int kb )
7651 return NULL;
7654 void WiiRemote_init(){}
7655 void WiiRemote_poll(){}
7656 void WiiRemote_quit(){}
7657 void WiiRemote_probe(){}
7659 int WiiRemote_count()
7661 return 0;
7664 int WiiRemote_open( int wr )
7666 return -1;
7669 int WiiRemote_open( const char * name )
7671 return -1;
7674 int WiiRemote_close( int wr )
7676 return -1;
7679 int WiiRemote_send( int wr, const HidMsg * msg )
7681 return -1;
7684 const char * WiiRemote_name( int wr )
7686 return NULL;
7690 #endif // __CHIP_MODE__