Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / dinput / keyboard / main.c
1 /*              DirectInput Keyboard device
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include <string.h>
24 #ifdef HAVE_SYS_ERRNO_H
25 # include <sys/errno.h>
26 #endif
27
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "winerror.h"
31 #include "dinput.h"
32
33 #include "dinput_private.h"
34 #include "device_private.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
38
39 static ICOM_VTABLE(IDirectInputDevice8A) SysKeyboardAvt;
40
41 typedef struct SysKeyboardAImpl SysKeyboardAImpl;
42 struct SysKeyboardAImpl
43 {
44         LPVOID                          lpVtbl;
45         DWORD                           ref;
46         GUID                            guid;
47
48         IDirectInputAImpl *dinput;
49
50         HANDLE  hEvent;
51         /* SysKeyboardAImpl */
52         int                             acquired;
53         int                             buffersize;  /* set in 'SetProperty'         */
54         LPDIDEVICEOBJECTDATA            buffer;      /* buffer for 'GetDeviceData'.
55                                                         Alloc at 'Acquire', Free at
56                                                         'Unacquire'                  */
57         int                             count;       /* number of objects in use in
58                                                         'buffer'                     */
59         int                             start;       /* 'buffer' rotates. This is the
60                                                         first in use (if count > 0)  */
61         BOOL                            overflow;    /* return DI_BUFFEROVERFLOW in
62                                                         'GetDeviceData'              */
63         CRITICAL_SECTION                crit;
64 };
65
66 SysKeyboardAImpl *current; /* Today's acquired device
67 FIXME: currently this can be only one.
68 Maybe this should be a linked list or st.
69 I don't know what the rules are for multiple acquired keyboards,
70 but 'DI_LOSTFOCUS' and 'DI_UNACQUIRED' exist for a reason.
71 */
72
73 static BYTE DInputKeyState[256]; /* array for 'GetDeviceState' */
74
75 HHOOK keyboard_hook;
76
77 LRESULT CALLBACK KeyboardCallback( int code, WPARAM wparam, LPARAM lparam )
78 {
79   if (code == HC_ACTION)
80     {
81       BYTE dik_code;
82       BOOL down;
83       DWORD timestamp;
84
85       {
86         KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam;
87         dik_code = hook->scanCode;
88         if (hook->flags & LLKHF_EXTENDED) dik_code |= 0x80;
89         down = !(hook->flags & LLKHF_UP);
90         timestamp = hook->time;
91       }
92
93       DInputKeyState[dik_code] = (down ? 0x80 : 0);
94
95       if (current != NULL)
96         {
97           if (current->hEvent)
98             SetEvent(current->hEvent);
99
100           if (current->buffer != NULL)
101             {
102               int n;
103
104               EnterCriticalSection(&(current->crit));
105
106               n = (current->start + current->count) % current->buffersize;
107
108               current->buffer[n].dwOfs = dik_code;
109               current->buffer[n].dwData = down ? 0x80 : 0;
110               current->buffer[n].dwTimeStamp = timestamp;
111               current->buffer[n].dwSequence = current->dinput->evsequence++;
112
113               if (current->count == current->buffersize)
114                 {
115                   current->start++;
116                   current->overflow = TRUE;
117                 }
118               else
119                 current->count++;
120
121               LeaveCriticalSection(&(current->crit));
122             }
123         }
124     }
125
126   return CallNextHookEx(keyboard_hook, code, wparam, lparam);
127 }
128
129 static GUID DInput_Wine_Keyboard_GUID = { /* 0ab8648a-7735-11d2-8c73-71df54a96441 */
130   0x0ab8648a,
131   0x7735,
132   0x11d2,
133   {0x8c, 0x73, 0x71, 0xdf, 0x54, 0xa9, 0x64, 0x41}
134 };
135
136 static BOOL keyboarddev_enum_device(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi)
137 {
138   if ((dwDevType == 0) || (dwDevType == DIDEVTYPE_KEYBOARD)) {
139     TRACE("Enumerating the Keyboard device\n");
140
141     lpddi->guidInstance = GUID_SysKeyboard;/* DInput's GUID */
142     lpddi->guidProduct = DInput_Wine_Keyboard_GUID; /* Vendor's GUID */
143     lpddi->dwDevType = DIDEVTYPE_KEYBOARD | (DIDEVTYPEKEYBOARD_UNKNOWN << 8);
144     strcpy(lpddi->tszInstanceName, "Keyboard");
145     strcpy(lpddi->tszProductName, "Wine Keyboard");
146
147     return TRUE;
148   }
149
150   return FALSE;
151 }
152
153 static SysKeyboardAImpl *alloc_device(REFGUID rguid, LPVOID kvt, IDirectInputAImpl *dinput)
154 {
155     SysKeyboardAImpl* newDevice;
156     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysKeyboardAImpl));
157     newDevice->lpVtbl = kvt;
158     newDevice->ref = 1;
159     memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
160     newDevice->dinput = dinput;
161
162     return newDevice;
163 }
164
165
166 static HRESULT keyboarddev_create_device(IDirectInputAImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
167 {
168   if ((IsEqualGUID(&GUID_SysKeyboard,rguid)) ||          /* Generic Keyboard */
169       (IsEqualGUID(&DInput_Wine_Keyboard_GUID,rguid))) { /* Wine Keyboard */
170     if ((riid == NULL) ||
171         IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
172         IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
173         IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
174         IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
175       *pdev=(IDirectInputDeviceA*) alloc_device(rguid, &SysKeyboardAvt, dinput);
176       TRACE("Creating a Keyboard device (%p)\n", *pdev);
177       return DI_OK;
178     } else
179       return DIERR_NOINTERFACE;
180   }
181
182   return DIERR_DEVICENOTREG;
183 }
184
185 static dinput_device keyboarddev = {
186   100,
187   keyboarddev_enum_device,
188   keyboarddev_create_device
189 };
190
191 DECL_GLOBAL_CONSTRUCTOR(keyboarddev_register) { dinput_register_device(&keyboarddev); }
192
193 static HRESULT WINAPI SysKeyboardAImpl_SetProperty(
194         LPDIRECTINPUTDEVICE8A iface,REFGUID rguid,LPCDIPROPHEADER ph
195 )
196 {
197         ICOM_THIS(SysKeyboardAImpl,iface);
198
199         TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
200         TRACE("(size=%ld,headersize=%ld,obj=%ld,how=%ld\n",
201             ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow);
202         if (!HIWORD(rguid)) {
203                 switch ((DWORD)rguid) {
204                 case (DWORD) DIPROP_BUFFERSIZE: {
205                         LPCDIPROPDWORD  pd = (LPCDIPROPDWORD)ph;
206
207                         TRACE("(buffersize=%ld)\n",pd->dwData);
208
209                         if (This->acquired)
210                            return DIERR_INVALIDPARAM;
211
212                         This->buffersize = pd->dwData;
213
214                         break;
215                 }
216                 default:
217                         WARN("Unknown type %ld\n",(DWORD)rguid);
218                         break;
219                 }
220         }
221         return 0;
222 }
223
224 static HRESULT WINAPI SysKeyboardAImpl_GetDeviceState(
225         LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
226 )
227 {
228     /* Note: device does not need to be acquired */
229     if (len != 256)
230       return DIERR_INVALIDPARAM;
231
232     memcpy(ptr, DInputKeyState, 256);
233     return DI_OK;
234 }
235
236 static HRESULT WINAPI SysKeyboardAImpl_GetDeviceData(
237         LPDIRECTINPUTDEVICE8A iface,DWORD dodsize,LPDIDEVICEOBJECTDATA dod,
238         LPDWORD entries,DWORD flags
239 )
240 {
241         ICOM_THIS(SysKeyboardAImpl,iface);
242         int ret = DI_OK, i = 0;
243
244         TRACE("(this=%p,%ld,%p,%p(%ld)),0x%08lx)\n",
245               This,dodsize,dod,entries,entries?*entries:0,flags);
246
247         if (This->buffer == NULL)
248           return DIERR_NOTBUFFERED;
249
250         if (dodsize < sizeof(*dod))
251           return DIERR_INVALIDPARAM;
252
253         EnterCriticalSection(&(This->crit));
254
255         /* Copy item at a time for the case dodsize > sizeof(buffer[n]) */
256         while ((i < *entries || *entries == INFINITE) && i < This->count)
257           {
258             if (dod != NULL)
259               {
260                 int n = (This->start + i) % This->buffersize;
261                 LPDIDEVICEOBJECTDATA pd
262                    = (LPDIDEVICEOBJECTDATA)((BYTE *)dod + dodsize * i);
263                 pd->dwOfs       = This->buffer[n].dwOfs;
264                 pd->dwData      = This->buffer[n].dwData;
265                 pd->dwTimeStamp = This->buffer[n].dwTimeStamp;
266                 pd->dwSequence  = This->buffer[n].dwSequence;
267               }
268             i++;
269           }
270
271         *entries = i;
272
273         if (This->overflow)
274           ret = DI_BUFFEROVERFLOW;
275
276         if (!(flags & DIGDD_PEEK))
277           {
278             /* Empty buffer */
279             This->count -= i;
280             This->start = (This->start + i) % This->buffersize;
281             This->overflow = FALSE;
282           }
283
284         LeaveCriticalSection(&(This->crit));
285
286         return ret;
287 }
288
289 static HRESULT WINAPI SysKeyboardAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface);
290
291 static HRESULT WINAPI SysKeyboardAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
292 {
293         ICOM_THIS(SysKeyboardAImpl,iface);
294
295         TRACE("(this=%p)\n",This);
296
297         if (This->acquired)
298           return S_FALSE;
299
300         This->acquired = 1;
301
302         if (current != NULL)
303           {
304             FIXME("Not more than one keyboard can be acquired at the same time.\n");
305             SysKeyboardAImpl_Unacquire(iface);
306           }
307
308         current = This;
309
310         if (This->buffersize > 0)
311           {
312             This->buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
313                                      This->buffersize * sizeof(*(This->buffer)));
314             This->start = 0;
315             This->count = 0;
316             This->overflow = FALSE;
317             InitializeCriticalSection(&(This->crit));
318           }
319         else
320           This->buffer = NULL;
321
322         return DI_OK;
323 }
324
325 static HRESULT WINAPI SysKeyboardAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
326 {
327         ICOM_THIS(SysKeyboardAImpl,iface);
328         TRACE("(this=%p)\n",This);
329
330         if (This->acquired == 0)
331           return DI_NOEFFECT;
332
333         if (current == This)
334           current = NULL;
335         else
336           ERR("this != current\n");
337
338         This->acquired = 0;
339
340         if (This->buffersize >= 0)
341           {
342             HeapFree(GetProcessHeap(), 0, This->buffer);
343             This->buffer = NULL;
344             DeleteCriticalSection(&(This->crit));
345           }
346
347         return DI_OK;
348 }
349
350 static HRESULT WINAPI SysKeyboardAImpl_SetEventNotification(LPDIRECTINPUTDEVICE8A iface,
351                                                             HANDLE hnd) {
352   ICOM_THIS(SysKeyboardAImpl,iface);
353
354   TRACE("(this=%p,0x%08lx)\n",This,(DWORD)hnd);
355
356   This->hEvent = hnd;
357   return DI_OK;
358 }
359
360 /******************************************************************************
361   *     GetCapabilities : get the device capablitites
362   */
363 static HRESULT WINAPI SysKeyboardAImpl_GetCapabilities(
364         LPDIRECTINPUTDEVICE8A iface,
365         LPDIDEVCAPS lpDIDevCaps)
366 {
367   ICOM_THIS(SysKeyboardAImpl,iface);
368
369   TRACE("(this=%p,%p)\n",This,lpDIDevCaps);
370
371   if (lpDIDevCaps->dwSize == sizeof(DIDEVCAPS)) {
372     lpDIDevCaps->dwFlags = DIDC_ATTACHED;
373     lpDIDevCaps->dwDevType = DIDEVTYPE_KEYBOARD;
374     lpDIDevCaps->dwAxes = 0;
375     lpDIDevCaps->dwButtons = 0;
376     lpDIDevCaps->dwPOVs = 0;
377     lpDIDevCaps->dwFFSamplePeriod = 0;
378     lpDIDevCaps->dwFFMinTimeResolution = 0;
379     lpDIDevCaps->dwFirmwareRevision = 100;
380     lpDIDevCaps->dwHardwareRevision = 100;
381     lpDIDevCaps->dwFFDriverVersion = 0;
382   } else {
383     /* DirectX 3.0 */
384     FIXME("DirectX 3.0 not supported....\n");
385   }
386
387   return DI_OK;
388 }
389
390 static ICOM_VTABLE(IDirectInputDevice8A) SysKeyboardAvt =
391 {
392         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
393         IDirectInputDevice2AImpl_QueryInterface,
394         IDirectInputDevice2AImpl_AddRef,
395         IDirectInputDevice2AImpl_Release,
396         SysKeyboardAImpl_GetCapabilities,
397         IDirectInputDevice2AImpl_EnumObjects,
398         IDirectInputDevice2AImpl_GetProperty,
399         SysKeyboardAImpl_SetProperty,
400         SysKeyboardAImpl_Acquire,
401         SysKeyboardAImpl_Unacquire,
402         SysKeyboardAImpl_GetDeviceState,
403         SysKeyboardAImpl_GetDeviceData,
404         IDirectInputDevice2AImpl_SetDataFormat,
405         SysKeyboardAImpl_SetEventNotification,
406         IDirectInputDevice2AImpl_SetCooperativeLevel,
407         IDirectInputDevice2AImpl_GetObjectInfo,
408         IDirectInputDevice2AImpl_GetDeviceInfo,
409         IDirectInputDevice2AImpl_RunControlPanel,
410         IDirectInputDevice2AImpl_Initialize,
411         IDirectInputDevice2AImpl_CreateEffect,
412         IDirectInputDevice2AImpl_EnumEffects,
413         IDirectInputDevice2AImpl_GetEffectInfo,
414         IDirectInputDevice2AImpl_GetForceFeedbackState,
415         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
416         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
417         IDirectInputDevice2AImpl_Escape,
418         IDirectInputDevice2AImpl_Poll,
419         IDirectInputDevice2AImpl_SendDeviceData,
420         IDirectInputDevice7AImpl_EnumEffectsInFile,
421         IDirectInputDevice7AImpl_WriteEffectToFile,
422         IDirectInputDevice8AImpl_BuildActionMap,
423         IDirectInputDevice8AImpl_SetActionMap,
424         IDirectInputDevice8AImpl_GetImageInfo
425 };