]> git.mxchange.org Git - flightgear.git/blob - src/Input/FGMacOSXEventInput.cxx
MSVC9 fix
[flightgear.git] / src / Input / FGMacOSXEventInput.cxx
1 // FGMacOSXEventInput.cxx -- handle event driven input devices for Mac OS X
2 //
3 // Written by Tatsuhiro Nishioka, started Aug. 2009.
4 //
5 // Copyright (C) 2009 Tasuhiro Nishioka, tat <dot> fgmacosx <at> gmail <dot> com
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23 #include "FGMacOSXEventInput.hxx"
24
25
26 #define GetHIDElementLongValue(element, key) ({ \
27       long value = 0; \
28       CFNumberRef refType = (CFNumberRef)CFDictionaryGetValue(element, CFSTR(key)); \
29       if (refType) { CFNumberGetValue(refType, kCFNumberLongType, &value); } \
30       value; })
31
32 #define GetHIDElementBooleanValue(element, key) ({ \
33       bool value = 0; \
34       CFBooleanRef refType = (CFBooleanRef)CFDictionaryGetValue(element, CFSTR(key)); \
35       if (refType) { value = CFBooleanGetValue(refType); } \
36       value; })
37
38 #define GetHIDElementStringValue(element, key) ({ \
39       const char *buf = ""; \
40       CFStringRef refType = (CFStringRef)CFDictionaryGetValue(element, CFSTR(key)); \
41       if (refType) { \
42         buf = CFStringGetCStringPtr(refType, CFStringGetSystemEncoding());  \
43        } \
44       buf; })
45
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, ""}
56 };
57
58 // HID Element Pages (log / debug use)
59 struct HIDTypes HID_PAGE_TABLE[] = {
60   // Page
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, ""}
77 };
78
79 #define USAGE_KEY(PAGE,USAGE) (PAGE << 16 | USAGE)
80 #define GET_PAGE(KEY)  (KEY >> 16)
81 #define GET_USAGE(KEY) (KEY & 0xFFFF)
82
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)
94
95 // HID Element Usage <-> FGEventData convertion data
96 // See http://www.usb.org/developers/devclass_docs/Hut1_12.pdf for detail on HID pages and usages
97 // Note 
98 // kHIDUsageAxis is FG specific type of DV since it is needed to be normalized into -1.0 to 1.0
99 // kHIDUsageHat also is FG specific type of DV. it's value is converted into two axis events
100 struct HIDTypes HID_USAGE_TABLE[] = {
101   // Generic Desktop Page
102   {GD_USAGE(kHIDUsage_GD_X), kHIDUsageAxis, "x-translate"},
103   {GD_USAGE(kHIDUsage_GD_Y), kHIDUsageAxis, "y-translate"},
104   {GD_USAGE(kHIDUsage_GD_Z), kHIDUsageAxis, "z-translate"},  
105   {GD_USAGE(kHIDUsage_GD_Rx), kHIDUsageAxis, "x-rotate"},  
106   {GD_USAGE(kHIDUsage_GD_Ry), kHIDUsageAxis, "y-rotate"},
107   {GD_USAGE(kHIDUsage_GD_Rz), kHIDUsageAxis, "z-rotate"},
108   {GD_USAGE(kHIDUsage_GD_Slider), kHIDUsageAxis, "slider"},
109   {GD_USAGE(kHIDUsage_GD_Dial), kHIDUsageAxis, "dial"},
110   {GD_USAGE(kHIDUsage_GD_Wheel), kHIDUsageAxis, "wheel"},
111   {GD_USAGE(kHIDUsage_GD_Hatswitch), kHIDUsageHat, "hat"},
112   
113   {GD_USAGE(kHIDUsage_GD_CountedBuffer), kHIDUsageNotSupported, "counted-buffer"},
114   {GD_USAGE(kHIDUsage_GD_ByteCount), kHIDUsageNotSupported, "byte-count"},
115   {GD_USAGE(kHIDUsage_GD_MotionWakeup), kHIDUsageDF, "motion-wakeup"},
116   {GD_USAGE(kHIDUsage_GD_Start), kHIDUsageOOC, "button-start"},
117   {GD_USAGE(kHIDUsage_GD_Select), kHIDUsageOOC, "button-select"},
118   {GD_USAGE(kHIDUsage_GD_Vx), kHIDUsageAxis, "x-vector"},
119   {GD_USAGE(kHIDUsage_GD_Vy), kHIDUsageAxis, "y-vector"},
120   {GD_USAGE(kHIDUsage_GD_Vz), kHIDUsageAxis, "z-vector"},
121   {GD_USAGE(kHIDUsage_GD_Vbrx), kHIDUsageAxis, "x-rel-vector"},
122   {GD_USAGE(kHIDUsage_GD_Vbry), kHIDUsageAxis, "y-rel-vector"},
123   {GD_USAGE(kHIDUsage_GD_Vbrz), kHIDUsageAxis, "z-rel-vector"},
124   {GD_USAGE(kHIDUsage_GD_Vno),  kHIDUsageAxis, "no-vector"},
125
126   {GD_USAGE(kHIDUsage_GD_SystemPowerDown), kHIDUsageOSC, "button-system-power-down"},
127   {GD_USAGE(kHIDUsage_GD_SystemSleep), kHIDUsageOSC, "button-system-sleep"},
128   {GD_USAGE(kHIDUsage_GD_SystemWakeUp), kHIDUsageOSC, "button-system-wake-up"},
129   {GD_USAGE(kHIDUsage_GD_SystemContextMenu), kHIDUsageOSC, "button-system-context-menu"},
130   {GD_USAGE(kHIDUsage_GD_SystemMainMenu), kHIDUsageOSC, "button-system-main-menu"},
131   {GD_USAGE(kHIDUsage_GD_SystemAppMenu), kHIDUsageOSC, "button-system-app-menu"},
132   {GD_USAGE(kHIDUsage_GD_SystemMenuHelp), kHIDUsageOSC, "button-system-menu-help"},
133   {GD_USAGE(kHIDUsage_GD_SystemMenuExit), kHIDUsageOSC, "button-system-menu-exit"},
134   {GD_USAGE(kHIDUsage_GD_SystemMenu), kHIDUsageOSC, "button-system-menu"},
135   {GD_USAGE(kHIDUsage_GD_SystemMenuRight), kHIDUsageRTC, "button-system-menu-right"},
136   {GD_USAGE(kHIDUsage_GD_SystemMenuLeft), kHIDUsageRTC, "button-system-menu-left"},
137   {GD_USAGE(kHIDUsage_GD_SystemMenuUp), kHIDUsageRTC, "button-system-menu-up"},
138   {GD_USAGE(kHIDUsage_GD_SystemMenuDown), kHIDUsageRTC, "button-system-menu-down"},
139   {GD_USAGE(kHIDUsage_GD_DPadUp), kHIDUsageOOC, "dpad-up"},
140   {GD_USAGE(kHIDUsage_GD_DPadDown), kHIDUsageOOC, "dpad-down"},
141   {GD_USAGE(kHIDUsage_GD_DPadRight), kHIDUsageOOC, "dpad-right"},
142   {GD_USAGE(kHIDUsage_GD_DPadLeft), kHIDUsageOOC, "dpad-left"},
143
144   // Game Controls Page
145   {GAME_USAGE(kHIDUsage_Game_TurnRightOrLeft), kHIDUsageAxis, "turn"},
146   {GAME_USAGE(kHIDUsage_Game_PitchUpOrDown), kHIDUsageAxis, "pitch"},
147   {GAME_USAGE(kHIDUsage_Game_MoveRightOrLeft), kHIDUsageAxis, "x-move"},
148   {GAME_USAGE(kHIDUsage_Game_MoveForwardOrBackward), kHIDUsageAxis, "y-move"},
149   {GAME_USAGE(kHIDUsage_Game_MoveUpOrDown), kHIDUsageAxis, "z-move"},
150   {GAME_USAGE(kHIDUsage_Game_LeanRightOrLeft), kHIDUsageAxis, "x-lean"},
151   {GAME_USAGE(kHIDUsage_Game_LeanForwardOrBackward), kHIDUsageAxis, "z-lean"},
152
153   // General Control Devices Page
154   {GDC_USAGE(0x20), kHIDUsageDV, "battery-strength"},
155   {GDC_USAGE(0x21), kHIDUsageDV, "wireless-channel"},
156   {GDC_USAGE(0x22), kHIDUsageDV, "wireless-id"},
157   {GDC_USAGE(0x23), kHIDUsageDV, "discover-wireless-control"},
158   {GDC_USAGE(0x24), kHIDUsageOSC, "security-code-character-entered"},
159   {GDC_USAGE(0x25), kHIDUsageOSC, "security-code-character-erased"},
160   {GDC_USAGE(0x26), kHIDUsageOSC, "security-code-cleared"},
161
162   // Simulation Controls Page
163   {SIM_USAGE(kHIDUsage_Sim_Aileron), kHIDUsageAxis, "aileron"},
164   {SIM_USAGE(kHIDUsage_Sim_AileronTrim), kHIDUsageAxis, "aileron-trim"},
165   {SIM_USAGE(kHIDUsage_Sim_AntiTorqueControl), kHIDUsageAxis, "anti-torque-control"},
166   {SIM_USAGE(kHIDUsage_Sim_AutopilotEnable), kHIDUsageOOC, "button-autopilot-enable"},
167   {SIM_USAGE(kHIDUsage_Sim_ChaffRelease), kHIDUsageOSC, "button-chaff-release"},
168   {SIM_USAGE(kHIDUsage_Sim_CollectiveControl), kHIDUsageAxis, "collective-control"},
169   {SIM_USAGE(kHIDUsage_Sim_DiveBrake), kHIDUsageAxis, "dive-brake"},
170   {SIM_USAGE(kHIDUsage_Sim_ElectronicCountermeasures), kHIDUsageOOC, "electronic-countermeasures"}, // OOC
171   {SIM_USAGE(kHIDUsage_Sim_Elevator), kHIDUsageAxis, "elevator"},
172   {SIM_USAGE(kHIDUsage_Sim_ElevatorTrim), kHIDUsageAxis, "elevator-trim"},
173   {SIM_USAGE(kHIDUsage_Sim_Rudder), kHIDUsageAxis, "rudder"},
174   {SIM_USAGE(kHIDUsage_Sim_Throttle), kHIDUsageAxis, "throttle"},
175   {SIM_USAGE(kHIDUsage_Sim_FlightCommunications), kHIDUsageOOC, "button-flight-communications"}, // OOC
176   {SIM_USAGE(kHIDUsage_Sim_FlareRelease), kHIDUsageOSC, "button-flare-release"},
177   {SIM_USAGE(kHIDUsage_Sim_LandingGear), kHIDUsageOOC, "button-landing-gear"}, // OOC
178   {SIM_USAGE(kHIDUsage_Sim_ToeBrake), kHIDUsageAxis, "toe-brake"},
179   {SIM_USAGE(kHIDUsage_Sim_Trigger), kHIDUsageMC, "button-trigger"},
180   {SIM_USAGE(kHIDUsage_Sim_WeaponsArm), kHIDUsageOOC, "button-weapons-arm"}, // OOC
181   {SIM_USAGE(kHIDUsage_Sim_Weapons), kHIDUsageOSC, "button-weapons"},
182   {SIM_USAGE(kHIDUsage_Sim_WingFlaps), kHIDUsageAxis, "wing-flaps"},  // DV
183   {SIM_USAGE(kHIDUsage_Sim_Accelerator), kHIDUsageAxis, "accelerator"}, // DV
184   {SIM_USAGE(kHIDUsage_Sim_Brake), kHIDUsageAxis, "brake"}, // DV
185   {SIM_USAGE(kHIDUsage_Sim_Clutch), kHIDUsageAxis, "clutch"}, // DV
186   {SIM_USAGE(kHIDUsage_Sim_Shifter), kHIDUsageAxis, "shifter"}, // DV
187   {SIM_USAGE(kHIDUsage_Sim_Steering), kHIDUsageAxis, "steering"}, // DV
188   {SIM_USAGE(kHIDUsage_Sim_TurretDirection), kHIDUsageAxis, "turret-direction"}, // DV
189   {SIM_USAGE(kHIDUsage_Sim_BarrelElevation), kHIDUsageAxis, "barrel-elevation"}, // DV
190   {SIM_USAGE(kHIDUsage_Sim_DivePlane), kHIDUsageAxis, "dive-plane"}, // DV
191   {SIM_USAGE(kHIDUsage_Sim_Ballast), kHIDUsageAxis, "ballast"}, // DV
192   {SIM_USAGE(kHIDUsage_Sim_BicycleCrank), kHIDUsageAxis, "bicycle-crank"}, // DV
193   {SIM_USAGE(kHIDUsage_Sim_HandleBars), kHIDUsageAxis, "handle-bars"}, // DV
194   {SIM_USAGE(kHIDUsage_Sim_FrontBrake), kHIDUsageAxis, "front-brake"}, // DV
195   {SIM_USAGE(kHIDUsage_Sim_RearBrake), kHIDUsageAxis, "rear-brake"}, // DV
196
197   // Digitizer Controls Page
198   {DIG_USAGE(kHIDUsage_Dig_TipPressure), kHIDUsageAxis, "tip-pressure"}, // DV
199   {DIG_USAGE(kHIDUsage_Dig_BarrelPressure), kHIDUsageAxis, "barrel-pressure"}, // DV
200   {DIG_USAGE(kHIDUsage_Dig_InRange), kHIDUsageMC, "in-range"}, // MC
201   {DIG_USAGE(kHIDUsage_Dig_Touch), kHIDUsageMC, "touch"}, // MC
202   {DIG_USAGE(kHIDUsage_Dig_Untouch), kHIDUsageOSC, "button-untouch"}, // OSC
203   {DIG_USAGE(kHIDUsage_Dig_Tap), kHIDUsageOSC, "button-tap"}, // OSC
204   {DIG_USAGE(kHIDUsage_Dig_Quality), kHIDUsageDV, "quality"}, // DV
205   {DIG_USAGE(kHIDUsage_Dig_DataValid), kHIDUsageDV, "button-data-valid"}, // MC
206   {DIG_USAGE(kHIDUsage_Dig_TransducerIndex), kHIDUsageDV, "transducer-index"}, // DV
207   {DIG_USAGE(kHIDUsage_Dig_BatteryStrength), kHIDUsageDV, "battery-strength"}, // DV
208   {DIG_USAGE(kHIDUsage_Dig_Invert), kHIDUsageMC, "invert"}, // MC
209   {DIG_USAGE(kHIDUsage_Dig_XTilt), kHIDUsageAxis, "x-tilt"}, // DV
210   {DIG_USAGE(kHIDUsage_Dig_YTilt), kHIDUsageAxis, "y-tilt"}, // DV
211   {DIG_USAGE(kHIDUsage_Dig_Azimuth), kHIDUsageAxis, "azimuth"}, // DV
212   {DIG_USAGE(kHIDUsage_Dig_Altitude), kHIDUsageAxis, "altitude"}, // DV
213   {DIG_USAGE(kHIDUsage_Dig_Twist), kHIDUsageAxis, "twist"}, // DV
214   {DIG_USAGE(kHIDUsage_Dig_TipSwitch), kHIDUsageMC, "button-tipswitch"}, // MC
215   {DIG_USAGE(kHIDUsage_Dig_SecondaryTipSwitch), kHIDUsageMC, "button-secondary-tipswitch"}, // MC
216   {DIG_USAGE(kHIDUsage_Dig_BarrelSwitch), kHIDUsageMC, "button-barrelswitch"}, // MC
217   {DIG_USAGE(kHIDUsage_Dig_Eraser), kHIDUsageMC, "eraser"}, // MC
218   {DIG_USAGE(kHIDUsage_Dig_TabletPick), kHIDUsageMC, "table-pick"}, // MC
219
220  // Consumer Page
221   {CON_USAGE(kHIDUsage_Csmr_Plus10), kHIDUsageOSC, "plus10"},
222   {CON_USAGE(kHIDUsage_Csmr_Plus100), kHIDUsageOSC, "plus100"},
223   {CON_USAGE(kHIDUsage_Csmr_AMOrPM), kHIDUsageOSC, "am-pm"},
224   {CON_USAGE(kHIDUsage_Csmr_Power), kHIDUsageOOC, "power"},
225   {CON_USAGE(kHIDUsage_Csmr_Reset), kHIDUsageOSC, "reset"},
226   {CON_USAGE(kHIDUsage_Csmr_Sleep), kHIDUsageOSC, "sleep"},
227   {CON_USAGE(kHIDUsage_Csmr_SleepAfter), kHIDUsageOSC, "sleep-after"},
228   {CON_USAGE(kHIDUsage_Csmr_SleepMode), kHIDUsageRTC, "sleep-mode"},
229   {CON_USAGE(kHIDUsage_Csmr_Illumination), kHIDUsageOOC, "illumination"},
230   {CON_USAGE(kHIDUsage_Csmr_Menu), kHIDUsageOOC, "menu"},
231   {CON_USAGE(kHIDUsage_Csmr_MenuPick), kHIDUsageOSC, "menu-pick"},
232   {CON_USAGE(kHIDUsage_Csmr_MenuUp), kHIDUsageOSC, "menu-up"},
233   {CON_USAGE(kHIDUsage_Csmr_MenuDown), kHIDUsageOSC, "menu-down"},
234   {CON_USAGE(kHIDUsage_Csmr_MenuLeft), kHIDUsageOSC, "menu-left"},
235   {CON_USAGE(kHIDUsage_Csmr_MenuRight), kHIDUsageOSC, "menu-right"},
236   {CON_USAGE(kHIDUsage_Csmr_MenuEscape), kHIDUsageOSC, "menu-escape"},
237   {CON_USAGE(kHIDUsage_Csmr_MenuValueIncrease), kHIDUsageOSC, "menu-value-increase"},
238   {CON_USAGE(kHIDUsage_Csmr_MenuValueDecrease), kHIDUsageOSC, "menu-value-decrease"},
239   {CON_USAGE(kHIDUsage_Csmr_DataOnScreen), kHIDUsageOOC, "data-on-screen"},
240   {CON_USAGE(kHIDUsage_Csmr_ClosedCaption), kHIDUsageOOC, "closed-caption"},
241   {CON_USAGE(kHIDUsage_Csmr_ClosedCaptionSelect), kHIDUsageSel, "closed-caption-select"},
242   {CON_USAGE(kHIDUsage_Csmr_VCROrTV), kHIDUsageOOC, "vcr-tv"},
243   {CON_USAGE(kHIDUsage_Csmr_BroadcastMode), kHIDUsageOSC, "broadcast-mode"},
244   {CON_USAGE(kHIDUsage_Csmr_Snapshot), kHIDUsageOSC, "snapshot"},
245   {CON_USAGE(kHIDUsage_Csmr_Still), kHIDUsageOSC, "still"},
246   {CON_USAGE(kHIDUsage_Csmr_Assign), kHIDUsageOSC, "assign"},
247   {CON_USAGE(kHIDUsage_Csmr_ModeStep), kHIDUsageOSC, "mode-step"},
248   {CON_USAGE(kHIDUsage_Csmr_RecallLast), kHIDUsageOSC, "recall-last"},
249   {CON_USAGE(kHIDUsage_Csmr_EnterChannel), kHIDUsageOSC, "enter-channel"},
250   {CON_USAGE(kHIDUsage_Csmr_OrderMovie), kHIDUsageOSC, "order-movie"},
251   {CON_USAGE(kHIDUsage_Csmr_Channel), kHIDUsageDV, "channel"}, // LC
252   {CON_USAGE(kHIDUsage_Csmr_MediaSelection), kHIDUsageSel, "media-selection"},
253   {CON_USAGE(kHIDUsage_Csmr_MediaSelectComputer), kHIDUsageSel, "media-select-computer"},
254   {CON_USAGE(kHIDUsage_Csmr_MediaSelectTV), kHIDUsageSel, "media-select-tv"},
255   {CON_USAGE(kHIDUsage_Csmr_MediaSelectWWW), kHIDUsageSel, "media-seleci-www"},
256   {CON_USAGE(kHIDUsage_Csmr_MediaSelectDVD), kHIDUsageSel, "media-select-dvd"},
257   {CON_USAGE(kHIDUsage_Csmr_MediaSelectTelephone), kHIDUsageSel, "media-select-telephone"},
258   {CON_USAGE(kHIDUsage_Csmr_MediaSelectProgramGuide), kHIDUsageSel, "media-select-programguide"},
259   {CON_USAGE(kHIDUsage_Csmr_MediaSelectVideoPhone), kHIDUsageSel, "media-select-videophone"},
260   {CON_USAGE(kHIDUsage_Csmr_MediaSelectGames), kHIDUsageSel, "media-select-games"},
261   {CON_USAGE(kHIDUsage_Csmr_MediaSelectMessages), kHIDUsageSel, "media-select-messages"},
262   {CON_USAGE(kHIDUsage_Csmr_MediaSelectCD), kHIDUsageSel, "media-select-cd"},
263   {CON_USAGE(kHIDUsage_Csmr_MediaSelectVCR), kHIDUsageSel, "media-select-vcr"},
264   {CON_USAGE(kHIDUsage_Csmr_MediaSelectTuner), kHIDUsageOSC, "media-select-tuner"},
265   {CON_USAGE(kHIDUsage_Csmr_Quit), kHIDUsageOSC, "quit"},
266   {CON_USAGE(kHIDUsage_Csmr_Help), kHIDUsageOOC, "help"},
267   {CON_USAGE(kHIDUsage_Csmr_MediaSelectTape), kHIDUsageSel, "media-select-tape"},
268   {CON_USAGE(kHIDUsage_Csmr_MediaSelectCable), kHIDUsageSel, "media-select-cable"},
269   {CON_USAGE(kHIDUsage_Csmr_MediaSelectSatellite), kHIDUsageSel, "media-select-satellite"},
270   {CON_USAGE(kHIDUsage_Csmr_MediaSelectSecurity), kHIDUsageSel, "media-select-security"},
271   {CON_USAGE(kHIDUsage_Csmr_MediaSelectHome), kHIDUsageSel, "media-select-home"},
272   {CON_USAGE(kHIDUsage_Csmr_MediaSelectCall), kHIDUsageSel, "media-select-call"},
273   {CON_USAGE(kHIDUsage_Csmr_ChannelIncrement), kHIDUsageOSC, "channel-increment"},
274   {CON_USAGE(kHIDUsage_Csmr_ChannelDecrement), kHIDUsageOSC, "channel-decrement"},
275   {CON_USAGE(kHIDUsage_Csmr_Media), kHIDUsageSel, "media"},
276   {CON_USAGE(kHIDUsage_Csmr_VCRPlus), kHIDUsageOSC, "vcr-plus"},
277   {CON_USAGE(kHIDUsage_Csmr_Once), kHIDUsageOSC, "once"},
278   {CON_USAGE(kHIDUsage_Csmr_Daily), kHIDUsageOSC, "daily"},
279   {CON_USAGE(kHIDUsage_Csmr_Weekly), kHIDUsageOSC, "weekly"},
280   {CON_USAGE(kHIDUsage_Csmr_Monthly), kHIDUsageOSC, "monthly"},
281   {CON_USAGE(kHIDUsage_Csmr_Play), kHIDUsageOOC, "play"},
282   {CON_USAGE(kHIDUsage_Csmr_Pause), kHIDUsageOOC, "pause"},
283   {CON_USAGE(kHIDUsage_Csmr_Record), kHIDUsageOOC, "record"},
284   {CON_USAGE(kHIDUsage_Csmr_FastForward), kHIDUsageOOC,"fastforward"},
285   {CON_USAGE(kHIDUsage_Csmr_Rewind), kHIDUsageOOC, "rewind"},
286   {CON_USAGE(kHIDUsage_Csmr_ScanNextTrack), kHIDUsageOSC, "scan-next-track"},
287   {CON_USAGE(kHIDUsage_Csmr_ScanPreviousTrack), kHIDUsageOSC, "scan-previous-track"},
288   {CON_USAGE(kHIDUsage_Csmr_Stop), kHIDUsageOSC, "stop"},
289   {CON_USAGE(kHIDUsage_Csmr_Eject), kHIDUsageOSC, "eject"},
290   {CON_USAGE(kHIDUsage_Csmr_RandomPlay), kHIDUsageOOC, "random-play"},
291   {CON_USAGE(kHIDUsage_Csmr_SelectDisc), kHIDUsageNotSupported, "select-disc"}, //NamedArray
292
293   {CON_USAGE(kHIDUsage_Csmr_VolumeIncrement), kHIDUsageRTC, "volume-increment"},
294   {CON_USAGE(kHIDUsage_Csmr_VolumeDecrement), kHIDUsageRTC, "volume-decrement"},
295   {CON_USAGE(kHIDUsage_Csmr_PlayOrPause), kHIDUsageOSC, "play-pause"},
296   {CON_USAGE(kHIDUsage_Csmr_Mute), kHIDUsageOOC, "mute"},
297   // Too many... and the rest are T.B.D. ;-)
298   {-1, kHIDElementPage, ""},
299 };
300
301 static HIDTypeByID hidTypeByID(HID_TYPE_TABLE);
302 static HIDTypeByID hidPageByID(HID_PAGE_TABLE);
303 static HIDTypeByID hidUsageByID(HID_USAGE_TABLE);
304
305
306 HIDElement::HIDElement(CFDictionaryRef element, long page, long usage) : 
307   page(page), usage(usage), value(0.0), lastValue(0.0) {
308
309   cookie = (IOHIDElementCookie)GetHIDElementLongValue(element, kIOHIDElementCookieKey);
310   name = hidUsageByID.getName(USAGE_KEY(page, usage));
311 }
312
313 long HIDElement::read(IOHIDDeviceInterface **interface)
314 {
315   IOHIDEventStruct event;
316   lastValue = value;
317   IOReturn ret = (*interface)->getElementValue(interface, cookie, &event);
318   if (ret == kIOReturnSuccess) {
319     value = event.value;
320 //    SG_LOG(SG_INPUT, SG_BULK, "Element: " << name << "; value = " << value);
321     return event.value;
322   } else {
323     SG_LOG(SG_INPUT, SG_WARN, "Failed reading value for HID Element: " << name);
324     return 0;
325   }
326 }
327
328 bool HIDElement::isUpdated()
329 {
330   return (value != lastValue);
331 }
332
333 void HIDElement::generateEvent(FGMacOSXInputDevice *device, double dt, int modifiers)
334 {
335   SG_LOG(SG_INPUT, SG_DEBUG, "Generating Input Event: " << name << "=" << value);
336   FGMacOSXEventData eventData(name, (float)value, dt, modifiers);
337   device->HandleEvent(eventData);
338 }
339
340 AxisElement::AxisElement(CFDictionaryRef element, long page, long usage) : 
341   HIDElement(element, page, usage), dead_band(0.00), saturate(1.0)
342 {
343   min = GetHIDElementLongValue(element, kIOHIDElementMinKey); 
344   max = GetHIDElementLongValue(element, kIOHIDElementMaxKey);
345   isRelative = GetHIDElementBooleanValue(element, kIOHIDElementIsRelativeKey);
346   isWrapping = GetHIDElementBooleanValue(element, kIOHIDElementIsWrappingKey);
347   isNonLinear = GetHIDElementBooleanValue(element, kIOHIDElementIsNonLinearKey);
348
349   name = ((isRelative == true) ? "rel-" : "abs-") + name;
350
351   center = min + (max - abs(min)) * 0.5;
352   SG_LOG(SG_INPUT, SG_DEBUG, "HID Axis Element; " << name << " min: " << min << " max:" << max << " center: " << center);
353   SG_LOG(SG_INPUT, SG_DEBUG, "isRelative=" << isRelative << ", isWrapping=" << isWrapping << ", isNonLinear=" << isNonLinear);
354 }
355
356 long AxisElement::read(IOHIDDeviceInterface **interface)
357 {
358   lastValue = value;
359   return HIDElement::read(interface);
360 }
361
362 // FIXME: This can be removed when AxisInputEvent can normalize its value
363 void AxisElement::generateEvent(FGMacOSXInputDevice *device, double dt, int modifiers)
364 {
365   float normalizedValue = (float)value;
366   if (!isRelative) {
367     normalizedValue = (value - (float)center) / (float)(max - center);
368     if (fabs(normalizedValue) < dead_band)
369       normalizedValue = 0.0;
370   }
371
372   SG_LOG(SG_INPUT, SG_DEBUG, "Generating Input Event: " << name << "=" << normalizedValue);
373   FGMacOSXEventData eventData(name, normalizedValue, dt, modifiers);
374   device->HandleEvent(eventData);
375 }
376
377 ButtonElement::ButtonElement(CFDictionaryRef element, long page, long usage) :
378   HIDElement(element, page, usage)
379 {
380   if (name == "") {
381     stringstream ss;
382     ss << (page == kHIDPage_KeyboardOrKeypad ? "keyboard-" : "button-") << usage;
383     ss >> name;
384   }
385 }
386
387 HatElement::HatElement(CFDictionaryRef element, long page, long usage, int id) :
388   HIDElement(element, page, usage), id(id)
389 {
390   min = GetHIDElementLongValue(element, kIOHIDElementMinKey);
391   max = GetHIDElementLongValue(element, kIOHIDElementMaxKey);
392   lastValue = 8;
393 }
394
395 void HatElement::generateEvent(FGMacOSXInputDevice *device, double dt, int modifiers)
396 {
397   // Hat value is from 0 to 8, representing:
398   // 0:N, 1:NE, 2:E, 3:SE, 4:S, 5:SW, 6:W, 7:NW, 8:Neutral
399   // FG can't bind hat directly, so need to convert its value to two axis events
400   static float xvalues[] = {0, 1, 1,  1,  0, -1, -1, -1, 0};
401   static float yvalues[] = {1, 1, 0, -1, -1, -1,  0,  1, 0};
402   stringstream ss1, ss2;
403   ss1 << "abs-hat" << id << "-x";
404   SG_LOG(SG_INPUT, SG_BULK, "Generating Input Event: " << ss1.str() << "=" << xvalues[(int)value]);
405   FGMacOSXEventData eventDataX(ss1.str(), xvalues[(int)value], dt, modifiers);
406   ss2 << "abs-hat" << id << "-y";
407   SG_LOG(SG_INPUT, SG_BULK, "Generating Input Event: " << ss2.str() << "=" << yvalues[(int)value]);
408   FGMacOSXEventData eventDataY(ss2.str(), yvalues[(int)value], dt, modifiers);
409   device->HandleEvent((FGEventData &)eventDataX);
410   device->HandleEvent((FGEventData &)eventDataY);
411 }
412
413
414 LEDElement::LEDElement(CFDictionaryRef element, long page, long usage) :
415   HIDElement(element, page, usage)
416 {
417   stringstream ss;
418   if (name == "") {
419     ss << "led-" << usage;
420     ss >> name;
421   }
422 }
423
424 void LEDElement::write(IOHIDDeviceInterface **interface, double value) {
425   IOHIDEventStruct event = (IOHIDEventStruct){kIOHIDElementTypeOutput, cookie, 0, {0}, 0, 0};
426   event.value = value;
427   (*interface)->setElementValue(interface, cookie, &event, 0, NULL, NULL, NULL);
428 }
429
430 //
431 // This is just for testing....
432 //
433 FeatureElement::FeatureElement(CFDictionaryRef element, long page, long usage, int count=1) :
434   HIDElement(element, page, usage) 
435 {
436   stringstream ss;
437   if (name == "") {
438     ss << "feature-" << usage;
439     if (count > 1) 
440       ss << "-" << count;
441     ss >> name;
442   }
443 }
444
445 long FeatureElement::read(IOHIDDeviceInterface **interface) {
446   IOHIDEventStruct event;
447   IOReturn ret = (*interface)->queryElementValue(interface, cookie, &event, 0, NULL, NULL, NULL);
448   if (ret != kIOReturnSuccess) {
449     ret = (*interface)->getElementValue(interface, cookie, &event);
450     if (ret != kIOReturnSuccess) {
451       SG_LOG(SG_INPUT, SG_WARN, "Can't get element value for feature element: " << getName());
452       return 0;
453     }
454   }
455   return event.value;
456 }
457
458 //
459 // HIDElementFactory
460 //
461 void HIDElementFactory::create(CFTypeRef element, FGMacOSXInputDevice *inputDevice)
462 {
463   assert(CFGetTypeID(element) == CFArrayGetTypeID());
464   CFRange range = {0, CFArrayGetCount((CFArrayRef)element)};
465   CFArrayApplyFunction((CFArrayRef) element, range, 
466                        HIDElementFactory::elementEnumerator, (void *)inputDevice);
467 };
468
469
470 void HIDElementFactory::elementEnumerator( const void *element, void *inputDevice) {
471   if (CFGetTypeID((CFTypeRef) element) != CFDictionaryGetTypeID()) {
472     SG_LOG(SG_INPUT, SG_WARN, "Element Enumerator passed non-dictionary value.");
473   }
474   HIDElementFactory::parseElement((CFDictionaryRef)element, (FGMacOSXInputDevice *)inputDevice);
475 };
476
477
478 void HIDElementFactory::parseElement(CFDictionaryRef element, FGMacOSXInputDevice *inputDevice) {
479   long page = GetHIDElementLongValue(element, kIOHIDElementUsagePageKey);
480   long usage = GetHIDElementLongValue(element, kIOHIDElementUsageKey);
481
482   static int id=0;
483   static map<FGMacOSXInputDevice *, map<long, unsigned> > elementCount;
484
485   long type = GetHIDElementLongValue(element, kIOHIDElementTypeKey);
486
487   if (type == kIOHIDElementTypeCollection) {
488     id = 0;
489     SG_LOG(SG_INPUT, SG_DEBUG, "Collection: " << hidTypeByID.getName(type) << "(" << type << ")" 
490                                               << ":" << hidPageByID.getName(page) << "(" << page << ")" 
491                                               << ":" << hidUsageByID.getName(USAGE_KEY(page, usage)) << "(" << usage << ")");
492     HIDElementFactory::create(CFDictionaryGetValue(element, CFSTR(kIOHIDElementKey)), inputDevice);
493   } else {
494     HIDUsageType usageType = hidUsageByID.getType(USAGE_KEY(page, usage));
495     // FIXME: Any other elegant way for counting the same usage on a device?
496     // This is mainly needed for feature / hat elements to avoid assigning the sane event name.
497     elementCount[inputDevice][USAGE_KEY(page, usage)] += 1;
498     
499     switch (usageType) {
500       case kHIDUsageAxis:
501           inputDevice->addElement(new AxisElement(element, page, usage));
502           break;
503       case kHIDUsageDV:
504       case kHIDUsageDF:
505           inputDevice->addElement(new HIDElement(element, page, usage));
506           break;
507       case kHIDUsageHat:
508           inputDevice->addElement(new HatElement(element, page, usage, elementCount[inputDevice][USAGE_KEY(page, usage)]));
509           break;
510       case kHIDUsageOOC:
511       case kHIDUsageOSC:
512       case kHIDUsageMC:
513       case kHIDUsageRTC:
514           if (usage > 0)
515             inputDevice->addElement(new ButtonElement(element, page, usage));
516           break;
517           
518       default:
519         if (page == kHIDPage_Button || type == kIOHIDElementTypeInput_Button && usage > 0) {
520         // FIXME: most of KeyboardOrKeypad elements should be treated as Selector type, not as Button...
521           inputDevice->addElement(new ButtonElement(element, page, usage));
522         } else if (page == kHIDPage_LEDs && usage > 0) {
523           inputDevice->addElement(new LEDElement(element, page, usage));
524 /* Feature elements are not fully tested yet
525         } else if (type == kIOHIDElementTypeFeature) {
526           // just for testing feature elements
527           inputDevice->addElement(new FeatureElement(element, page, usage, elementCount[inputDevice][USAGE_KEY(page, usage)]));
528 */
529         } else {
530           SG_LOG(SG_INPUT, SG_INFO, "HID Element Page/Usage is not supported: type=" << hidTypeByID.getName(type) 
531                     << "(" << type << ")" << ", page=" << hidPageByID.getName(page) << "(" << page << ")" 
532                     << ", usage=" << usage);
533         }
534     }
535   }
536 }
537
538 //
539 // FGMacOSXInputDevice implementation
540 //
541
542 FGMacOSXInputDevice::FGMacOSXInputDevice(io_object_t device) : device(device), devInterface(NULL)
543 {
544   CFDictionaryRef properties = getProperties();
545   string deviceName = GetHIDElementStringValue(properties, kIOHIDProductKey);
546   if (deviceName == "") {
547     deviceName = GetHIDElementStringValue(properties, "USB Product Name");
548   }
549
550   SetName(deviceName);
551   CFRelease(properties);
552 }
553
554 const char *FGMacOSXInputDevice::TranslateEventName(FGEventData &eventData)
555 {
556   FGMacOSXEventData &macEvent = (FGMacOSXEventData &)eventData;
557   return macEvent.name.c_str();
558 }
559
560
561 //
562 // Outputs value to an writable element (like LEDElement)
563 //
564 void FGMacOSXInputDevice::Send(const char *eventName, double value)
565 {
566   HIDElement *element = elements[eventName];
567   if (element) {
568     element->write(devInterface, value);
569   } else {
570     SG_LOG(SG_INPUT, SG_WARN, "No element to handle event: " << eventName);
571   }
572 }
573
574
575 CFDictionaryRef FGMacOSXInputDevice::getProperties(io_object_t device)
576 {
577   IOReturn ret;
578   CFMutableDictionaryRef properties;
579
580   ret = IORegistryEntryCreateCFProperties( device, &properties, kCFAllocatorDefault, kNilOptions);
581   if (ret != kIOReturnSuccess || !properties) {
582     SG_LOG(SG_INPUT, SG_WARN, "Error getting device properties.");
583     return NULL;
584   }
585
586   return properties;
587 }
588
589 //
590 // Adds HID element to FGMacOSXInputDevice.
591 // On Mac OS X, update() will read value of each HID element.
592 // Thus, FGMacOSXInputDevice needs all supported elements when a device is opened
593 //
594 void FGMacOSXInputDevice::addElement(HIDElement *element)
595 {
596   elements[element->getName()] = element;
597   int count = elements.size();
598   SG_LOG(SG_INPUT, SG_DEBUG, "adding element " << count << ":" << element->getName());
599 }
600
601 void FGMacOSXInputDevice::Open() {
602   // create device interface
603   IOReturn ret;
604   SInt32 score;
605   IOCFPlugInInterface **plugin;
606   SG_LOG(SG_INPUT, SG_INFO, "Opening HID : " << GetName());
607
608   ret = IOCreatePlugInInterfaceForService(device,
609                                           kIOHIDDeviceUserClientTypeID,
610                                           kIOCFPlugInInterfaceID,
611                                           &plugin, &score);
612         
613   if (ret != kIOReturnSuccess) {
614     SG_LOG(SG_INPUT, SG_ALERT, "Error creating a plugin for HID : " << GetName());
615     throw exception();
616     return;
617   }
618
619   HRESULT result = (*plugin)->QueryInterface(plugin, 
620                                              CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), 
621                                              (LPVOID*)&devInterface );
622         
623   if (result != S_OK)
624     SG_LOG(SG_INPUT, SG_WARN, "Failed Querying HID plugin interface: " << GetName());
625
626   (*plugin)->Release(plugin); // don't leak a ref
627   if (devInterface == NULL) {
628     return;
629     throw exception();
630   }
631         
632   // store the interface in this instance
633   ret = (*devInterface)->open(devInterface, 0);
634   if (ret != kIOReturnSuccess) {
635     SG_LOG(SG_INPUT, SG_ALERT, "Error opening device interface: " << GetName());
636     throw exception();
637     return;
638   }
639   CFDictionaryRef props = getProperties();
640                 
641   // recursively adds all supported HID elements to FGMacOSXInputDevice
642   CFTypeRef topLevelElement = CFDictionaryGetValue (props, CFSTR(kIOHIDElementKey));
643   HIDElementFactory::create(topLevelElement, this);
644   CFRelease(props);
645 }
646
647 void FGMacOSXInputDevice::Close() {
648   SG_LOG(SG_INPUT, SG_INFO, "Closing HID: " << GetName());
649   if (devInterface) {
650     (*devInterface)->close(devInterface);
651   }
652
653   map<string, HIDElement *>::iterator it;
654   for (it = elements.begin(); it != elements.end(); it++) {
655     if ((*it).second)
656       delete (*it).second;
657   }
658   elements.clear();
659 }
660
661 //
662 // Reads values of assigned HIDElement and generates events
663 //
664 void FGMacOSXInputDevice::update(double dt)
665 {
666   map<string, HIDElement *>::iterator it;
667   for (it = elements.begin(); it != elements.end(); it++) {
668     (*it).second->read(devInterface);
669     if ((*it).second->isUpdated()) { // Is this needed?
670       int modifiers = fgGetKeyModifiers();
671       (*it).second->generateEvent(this, dt, modifiers);
672     }
673   }
674 }
675
676 //
677 // FGMacOSXEventInput implementation
678 //
679 FGMacOSXEventInput::~FGMacOSXEventInput() {
680   deviceIndices.clear();
681 }
682
683 void FGMacOSXEventInput::init()
684 {
685   IOReturn ret;
686   SG_LOG(SG_INPUT, SG_INFO, "initializing FGMacOSXEventInput");
687
688   // We want all HID devices for matching
689   CFMutableDictionaryRef matchingDictionary = IOServiceMatching(kIOHIDDeviceKey);
690
691   // Needs to retain machingDict since IOServiceAddMatchingNotification consumes one reference.
692   matchingDictionary = (CFMutableDictionaryRef) CFRetain(matchingDictionary);
693   matchingDictionary = (CFMutableDictionaryRef) CFRetain(matchingDictionary);
694
695   // Registers Hotplug notification for plug and play.
696   notifyPort = IONotificationPortCreate(kIOMasterPortDefault); 
697   runLoopSource = IONotificationPortGetRunLoopSource(notifyPort);
698   CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
699   ret = IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, 
700                                          matchingDictionary, FGMacOSXEventInput::deviceAttached, (void *)this, &addedIterator);
701   ret = IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, 
702                                          matchingDictionary, FGMacOSXEventInput::deviceDetached, (void *)this, &removedIterator);
703
704   // prepare for notification by calling these callback funcs to remove existing HID device iterators
705   FGMacOSXEventInput::deviceAttached((void *)this, addedIterator);
706   FGMacOSXEventInput::deviceDetached((void *)this, removedIterator);
707 }
708
709
710 void FGMacOSXEventInput::attachDevice(io_iterator_t iterator)
711 {
712   io_object_t device;
713
714   while ((device = IOIteratorNext(iterator))) {
715     FGMacOSXInputDevice *inputDevice = new FGMacOSXInputDevice(device);
716     if (inputDevice) {
717       SG_LOG(SG_INPUT, SG_INFO, "HID Device Atached: " << inputDevice->GetName());
718       unsigned index = AddDevice(inputDevice);
719       // Needs to check if AddDevice closed the device due to lack of config file
720       if (index != FGEventInput::INVALID_DEVICE_INDEX) {
721         // maps device with FG device index. 
722         // This map is needed in detachDevice to tell FGEventInput which deivce ID is to be removed
723         deviceIndices[device] = index;
724       }
725     }
726     IOObjectRelease(device);
727   }
728 }
729
730
731 void FGMacOSXEventInput::detachDevice(io_iterator_t iterator)
732 {
733   io_object_t device;
734
735   while ((device = IOIteratorNext(iterator))) {
736     unsigned index = deviceIndices[device];
737     if (index != FGEventInput::INVALID_DEVICE_INDEX) {
738       FGMacOSXInputDevice *inputDevice = dynamic_cast<FGMacOSXInputDevice *>(input_devices[index]);
739       if (inputDevice) {
740         SG_LOG(SG_INPUT, SG_INFO, "HID Device Detached: " << inputDevice->GetName());
741         RemoveDevice(index);
742         deviceIndices.erase(device); 
743       } else {
744         SG_LOG(SG_INPUT, SG_WARN, "Invalid device index:" << index << ". Detach failed.");
745       }
746     } else {
747       SG_LOG(SG_INPUT, SG_INFO, "Device ID unmatched: " << (int)device << " No HID deivce is detached since it is not supported by FG.");
748     }
749     IOObjectRelease(device);
750   }
751 }
752
753 //
754 // read all elements in each input device
755 //
756 void FGMacOSXEventInput::update(double dt)
757 {
758   FGEventInput::update(dt);
759
760   map<int, FGInputDevice*>::const_iterator it;
761   for (it = input_devices.begin(); it != input_devices.end(); it++) {
762     if ((*it).second) {
763       FGMacOSXInputDevice *inputDevice = dynamic_cast<FGMacOSXInputDevice *>((*it).second);
764       if (inputDevice) {
765         inputDevice->update(dt);
766       } else {
767         SG_LOG(SG_INPUT, SG_WARN, "Invalid device. Update failed.");
768       }
769     }
770   }
771 }