Release 9.11.
[wine.git] / dlls / dinput / keyboard.c
blobcbcbf94f0f640b775d3d51af654d0a93e4c13a8f
1 /* DirectInput Keyboard device
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2005 Raphael Junqueira
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <string.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "dinput.h"
31 #include "dinput_private.h"
32 #include "device_private.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
37 static const struct dinput_device_vtbl keyboard_vtbl;
39 struct keyboard
41 struct dinput_device base;
44 static inline struct keyboard *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface )
46 return CONTAINING_RECORD( CONTAINING_RECORD( iface, struct dinput_device, IDirectInputDevice8W_iface ),
47 struct keyboard, base );
50 static BYTE map_dik_code(DWORD scanCode, DWORD vkCode, DWORD subType, DWORD version)
52 if (!scanCode && version < 0x0800)
53 scanCode = MapVirtualKeyW(vkCode, MAPVK_VK_TO_VSC);
55 if (subType == DIDEVTYPEKEYBOARD_JAPAN106)
57 switch (scanCode)
59 case 0x0d: /* ^ */
60 scanCode = DIK_CIRCUMFLEX;
61 break;
62 case 0x1a: /* @ */
63 scanCode = DIK_AT;
64 break;
65 case 0x1b: /* [ */
66 scanCode = DIK_LBRACKET;
67 break;
68 case 0x28: /* : */
69 scanCode = DIK_COLON;
70 break;
71 case 0x29: /* Hankaku/Zenkaku */
72 scanCode = DIK_KANJI;
73 break;
74 case 0x2b: /* ] */
75 scanCode = DIK_RBRACKET;
76 break;
77 case 0x73: /* \ */
78 scanCode = DIK_BACKSLASH;
79 break;
82 if (scanCode & 0x100) scanCode |= 0x80;
83 return (BYTE)scanCode;
86 static void keyboard_handle_event( struct keyboard *impl, DWORD vkey, DWORD scan_code, BOOL up )
88 BYTE new_diks, subtype = GET_DIDEVICE_SUBTYPE( impl->base.instance.dwDevType );
89 IDirectInputDevice8W *iface = &impl->base.IDirectInputDevice8W_iface;
90 int dik_code, index;
92 switch (vkey)
94 /* R-Shift is special - it is an extended key with separate scan code */
95 case VK_RSHIFT : dik_code = DIK_RSHIFT; break;
96 case VK_PAUSE : dik_code = DIK_PAUSE; break;
97 case VK_NUMLOCK : dik_code = DIK_NUMLOCK; break;
98 case VK_SUBTRACT: dik_code = DIK_SUBTRACT; break;
99 default:
100 dik_code = map_dik_code( scan_code, vkey, subtype, impl->base.dinput->dwVersion );
101 break;
103 new_diks = (up ? 0 : 0x80);
105 /* returns now if key event already known */
106 if (new_diks == impl->base.device_state[dik_code]) return;
108 impl->base.device_state[dik_code] = new_diks;
109 TRACE( "setting key %02x to %02x\n", dik_code, impl->base.device_state[dik_code] );
111 EnterCriticalSection( &impl->base.crit );
113 if ((index = dinput_device_object_index_from_id( iface, DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( dik_code ) )) >= 0)
114 queue_event( iface, index, new_diks, GetCurrentTime(), impl->base.dinput->evsequence++ );
116 if (impl->base.hEvent) SetEvent( impl->base.hEvent );
117 LeaveCriticalSection( &impl->base.crit );
120 void dinput_keyboard_rawinput_hook( IDirectInputDevice8W *iface, WPARAM wparam, LPARAM lparam, RAWINPUT *ri )
122 struct keyboard *impl = impl_from_IDirectInputDevice8W( iface );
123 DWORD scan_code;
125 TRACE("(%p) wparam %Ix, lparam %Ix\n", iface, wparam, lparam);
127 scan_code = ri->data.keyboard.MakeCode & 0xff;
128 if (ri->data.keyboard.Flags & RI_KEY_E0) scan_code |= 0x100;
129 keyboard_handle_event( impl, ri->data.keyboard.VKey, scan_code, ri->data.keyboard.Flags & RI_KEY_BREAK );
132 int dinput_keyboard_hook( IDirectInputDevice8W *iface, WPARAM wparam, LPARAM lparam )
134 struct keyboard *impl = impl_from_IDirectInputDevice8W( iface );
135 KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam;
136 DWORD scan_code;
138 TRACE( "iface %p, wparam %#Ix, lparam %#Ix, vkCode %#lx, scanCode %#lx.\n", iface, wparam,
139 lparam, hook->vkCode, hook->scanCode );
141 if (wparam != WM_KEYDOWN && wparam != WM_KEYUP && wparam != WM_SYSKEYDOWN && wparam != WM_SYSKEYUP)
142 return 0;
144 scan_code = hook->scanCode & 0xff;
145 if (hook->flags & LLKHF_EXTENDED) scan_code |= 0x100;
146 keyboard_handle_event( impl, hook->vkCode, scan_code, hook->flags & LLKHF_UP );
148 return impl->base.dwCoopLevel & DISCL_EXCLUSIVE;
151 static DWORD get_keyboard_subtype(void)
153 INT kbd_type, kbd_subtype, dev_subtype;
154 kbd_type = GetKeyboardType(0);
155 kbd_subtype = GetKeyboardType(1);
157 if (kbd_type == 4 || (kbd_type == 7 && kbd_subtype == 0))
158 dev_subtype = DIDEVTYPEKEYBOARD_PCENH;
159 else if (kbd_type == 7 && kbd_subtype == 2)
160 dev_subtype = DIDEVTYPEKEYBOARD_JAPAN106;
161 else {
162 FIXME( "Unknown keyboard type %d, subtype %d\n", kbd_type, kbd_subtype );
163 dev_subtype = DIDEVTYPEKEYBOARD_PCENH;
165 return dev_subtype;
168 HRESULT keyboard_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, DWORD version )
170 BYTE subtype = get_keyboard_subtype();
171 DWORD size;
173 TRACE( "type %#lx, flags %#lx, instance %p, version %#lx.\n", type, flags, instance, version );
175 size = instance->dwSize;
176 memset( instance, 0, size );
177 instance->dwSize = size;
178 instance->guidInstance = GUID_SysKeyboard;
179 instance->guidProduct = GUID_SysKeyboard;
180 if (version >= 0x0800) instance->dwDevType = DI8DEVTYPE_KEYBOARD | (subtype << 8);
181 else instance->dwDevType = DIDEVTYPE_KEYBOARD | (subtype << 8);
182 MultiByteToWideChar( CP_ACP, 0, "Keyboard", -1, instance->tszInstanceName, MAX_PATH );
183 MultiByteToWideChar( CP_ACP, 0, "Wine Keyboard", -1, instance->tszProductName, MAX_PATH );
185 return DI_OK;
188 HRESULT keyboard_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out )
190 struct keyboard *impl;
191 HRESULT hr;
193 TRACE( "dinput %p, guid %s, out %p.\n", dinput, debugstr_guid( guid ), out );
195 *out = NULL;
196 if (!IsEqualGUID( &GUID_SysKeyboard, guid )) return DIERR_DEVICENOTREG;
198 if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY;
199 dinput_device_init( &impl->base, &keyboard_vtbl, guid, dinput );
200 impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": struct keyboard*->base.crit");
202 keyboard_enum_device( 0, 0, &impl->base.instance, dinput->dwVersion );
203 impl->base.caps.dwDevType = impl->base.instance.dwDevType;
204 impl->base.caps.dwFirmwareRevision = 100;
205 impl->base.caps.dwHardwareRevision = 100;
206 if (dinput->dwVersion >= 0x0800) impl->base.use_raw_input = TRUE;
208 if (FAILED(hr = dinput_device_init_device_format( &impl->base.IDirectInputDevice8W_iface ))) goto failed;
210 *out = &impl->base.IDirectInputDevice8W_iface;
211 return DI_OK;
213 failed:
214 IDirectInputDevice_Release( &impl->base.IDirectInputDevice8W_iface );
215 return hr;
218 static HRESULT keyboard_poll( IDirectInputDevice8W *iface )
220 check_dinput_events();
221 return DI_OK;
224 static HRESULT keyboard_acquire( IDirectInputDevice8W *iface )
226 return DI_OK;
229 static HRESULT keyboard_unacquire( IDirectInputDevice8W *iface )
231 struct keyboard *impl = impl_from_IDirectInputDevice8W( iface );
232 memset( impl->base.device_state, 0, sizeof(impl->base.device_state) );
233 return DI_OK;
236 static BOOL try_enum_object( struct dinput_device *impl, const DIPROPHEADER *filter, DWORD flags, enum_object_callback callback,
237 UINT index, DIDEVICEOBJECTINSTANCEW *instance, void *data )
239 if (flags != DIDFT_ALL && !(flags & DIDFT_GETTYPE( instance->dwType ))) return DIENUM_CONTINUE;
241 switch (filter->dwHow)
243 case DIPH_DEVICE:
244 return callback( impl, index, NULL, instance, data );
245 case DIPH_BYOFFSET:
246 if (filter->dwObj != instance->dwOfs) return DIENUM_CONTINUE;
247 return callback( impl, index, NULL, instance, data );
248 case DIPH_BYID:
249 if ((filter->dwObj & 0x00ffffff) != (instance->dwType & 0x00ffffff)) return DIENUM_CONTINUE;
250 return callback( impl, index, NULL, instance, data );
253 return DIENUM_CONTINUE;
256 static HRESULT keyboard_enum_objects( IDirectInputDevice8W *iface, const DIPROPHEADER *filter,
257 DWORD flags, enum_object_callback callback, void *context )
259 struct keyboard *impl = impl_from_IDirectInputDevice8W( iface );
260 BYTE subtype = GET_DIDEVICE_SUBTYPE( impl->base.instance.dwDevType );
261 DIDEVICEOBJECTINSTANCEW instance =
263 .dwSize = sizeof(DIDEVICEOBJECTINSTANCEW),
264 .guidType = GUID_Key,
265 .dwOfs = DIK_ESCAPE,
266 .dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( DIK_ESCAPE ),
268 DWORD index, i, dik;
269 BOOL ret;
271 for (i = 0, index = 0; i < 512; ++i)
273 if (!GetKeyNameTextW( i << 16, instance.tszName, ARRAY_SIZE(instance.tszName) )) continue;
274 if (!(dik = map_dik_code( i, 0, subtype, impl->base.dinput->dwVersion ))) continue;
275 instance.dwOfs = dik;
276 instance.dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( dik );
277 ret = try_enum_object( &impl->base, filter, flags, callback, index++, &instance, context );
278 if (ret != DIENUM_CONTINUE) return DIENUM_STOP;
281 return DIENUM_CONTINUE;
284 static HRESULT keyboard_get_property( IDirectInputDevice8W *iface, DWORD property,
285 DIPROPHEADER *header, const DIDEVICEOBJECTINSTANCEW *instance )
287 switch (property)
289 case (DWORD_PTR)DIPROP_KEYNAME:
291 DIPROPSTRING *value = (DIPROPSTRING *)header;
292 lstrcpynW( value->wsz, instance->tszName, ARRAY_SIZE(value->wsz) );
293 return DI_OK;
296 return DIERR_UNSUPPORTED;
299 static const struct dinput_device_vtbl keyboard_vtbl =
301 NULL,
302 keyboard_poll,
303 NULL,
304 keyboard_acquire,
305 keyboard_unacquire,
306 keyboard_enum_objects,
307 keyboard_get_property,
308 NULL,
309 NULL,
310 NULL,
311 NULL,
312 NULL,