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