1 // FGMacOSXEventInput.cxx -- handle event driven input devices for Mac OS X
3 // Written by Tatsuhiro Nishioka, started Aug. 2009.
5 // Copyright (C) 2009 Tasuhiro Nishioka, tat <dot> fgmacosx <at> gmail <dot> com
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "FGMacOSXEventInput.hxx"
26 #define GetHIDElementLongValue(element, key) ({ \
28 CFNumberRef refType = (CFNumberRef)CFDictionaryGetValue(element, CFSTR(key)); \
29 if (refType) { CFNumberGetValue(refType, kCFNumberLongType, &value); } \
32 #define GetHIDElementBooleanValue(element, key) ({ \
34 CFBooleanRef refType = (CFBooleanRef)CFDictionaryGetValue(element, CFSTR(key)); \
35 if (refType) { value = CFBooleanGetValue(refType); } \
38 #define GetHIDElementStringValue(element, key) ({ \
39 const char *buf = ""; \
40 CFStringRef refType = (CFStringRef)CFDictionaryGetValue(element, CFSTR(key)); \
42 buf = CFStringGetCStringPtr(refType, CFStringGetSystemEncoding()); \
46 // HID Element Types (log / debug use)
47 struct HIDTypes HID_TYPE_TABLE[] = {
48 {kIOHIDElementTypeInput_Misc, kHIDElementType, "Input Misc"},
49 {kIOHIDElementTypeInput_Button, kHIDElementType, "Input Button"},
50 {kIOHIDElementTypeInput_Axis, kHIDElementType, "Input Axis"},
51 {kIOHIDElementTypeInput_ScanCodes, kHIDElementType, "Input ScanCodes"},
52 {kIOHIDElementTypeOutput, kHIDElementType, "Output"},
53 {kIOHIDElementTypeFeature, kHIDElementType, "Feature"},
54 {kIOHIDElementTypeCollection, kHIDElementType, "Collection"},
55 {-1, kHIDElementType, ""}
58 // HID Element Pages (log / debug use)
59 struct HIDTypes HID_PAGE_TABLE[] = {
61 {kHIDPage_GenericDesktop, kHIDElementPage, "GenericDesktop"},
62 {kHIDPage_Simulation, kHIDElementPage, "Simulation Controls"},
63 {kHIDPage_VR, kHIDElementPage, "VR Controls"},
64 {kHIDPage_Sport, kHIDElementPage, "Sport Controls"},
65 {kHIDPage_Game, kHIDElementPage, "Game Controls"},
66 {0x06, kHIDElementPage, "Generic Device Controls"},
67 {kHIDPage_KeyboardOrKeypad, kHIDElementPage, "KeyboardOrKeypad"},
68 {kHIDPage_LEDs, kHIDElementPage, "LEDs"},
69 {kHIDPage_Button, kHIDElementPage, "Button"},
70 {kHIDPage_Ordinal, kHIDElementPage, "Ordinal"},
71 {kHIDPage_Telephony, kHIDElementPage, "Telephony"},
72 {kHIDPage_Consumer, kHIDElementPage, "Consumer"},
73 {kHIDPage_Digitizer, kHIDElementPage, "Digitizer"},
74 {kHIDPage_PID, kHIDElementPage, "PID"},
75 {kHIDPage_VendorDefinedStart, kHIDElementPage, "VendorDefinedStart"},
76 {-1, kHIDElementPage, ""}
79 #define USAGE_KEY(PAGE,USAGE) (PAGE << 16 | USAGE)
80 #define GET_PAGE(KEY) (KEY >> 16)
81 #define GET_USAGE(KEY) (KEY & 0xFFFF)
83 #define GD_USAGE(USAGE) USAGE_KEY(kHIDPage_GenericDesktop, USAGE)
84 #define SIM_USAGE(USAGE) USAGE_KEY(kHIDPage_GenericDesktop, USAGE)
85 #define VR_USAGE(USAGE) USAGE_KEY(kHIDPage_VR, USAGE)
86 #define SP_USAGE(USAGE) USAGE_KEY(kHIDPage_Sport, USAGE)
87 #define GAME_USAGE(USAGE) USAGE_KEY(kHIDPage_Game, USAGE)
88 #define GDC_USAGE(USAGE) USAGE_KEY(0x06, USAGE) // Generic Device Control is "Reserved" in Apple's definition
89 #define KEY_USAGE(USAGE) USAGE_KEY(kHIDPage_KeyboardOrKeypad, USAGE)
90 #define LED_USAGE(USAGE) USAGE_KEY(kHIDPage_LEDs, USAGE)
91 #define BUTTON_USAGE(USAGE) USAGE_KEY(kHIDPage_Button, USAGE)
92 #define DIG_USAGE(USAGE) USAGE_KEY(kHIDPage_Digitizer, USAGE)
93 #define CON_USAGE(USAGE) USAGE_KEY(kHIDPage_Consumer, USAGE)
95 // HID Element Usage <-> FGEventData convertion data
96 struct HIDTypes HID_USAGE_TABLE[] = {
97 // Generic Desktop Page
98 {GD_USAGE(kHIDUsage_GD_X), kHIDUsageAxis, "x-translate"},
99 {GD_USAGE(kHIDUsage_GD_Y), kHIDUsageAxis, "y-translate"},
100 {GD_USAGE(kHIDUsage_GD_Z), kHIDUsageAxis, "z-translate"},
101 {GD_USAGE(kHIDUsage_GD_Rx), kHIDUsageAxis, "x-rotate"},
102 {GD_USAGE(kHIDUsage_GD_Ry), kHIDUsageAxis, "y-rotate"},
103 {GD_USAGE(kHIDUsage_GD_Rz), kHIDUsageAxis, "z-rotate"},
104 {GD_USAGE(kHIDUsage_GD_Slider), kHIDUsageAxis, "slider"},
105 {GD_USAGE(kHIDUsage_GD_Dial), kHIDUsageAxis, "dial"},
106 {GD_USAGE(kHIDUsage_GD_Wheel), kHIDUsageAxis, "wheel"},
107 {GD_USAGE(kHIDUsage_GD_Hatswitch), kHIDUsageHat, "hat"},
109 {GD_USAGE(kHIDUsage_GD_CountedBuffer), kHIDUsageNotSupported, "counted-buffer"},
110 {GD_USAGE(kHIDUsage_GD_ByteCount), kHIDUsageNotSupported, "byte-count"},
111 {GD_USAGE(kHIDUsage_GD_MotionWakeup), kHIDUsageDF, "motion-wakeup"},
112 {GD_USAGE(kHIDUsage_GD_Start), kHIDUsageOOC, "button-start"},
113 {GD_USAGE(kHIDUsage_GD_Select), kHIDUsageOOC, "button-select"},
114 {GD_USAGE(kHIDUsage_GD_Vx), kHIDUsageAxis, "x-vector"},
115 {GD_USAGE(kHIDUsage_GD_Vy), kHIDUsageAxis, "y-vector"},
116 {GD_USAGE(kHIDUsage_GD_Vz), kHIDUsageAxis, "z-vector"},
117 {GD_USAGE(kHIDUsage_GD_Vbrx), kHIDUsageAxis, "x-rel-vector"},
118 {GD_USAGE(kHIDUsage_GD_Vbry), kHIDUsageAxis, "y-rel-vector"},
119 {GD_USAGE(kHIDUsage_GD_Vbrz), kHIDUsageAxis, "z-rel-vector"},
120 {GD_USAGE(kHIDUsage_GD_Vno), kHIDUsageAxis, "no-vector"},
122 {GD_USAGE(kHIDUsage_GD_SystemPowerDown), kHIDUsageOSC, "button-system-power-down"},
123 {GD_USAGE(kHIDUsage_GD_SystemSleep), kHIDUsageOSC, "button-system-sleep"},
124 {GD_USAGE(kHIDUsage_GD_SystemWakeUp), kHIDUsageOSC, "button-system-wake-up"},
125 {GD_USAGE(kHIDUsage_GD_SystemContextMenu), kHIDUsageOSC, "button-system-context-menu"},
126 {GD_USAGE(kHIDUsage_GD_SystemMainMenu), kHIDUsageOSC, "button-system-main-menu"},
127 {GD_USAGE(kHIDUsage_GD_SystemAppMenu), kHIDUsageOSC, "button-system-app-menu"},
128 {GD_USAGE(kHIDUsage_GD_SystemMenuHelp), kHIDUsageOSC, "button-system-menu-help"},
129 {GD_USAGE(kHIDUsage_GD_SystemMenuExit), kHIDUsageOSC, "button-system-menu-exit"},
130 {GD_USAGE(kHIDUsage_GD_SystemMenu), kHIDUsageOSC, "button-system-menu"},
131 {GD_USAGE(kHIDUsage_GD_SystemMenuRight), kHIDUsageRTC, "button-system-menu-right"},
132 {GD_USAGE(kHIDUsage_GD_SystemMenuLeft), kHIDUsageRTC, "button-system-menu-left"},
133 {GD_USAGE(kHIDUsage_GD_SystemMenuUp), kHIDUsageRTC, "button-system-menu-up"},
134 {GD_USAGE(kHIDUsage_GD_SystemMenuDown), kHIDUsageRTC, "button-system-menu-down"},
135 {GD_USAGE(kHIDUsage_GD_DPadUp), kHIDUsageOOC, "dpad-up"},
136 {GD_USAGE(kHIDUsage_GD_DPadDown), kHIDUsageOOC, "dpad-down"},
137 {GD_USAGE(kHIDUsage_GD_DPadRight), kHIDUsageOOC, "dpad-right"},
138 {GD_USAGE(kHIDUsage_GD_DPadLeft), kHIDUsageOOC, "dpad-left"},
140 // Game Controls Page
141 {GAME_USAGE(kHIDUsage_Game_TurnRightOrLeft), kHIDUsageAxis, "turn"},
142 {GAME_USAGE(kHIDUsage_Game_PitchUpOrDown), kHIDUsageAxis, "pitch"},
143 {GAME_USAGE(kHIDUsage_Game_MoveRightOrLeft), kHIDUsageAxis, "x-move"},
144 {GAME_USAGE(kHIDUsage_Game_MoveForwardOrBackward), kHIDUsageAxis, "y-move"},
145 {GAME_USAGE(kHIDUsage_Game_MoveUpOrDown), kHIDUsageAxis, "z-move"},
146 {GAME_USAGE(kHIDUsage_Game_LeanRightOrLeft), kHIDUsageAxis, "x-lean"},
147 {GAME_USAGE(kHIDUsage_Game_LeanForwardOrBackward), kHIDUsageAxis, "z-lean"},
149 // General Control Devices Page
150 {GDC_USAGE(0x20), kHIDUsageDV, "battery-strength"},
151 {GDC_USAGE(0x21), kHIDUsageDV, "wireless-channel"},
152 {GDC_USAGE(0x22), kHIDUsageDV, "wireless-id"},
153 {GDC_USAGE(0x23), kHIDUsageDV, "discover-wireless-control"},
154 {GDC_USAGE(0x24), kHIDUsageOSC, "security-code-character-entered"},
155 {GDC_USAGE(0x25), kHIDUsageOSC, "security-code-character-erased"},
156 {GDC_USAGE(0x26), kHIDUsageOSC, "security-code-cleared"},
158 // Simulation Controls Page
159 {SIM_USAGE(kHIDUsage_Sim_Aileron), kHIDUsageAxis, "aileron"},
160 {SIM_USAGE(kHIDUsage_Sim_AileronTrim), kHIDUsageAxis, "aileron-trim"},
161 {SIM_USAGE(kHIDUsage_Sim_AntiTorqueControl), kHIDUsageAxis, "anti-torque-control"},
162 {SIM_USAGE(kHIDUsage_Sim_AutopilotEnable), kHIDUsageOOC, "button-autopilot-enable"},
163 {SIM_USAGE(kHIDUsage_Sim_ChaffRelease), kHIDUsageOSC, "button-chaff-release"},
164 {SIM_USAGE(kHIDUsage_Sim_CollectiveControl), kHIDUsageAxis, "collective-control"},
165 {SIM_USAGE(kHIDUsage_Sim_DiveBrake), kHIDUsageAxis, "dive-brake"},
166 {SIM_USAGE(kHIDUsage_Sim_ElectronicCountermeasures), kHIDUsageOOC, "electronic-countermeasures"}, // OOC
167 {SIM_USAGE(kHIDUsage_Sim_Elevator), kHIDUsageAxis, "elevator"},
168 {SIM_USAGE(kHIDUsage_Sim_ElevatorTrim), kHIDUsageAxis, "elevator-trim"},
169 {SIM_USAGE(kHIDUsage_Sim_Rudder), kHIDUsageAxis, "rudder"},
170 {SIM_USAGE(kHIDUsage_Sim_Throttle), kHIDUsageAxis, "throttle"},
171 {SIM_USAGE(kHIDUsage_Sim_FlightCommunications), kHIDUsageOOC, "button-flight-communications"}, // OOC
172 {SIM_USAGE(kHIDUsage_Sim_FlareRelease), kHIDUsageOSC, "button-flare-release"},
173 {SIM_USAGE(kHIDUsage_Sim_LandingGear), kHIDUsageOOC, "button-landing-gear"}, // OOC
174 {SIM_USAGE(kHIDUsage_Sim_ToeBrake), kHIDUsageAxis, "toe-brake"},
175 {SIM_USAGE(kHIDUsage_Sim_Trigger), kHIDUsageMC, "button-trigger"},
176 {SIM_USAGE(kHIDUsage_Sim_WeaponsArm), kHIDUsageOOC, "button-weapons-arm"}, // OOC
177 {SIM_USAGE(kHIDUsage_Sim_Weapons), kHIDUsageOSC, "button-weapons"},
178 {SIM_USAGE(kHIDUsage_Sim_WingFlaps), kHIDUsageAxis, "wing-flaps"}, // DV
179 {SIM_USAGE(kHIDUsage_Sim_Accelerator), kHIDUsageAxis, "accelerator"}, // DV
180 {SIM_USAGE(kHIDUsage_Sim_Brake), kHIDUsageAxis, "brake"}, // DV
181 {SIM_USAGE(kHIDUsage_Sim_Clutch), kHIDUsageAxis, "clutch"}, // DV
182 {SIM_USAGE(kHIDUsage_Sim_Shifter), kHIDUsageAxis, "shifter"}, // DV
183 {SIM_USAGE(kHIDUsage_Sim_Steering), kHIDUsageAxis, "steering"}, // DV
184 {SIM_USAGE(kHIDUsage_Sim_TurretDirection), kHIDUsageAxis, "turret-direction"}, // DV
185 {SIM_USAGE(kHIDUsage_Sim_BarrelElevation), kHIDUsageAxis, "barrel-elevation"}, // DV
186 {SIM_USAGE(kHIDUsage_Sim_DivePlane), kHIDUsageAxis, "dive-plane"}, // DV
187 {SIM_USAGE(kHIDUsage_Sim_Ballast), kHIDUsageAxis, "ballast"}, // DV
188 {SIM_USAGE(kHIDUsage_Sim_BicycleCrank), kHIDUsageAxis, "bicycle-crank"}, // DV
189 {SIM_USAGE(kHIDUsage_Sim_HandleBars), kHIDUsageAxis, "handle-bars"}, // DV
190 {SIM_USAGE(kHIDUsage_Sim_FrontBrake), kHIDUsageAxis, "front-brake"}, // DV
191 {SIM_USAGE(kHIDUsage_Sim_RearBrake), kHIDUsageAxis, "rear-brake"}, // DV
193 // Digitizer Controls Page
194 {DIG_USAGE(kHIDUsage_Dig_TipPressure), kHIDUsageAxis, "tip-pressure"}, // DV
195 {DIG_USAGE(kHIDUsage_Dig_BarrelPressure), kHIDUsageAxis, "barrel-pressure"}, // DV
196 {DIG_USAGE(kHIDUsage_Dig_InRange), kHIDUsageMC, "in-range"}, // MC
197 {DIG_USAGE(kHIDUsage_Dig_Touch), kHIDUsageMC, "touch"}, // MC
198 {DIG_USAGE(kHIDUsage_Dig_Untouch), kHIDUsageOSC, "button-untouch"}, // OSC
199 {DIG_USAGE(kHIDUsage_Dig_Tap), kHIDUsageOSC, "button-tap"}, // OSC
200 {DIG_USAGE(kHIDUsage_Dig_Quality), kHIDUsageDV, "quality"}, // DV
201 {DIG_USAGE(kHIDUsage_Dig_DataValid), kHIDUsageDV, "button-data-valid"}, // MC
202 {DIG_USAGE(kHIDUsage_Dig_TransducerIndex), kHIDUsageDV, "transducer-index"}, // DV
203 {DIG_USAGE(kHIDUsage_Dig_BatteryStrength), kHIDUsageDV, "battery-strength"}, // DV
204 {DIG_USAGE(kHIDUsage_Dig_Invert), kHIDUsageMC, "invert"}, // MC
205 {DIG_USAGE(kHIDUsage_Dig_XTilt), kHIDUsageAxis, "x-tilt"}, // DV
206 {DIG_USAGE(kHIDUsage_Dig_YTilt), kHIDUsageAxis, "y-tilt"}, // DV
207 {DIG_USAGE(kHIDUsage_Dig_Azimuth), kHIDUsageAxis, "azimuth"}, // DV
208 {DIG_USAGE(kHIDUsage_Dig_Altitude), kHIDUsageAxis, "altitude"}, // DV
209 {DIG_USAGE(kHIDUsage_Dig_Twist), kHIDUsageAxis, "twist"}, // DV
210 {DIG_USAGE(kHIDUsage_Dig_TipSwitch), kHIDUsageMC, "button-tipswitch"}, // MC
211 {DIG_USAGE(kHIDUsage_Dig_SecondaryTipSwitch), kHIDUsageMC, "button-secondary-tipswitch"}, // MC
212 {DIG_USAGE(kHIDUsage_Dig_BarrelSwitch), kHIDUsageMC, "button-barrelswitch"}, // MC
213 {DIG_USAGE(kHIDUsage_Dig_Eraser), kHIDUsageMC, "eraser"}, // MC
214 {DIG_USAGE(kHIDUsage_Dig_TabletPick), kHIDUsageMC, "table-pick"}, // MC
217 {CON_USAGE(kHIDUsage_Csmr_Plus10), kHIDUsageOSC, "plus10"},
218 {CON_USAGE(kHIDUsage_Csmr_Plus100), kHIDUsageOSC, "plus100"},
219 {CON_USAGE(kHIDUsage_Csmr_AMOrPM), kHIDUsageOSC, "am-pm"},
220 {CON_USAGE(kHIDUsage_Csmr_Power), kHIDUsageOOC, "power"},
221 {CON_USAGE(kHIDUsage_Csmr_Reset), kHIDUsageOSC, "reset"},
222 {CON_USAGE(kHIDUsage_Csmr_Sleep), kHIDUsageOSC, "sleep"},
223 {CON_USAGE(kHIDUsage_Csmr_SleepAfter), kHIDUsageOSC, "sleep-after"},
224 {CON_USAGE(kHIDUsage_Csmr_SleepMode), kHIDUsageRTC, "sleep-mode"},
225 {CON_USAGE(kHIDUsage_Csmr_Illumination), kHIDUsageOOC, "illumination"},
226 {CON_USAGE(kHIDUsage_Csmr_Menu), kHIDUsageOOC, "menu"},
227 {CON_USAGE(kHIDUsage_Csmr_MenuPick), kHIDUsageOSC, "menu-pick"},
228 {CON_USAGE(kHIDUsage_Csmr_MenuUp), kHIDUsageOSC, "menu-up"},
229 {CON_USAGE(kHIDUsage_Csmr_MenuDown), kHIDUsageOSC, "menu-down"},
230 {CON_USAGE(kHIDUsage_Csmr_MenuLeft), kHIDUsageOSC, "menu-left"},
231 {CON_USAGE(kHIDUsage_Csmr_MenuRight), kHIDUsageOSC, "menu-right"},
232 {CON_USAGE(kHIDUsage_Csmr_MenuEscape), kHIDUsageOSC, "menu-escape"},
233 {CON_USAGE(kHIDUsage_Csmr_MenuValueIncrease), kHIDUsageOSC, "menu-value-increase"},
234 {CON_USAGE(kHIDUsage_Csmr_MenuValueDecrease), kHIDUsageOSC, "menu-value-decrease"},
235 {CON_USAGE(kHIDUsage_Csmr_DataOnScreen), kHIDUsageOOC, "data-on-screen"},
236 {CON_USAGE(kHIDUsage_Csmr_ClosedCaption), kHIDUsageOOC, "closed-caption"},
237 {CON_USAGE(kHIDUsage_Csmr_ClosedCaptionSelect), kHIDUsageSel, "closed-caption-select"},
238 {CON_USAGE(kHIDUsage_Csmr_VCROrTV), kHIDUsageOOC, "vcr-tv"},
239 {CON_USAGE(kHIDUsage_Csmr_BroadcastMode), kHIDUsageOSC, "broadcast-mode"},
240 {CON_USAGE(kHIDUsage_Csmr_Snapshot), kHIDUsageOSC, "snapshot"},
241 {CON_USAGE(kHIDUsage_Csmr_Still), kHIDUsageOSC, "still"},
242 {CON_USAGE(kHIDUsage_Csmr_Assign), kHIDUsageOSC, "assign"},
243 {CON_USAGE(kHIDUsage_Csmr_ModeStep), kHIDUsageOSC, "mode-step"},
244 {CON_USAGE(kHIDUsage_Csmr_RecallLast), kHIDUsageOSC, "recall-last"},
245 {CON_USAGE(kHIDUsage_Csmr_EnterChannel), kHIDUsageOSC, "enter-channel"},
246 {CON_USAGE(kHIDUsage_Csmr_OrderMovie), kHIDUsageOSC, "order-movie"},
247 {CON_USAGE(kHIDUsage_Csmr_Channel), kHIDUsageDV, "channel"}, // LC
248 {CON_USAGE(kHIDUsage_Csmr_MediaSelection), kHIDUsageSel, "media-selection"},
249 {CON_USAGE(kHIDUsage_Csmr_MediaSelectComputer), kHIDUsageSel, "media-select-computer"},
250 {CON_USAGE(kHIDUsage_Csmr_MediaSelectTV), kHIDUsageSel, "media-select-tv"},
251 {CON_USAGE(kHIDUsage_Csmr_MediaSelectWWW), kHIDUsageSel, "media-seleci-www"},
252 {CON_USAGE(kHIDUsage_Csmr_MediaSelectDVD), kHIDUsageSel, "media-select-dvd"},
253 {CON_USAGE(kHIDUsage_Csmr_MediaSelectTelephone), kHIDUsageSel, "media-select-telephone"},
254 {CON_USAGE(kHIDUsage_Csmr_MediaSelectProgramGuide), kHIDUsageSel, "media-select-programguide"},
255 {CON_USAGE(kHIDUsage_Csmr_MediaSelectVideoPhone), kHIDUsageSel, "media-select-videophone"},
256 {CON_USAGE(kHIDUsage_Csmr_MediaSelectGames), kHIDUsageSel, "media-select-games"},
257 {CON_USAGE(kHIDUsage_Csmr_MediaSelectMessages), kHIDUsageSel, "media-select-messages"},
258 {CON_USAGE(kHIDUsage_Csmr_MediaSelectCD), kHIDUsageSel, "media-select-cd"},
259 {CON_USAGE(kHIDUsage_Csmr_MediaSelectVCR), kHIDUsageSel, "media-select-vcr"},
260 {CON_USAGE(kHIDUsage_Csmr_MediaSelectTuner), kHIDUsageOSC, "media-select-tuner"},
261 {CON_USAGE(kHIDUsage_Csmr_Quit), kHIDUsageOSC, "quit"},
262 {CON_USAGE(kHIDUsage_Csmr_Help), kHIDUsageOOC, "help"},
263 {CON_USAGE(kHIDUsage_Csmr_MediaSelectTape), kHIDUsageSel, "media-select-tape"},
264 {CON_USAGE(kHIDUsage_Csmr_MediaSelectCable), kHIDUsageSel, "media-select-cable"},
265 {CON_USAGE(kHIDUsage_Csmr_MediaSelectSatellite), kHIDUsageSel, "media-select-satellite"},
266 {CON_USAGE(kHIDUsage_Csmr_MediaSelectSecurity), kHIDUsageSel, "media-select-security"},
267 {CON_USAGE(kHIDUsage_Csmr_MediaSelectHome), kHIDUsageSel, "media-select-home"},
268 {CON_USAGE(kHIDUsage_Csmr_MediaSelectCall), kHIDUsageSel, "media-select-call"},
269 {CON_USAGE(kHIDUsage_Csmr_ChannelIncrement), kHIDUsageOSC, "channel-increment"},
270 {CON_USAGE(kHIDUsage_Csmr_ChannelDecrement), kHIDUsageOSC, "channel-decrement"},
271 {CON_USAGE(kHIDUsage_Csmr_Media), kHIDUsageSel, "media"},
272 {CON_USAGE(kHIDUsage_Csmr_VCRPlus), kHIDUsageOSC, "vcr-plus"},
273 {CON_USAGE(kHIDUsage_Csmr_Once), kHIDUsageOSC, "once"},
274 {CON_USAGE(kHIDUsage_Csmr_Daily), kHIDUsageOSC, "daily"},
275 {CON_USAGE(kHIDUsage_Csmr_Weekly), kHIDUsageOSC, "weekly"},
276 {CON_USAGE(kHIDUsage_Csmr_Monthly), kHIDUsageOSC, "monthly"},
277 {CON_USAGE(kHIDUsage_Csmr_Play), kHIDUsageOOC, "play"},
278 {CON_USAGE(kHIDUsage_Csmr_Pause), kHIDUsageOOC, "pause"},
279 {CON_USAGE(kHIDUsage_Csmr_Record), kHIDUsageOOC, "record"},
280 {CON_USAGE(kHIDUsage_Csmr_FastForward), kHIDUsageOOC,"fastforward"},
281 {CON_USAGE(kHIDUsage_Csmr_Rewind), kHIDUsageOOC, "rewind"},
282 {CON_USAGE(kHIDUsage_Csmr_ScanNextTrack), kHIDUsageOSC, "scan-next-track"},
283 {CON_USAGE(kHIDUsage_Csmr_ScanPreviousTrack), kHIDUsageOSC, "scan-previous-track"},
284 {CON_USAGE(kHIDUsage_Csmr_Stop), kHIDUsageOSC, "stop"},
285 {CON_USAGE(kHIDUsage_Csmr_Eject), kHIDUsageOSC, "eject"},
286 {CON_USAGE(kHIDUsage_Csmr_RandomPlay), kHIDUsageOOC, "random-play"},
287 {CON_USAGE(kHIDUsage_Csmr_SelectDisc), kHIDUsageNotSupported, "select-disc"}, //NamedArray
289 {CON_USAGE(kHIDUsage_Csmr_VolumeIncrement), kHIDUsageRTC, "volume-increment"},
290 {CON_USAGE(kHIDUsage_Csmr_VolumeDecrement), kHIDUsageRTC, "volume-decrement"},
291 {CON_USAGE(kHIDUsage_Csmr_PlayOrPause), kHIDUsageOSC, "play-pause"},
292 {CON_USAGE(kHIDUsage_Csmr_Mute), kHIDUsageOOC, "mute"},
293 // Too many... and the rest are T.B.D. ;-)
294 {-1, kHIDElementPage, ""},
297 static HIDTypeByID hidTypeByID(HID_TYPE_TABLE);
298 static HIDTypeByID hidPageByID(HID_PAGE_TABLE);
299 static HIDTypeByID hidUsageByID(HID_USAGE_TABLE);
302 HIDElement::HIDElement(CFDictionaryRef element, long page, long usage) :
303 page(page), usage(usage), value(0.0), lastValue(0.0) {
305 cookie = (IOHIDElementCookie)GetHIDElementLongValue(element, kIOHIDElementCookieKey);
306 name = hidUsageByID.getName(USAGE_KEY(page, usage));
309 float HIDElement::readStatus(IOHIDDeviceInterface **interface)
311 IOHIDEventStruct event;
313 IOReturn ret = (*interface)->getElementValue(interface, cookie, &event);
314 if (ret == kIOReturnSuccess) {
316 // SG_LOG(SG_INPUT, SG_BULK, "Element: " << name << "; value = " << value);
317 return (float)event.value;
319 SG_LOG(SG_INPUT, SG_ALERT, "Failed reading value for HID Element: " << name);
324 bool HIDElement::isUpdated()
326 return (value != lastValue);
329 void HIDElement::generateEvent(FGMacOSXInputDevice *device, double dt, int modifiers)
331 SG_LOG(SG_INPUT, SG_DEBUG, "Generating Input Event: " << name << "=" << value);
332 FGMacOSXEventData eventData(name, value, dt, modifiers);
333 device->HandleEvent(eventData);
336 AxisElement::AxisElement(CFDictionaryRef element, long page, long usage) :
337 HIDElement(element, page, usage), dead_band(0.00), saturate(1.0)
339 // long scaledmin, scaledmax;
340 min = GetHIDElementLongValue(element, kIOHIDElementMinKey);
341 max = GetHIDElementLongValue(element, kIOHIDElementMaxKey);
342 isRelative = GetHIDElementBooleanValue(element, kIOHIDElementIsRelativeKey);
343 isWrapping = GetHIDElementBooleanValue(element, kIOHIDElementIsWrappingKey);
344 isNonLinear = GetHIDElementBooleanValue(element, kIOHIDElementIsNonLinearKey);
345 cout << "isRelative=" << isRelative << ", isWrapping=" << isWrapping << ", isNonLinear=" << isNonLinear << endl;
347 name = ((isRelative == true) ? "rel-" : "abs-") + name;
349 center = min + (max - min) / 2;
350 SG_LOG(SG_INPUT, SG_DEBUG, "HID Axis Element; " << name << " min: " << min << " max:" << max << " center: " << center);
353 float AxisElement::readStatus(IOHIDDeviceInterface **interface)
356 value = HIDElement::readStatus(interface);
360 value = (value - (float)center) / (float)(max - center);
361 if (fabs(value) < dead_band)
369 ButtonElement::ButtonElement(CFDictionaryRef element, long page, long usage) :
370 HIDElement(element, page, usage)
374 ss << (page == kHIDPage_KeyboardOrKeypad ? "keyboard-" : "button-") << usage;
379 HatElement::HatElement(CFDictionaryRef element, long page, long usage, int id) :
380 HIDElement(element, page, usage), id(id)
382 min = GetHIDElementLongValue(element, kIOHIDElementMinKey);
383 max = GetHIDElementLongValue(element, kIOHIDElementMaxKey);
387 void HatElement::generateEvent(FGMacOSXInputDevice *device, double dt, int modifiers)
389 // Hat Value is from 0 to 8, representing:
390 // 0:N, 1:NE, 2:E, 3:SE, 4:S, 5:SW, 6:W, 7:NW, 8:N
391 static float xvalues[] = {0, 1, 1, 1, 0, -1, -1, -1, 0};
392 static float yvalues[] = {1, 1, 0, -1, -1, -1, 0, 1, 0};
395 ss << "abs-hat" << id << "-x";
397 SG_LOG(SG_INPUT, SG_BULK, "Generating Input Event: " << eventName << "=" << xvalues[(int)value]);
398 FGMacOSXEventData eventDataX(eventName, xvalues[(int)value], dt, modifiers);
399 ss << "abs-hat" << id << "-y";
401 SG_LOG(SG_INPUT, SG_BULK, "Generating Input Event: " << eventName << "=" << yvalues[(int)value]);
402 FGMacOSXEventData eventDataY(eventName, yvalues[(int)value], dt, modifiers);
403 device->HandleEvent((FGEventData &)eventDataX);
404 device->HandleEvent((FGEventData &)eventDataY);
408 LEDElement::LEDElement(CFDictionaryRef element, long page, long usage) :
409 HIDElement(element, page, usage)
413 ss << "led-" << usage;
418 void LEDElement::write(IOHIDDeviceInterface **interface, double value) {
419 IOHIDEventStruct event = (IOHIDEventStruct){kIOHIDElementTypeOutput, cookie, 0, {0}, 0, 0};
421 (*interface)->setElementValue(interface, cookie, &event, 0, NULL, NULL, NULL);
425 // This is just for testing....
427 FeatureElement::FeatureElement(CFDictionaryRef element, long page, long usage, int count=1) :
428 HIDElement(element, page, usage)
432 ss << "feature-" << usage;
439 float FeatureElement::readStatus(IOHIDDeviceInterface **interface) {
440 IOHIDEventStruct event;
441 IOReturn ret = (*interface)->queryElementValue(interface, cookie, &event, 0, NULL, NULL, NULL);
442 if (ret != kIOReturnSuccess) {
443 ret = (*interface)->getElementValue(interface, cookie, &event);
444 if (ret != kIOReturnSuccess) {
445 SG_LOG(SG_INPUT, SG_ALERT, "Can't get element value for feature element: " << getName());
449 cout << getName() << "=" << event.value << endl;
456 void HIDElementFactory::create(CFTypeRef element, FGMacOSXInputDevice *inputDevice)
458 assert(CFGetTypeID(element) == CFArrayGetTypeID());
459 CFRange range = {0, CFArrayGetCount((CFArrayRef)element)};
460 CFArrayApplyFunction((CFArrayRef) element, range,
461 HIDElementFactory::elementEnumerator, (void *)inputDevice);
465 void HIDElementFactory::elementEnumerator( const void *element, void *inputDevice) {
466 if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
467 SG_LOG(SG_INPUT, SG_WARN, "Element Enumerator passed non-dictionary value.");
469 HIDElementFactory::parseElement((CFDictionaryRef)element, (FGMacOSXInputDevice *)inputDevice);
473 void HIDElementFactory::parseElement(CFDictionaryRef element, FGMacOSXInputDevice *inputDevice) {
474 long page = GetHIDElementLongValue(element, kIOHIDElementUsagePageKey);
475 long usage = GetHIDElementLongValue(element, kIOHIDElementUsageKey);
478 static map<FGMacOSXInputDevice *, map<long, unsigned> > elementCount;
480 long type = GetHIDElementLongValue(element, kIOHIDElementTypeKey);
482 if (type == kIOHIDElementTypeCollection) {
484 cout << "Collection: " << hidTypeByID.getName(type) << "(" << type << ")"
485 <<":" << hidPageByID.getName(page) << "(" << page << ")"
486 << ":" << hidUsageByID.getName(USAGE_KEY(page, usage)) << "(" << usage << ")" << endl;
487 HIDElementFactory::create(CFDictionaryGetValue(element, CFSTR(kIOHIDElementKey)), inputDevice);
489 HIDUsageType usageType = hidUsageByID.getType(USAGE_KEY(page, usage));
490 // FIXME: Any other elegant way for counting the same usage on a device?
491 // This is mainly needed for feature / hat elements to avoid assigning the sane event name.
492 elementCount[inputDevice][USAGE_KEY(page, usage)] += 1;
496 inputDevice->addElement(new AxisElement(element, page, usage));
500 inputDevice->addElement(new HIDElement(element, page, usage));
503 inputDevice->addElement(new HatElement(element, page, usage, elementCount[inputDevice][USAGE_KEY(page, usage)]));
510 inputDevice->addElement(new ButtonElement(element, page, usage));
514 if (page == kHIDPage_Button || type == kIOHIDElementTypeInput_Button && usage > 0) {
515 // FIXME: most of KeyboardOrKeypad elements should be treated as Selector type, not as Button...
516 inputDevice->addElement(new ButtonElement(element, page, usage));
517 } else if (page == kHIDPage_LEDs && usage > 0) {
518 inputDevice->addElement(new LEDElement(element, page, usage));
520 } else if (type == kIOHIDElementTypeFeature) {
521 // just for testing feature elements
522 inputDevice->addElement(new FeatureElement(element, page, usage, elementCount[inputDevice][USAGE_KEY(page, usage)]));
525 cout << "HID Element Page/Usage is not supported: type=" << hidTypeByID.getName(type) << "(" << type << ")"
526 << ", page=" << hidPageByID.getName(page) << "(" << page << ")" << ", usage=" << usage << endl;
533 // FGMacOSXInputDevice implementation
536 FGMacOSXInputDevice::FGMacOSXInputDevice(io_object_t device) : device(device), devInterface(NULL)
538 CFDictionaryRef properties = getProperties();
539 string deviceName = GetHIDElementStringValue(properties, kIOHIDProductKey);
540 if (deviceName == "") {
541 deviceName = GetHIDElementStringValue(properties, "USB Product Name");
545 CFRelease(properties);
548 const char *FGMacOSXInputDevice::TranslateEventName(FGEventData &eventData)
550 FGMacOSXEventData &macEvent = (FGMacOSXEventData &)eventData;
551 return macEvent.name.c_str();
555 void FGMacOSXInputDevice::Send(const char *eventName, double value)
557 HIDElement *element = elements[eventName];
559 element->write(devInterface, value);
561 cout << "No element to handle event: " << eventName << endl;
566 CFDictionaryRef FGMacOSXInputDevice::getProperties(io_object_t device)
569 CFMutableDictionaryRef properties;
571 ret = IORegistryEntryCreateCFProperties( device, &properties, kCFAllocatorDefault, kNilOptions);
572 if (ret != kIOReturnSuccess || !properties) {
573 SG_LOG(SG_INPUT, SG_WARN, "Error getting device properties.");
581 void FGMacOSXInputDevice::addElement(HIDElement *element)
583 elements[element->getName()] = element;
584 int count = elements.size();
585 SG_LOG(SG_INPUT, SG_DEBUG, "adding element " << count << ":" << element->getName());
588 void FGMacOSXInputDevice::Open() {
589 // create device interface
592 IOCFPlugInInterface **plugin;
593 SG_LOG(SG_INPUT, SG_INFO, "Opening HID : " << GetName());
595 ret = IOCreatePlugInInterfaceForService(device,
596 kIOHIDDeviceUserClientTypeID,
597 kIOCFPlugInInterfaceID,
600 if (ret != kIOReturnSuccess) {
601 SG_LOG(SG_INPUT, SG_ALERT, "Error creating a plugin for HID : " << GetName());
606 HRESULT result = (*plugin)->QueryInterface(plugin,
607 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
608 (LPVOID*)&devInterface );
611 SG_LOG(SG_INPUT, SG_ALERT, "Failed Querying HID plugin interface: " << GetName());
613 (*plugin)->Release(plugin); // don't leak a ref
614 if (devInterface == NULL) {
619 // store the interface in this instance
620 ret = (*devInterface)->open(devInterface, 0);
621 if (ret != kIOReturnSuccess) {
622 SG_LOG(SG_INPUT, SG_ALERT, "Error opening device interface: " << GetName());
626 CFDictionaryRef props = getProperties();
628 // recursively enumerate all the bits (buttons, axes, hats, ...)
629 CFTypeRef topLevelElement =
630 CFDictionaryGetValue (props, CFSTR(kIOHIDElementKey));
631 HIDElementFactory::create(topLevelElement, this);
635 void FGMacOSXInputDevice::Close() {
636 SG_LOG(SG_INPUT, SG_INFO, "Closing HID: " << GetName());
638 (*devInterface)->close(devInterface);
641 map<string, HIDElement *>::iterator it;
642 for (it = elements.begin(); it != elements.end(); it++) {
649 void FGMacOSXInputDevice::update(double dt)
651 map<string, HIDElement *>::iterator it;
652 for (it = elements.begin(); it != elements.end(); it++) {
653 (*it).second->readStatus(devInterface);
654 if ((*it).second->isUpdated()) {
655 int modifiers = fgGetKeyModifiers();
656 (*it).second->generateEvent(this, dt, modifiers);
662 // FGMacOSXEventInput implementation
664 FGMacOSXEventInput *FGMacOSXEventInput::_instance=NULL;
665 FGMacOSXEventInput &FGMacOSXEventInput::instance()
667 if (!FGMacOSXEventInput::_instance) {
668 SG_LOG(SG_INPUT, SG_ALERT, "FGMacOSXEventInput is not created but its instance is referred.");
670 return *FGMacOSXEventInput::_instance;
673 FGMacOSXEventInput::~FGMacOSXEventInput() {
675 // inputDevice will be deleted in FGEventInput
676 map<io_object_t, FGMacOSXInputDevice *>::iterator deviceIterator;
677 for (deviceIterator = inputDevices.begin(); deviceIterator != inputDevices.end(); deviceIterator++) {
678 FGMacOSXInputDevice *inputDevice = (*deviceIterator).second;
679 // inputDevice->Close();
680 // delete inputDevice;
683 deviceIndices.clear();
686 void FGMacOSXEventInput::init()
689 SG_LOG(SG_INPUT, SG_INFO, "initializing FGMacOSXEventInput");
691 // We want all HID devices for matching
692 CFMutableDictionaryRef matchingDictionary = IOServiceMatching(kIOHIDDeviceKey);
694 // Needs to retain machingDict since IOServiceAddMatchingNotification consumes one reference.
695 matchingDictionary = (CFMutableDictionaryRef) CFRetain(matchingDictionary);
696 matchingDictionary = (CFMutableDictionaryRef) CFRetain(matchingDictionary);
698 notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
699 runLoopSource = IONotificationPortGetRunLoopSource(notifyPort);
700 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
701 ret = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification,
702 matchingDictionary, FGMacOSXEventInput::deviceAttached, this, &addedIterator);
703 ret = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification,
704 matchingDictionary, FGMacOSXEventInput::deviceDetached, this, &removedIterator);
706 // prepare for notification by calling these callback funcs to remove existing HID device iterators
707 FGMacOSXEventInput::deviceAttached(NULL, addedIterator);
708 FGMacOSXEventInput::deviceDetached(NULL, removedIterator);
712 void FGMacOSXEventInput::attachDevice(io_iterator_t iterator)
716 while ((device = IOIteratorNext(iterator))) {
717 FGMacOSXInputDevice *inputDevice = new FGMacOSXInputDevice(device);
719 SG_LOG(SG_INPUT, SG_INFO, "HID Device Atached: " << inputDevice->GetName());
720 unsigned index = AddDevice(inputDevice);
721 // Needs to check if AddDevice closed the device due to lack to config file
722 if (index != FGEventInput::INVALID_DEVICE_INDEX) {
723 deviceIndices[device] = index;
726 IOObjectRelease(device);
731 void FGMacOSXEventInput::detachDevice(io_iterator_t iterator)
735 while ((device = IOIteratorNext(iterator))) {
736 unsigned index = deviceIndices[device];
737 if (index != FGEventInput::INVALID_DEVICE_INDEX) {
738 FGMacOSXInputDevice *inputDevice = (FGMacOSXInputDevice *)input_devices[index];
739 SG_LOG(SG_INPUT, SG_INFO, "HID Device Detached: " << inputDevice->GetName());
741 deviceIndices.erase(device);
743 SG_LOG(SG_INPUT, SG_INFO, "Device ID unmatched: " << (int)device << " No HID deivce is detached since it is not supported by FG.");
745 IOObjectRelease(device);
749 void FGMacOSXEventInput::update(double dt)
751 FGEventInput::update(dt);
753 map<int, FGInputDevice*>::const_iterator it;
754 for (it = input_devices.begin(); it != input_devices.end(); it++) {
756 FGMacOSXInputDevice *inputDevice = (FGMacOSXInputDevice *)((*it).second);
757 inputDevice->update(dt);