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
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
;
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
)
60 scanCode
= DIK_CIRCUMFLEX
;
66 scanCode
= DIK_LBRACKET
;
71 case 0x29: /* Hankaku/Zenkaku */
75 scanCode
= DIK_RBRACKET
;
78 scanCode
= DIK_BACKSLASH
;
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
;
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;
100 dik_code
= map_dik_code( scan_code
, vkey
, subtype
, impl
->base
.dinput
->dwVersion
);
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
);
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
;
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
)
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
;
162 FIXME( "Unknown keyboard type %d, subtype %d\n", kbd_type
, kbd_subtype
);
163 dev_subtype
= DIDEVTYPEKEYBOARD_PCENH
;
168 HRESULT
keyboard_enum_device( DWORD type
, DWORD flags
, DIDEVICEINSTANCEW
*instance
, DWORD version
)
170 BYTE subtype
= get_keyboard_subtype();
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
);
188 HRESULT
keyboard_create_device( struct dinput
*dinput
, const GUID
*guid
, IDirectInputDevice8W
**out
)
190 struct keyboard
*impl
;
193 TRACE( "dinput %p, guid %s, out %p.\n", dinput
, debugstr_guid( guid
), out
);
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
;
214 IDirectInputDevice_Release( &impl
->base
.IDirectInputDevice8W_iface
);
218 static HRESULT
keyboard_poll( IDirectInputDevice8W
*iface
)
220 check_dinput_events();
224 static HRESULT
keyboard_acquire( IDirectInputDevice8W
*iface
)
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
) );
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
)
244 return callback( impl
, index
, NULL
, instance
, data
);
246 if (filter
->dwObj
!= instance
->dwOfs
) return DIENUM_CONTINUE
;
247 return callback( impl
, index
, NULL
, instance
, data
);
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
,
266 .dwType
= DIDFT_PSHBUTTON
| DIDFT_MAKEINSTANCE( DIK_ESCAPE
),
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
)
289 case (DWORD_PTR
)DIPROP_KEYNAME
:
291 DIPROPSTRING
*value
= (DIPROPSTRING
*)header
;
292 lstrcpynW( value
->wsz
, instance
->tszName
, ARRAY_SIZE(value
->wsz
) );
296 return DIERR_UNSUPPORTED
;
299 static const struct dinput_device_vtbl keyboard_vtbl
=
306 keyboard_enum_objects
,
307 keyboard_get_property
,