/***************************************************************************** * * DIEm.h * * Copyright (c) 1996-1997 Microsoft Corporation. All Rights Reserved. * * Abstract: * * DirectInput internal header file for emulation. * *****************************************************************************/ /***************************************************************************** * * @doc INTERNAL * * @struct CEd | * * Emulation descriptor. One of these is created for each * device. It is never destroyed, so the variable must * be a global variable or memory allocated inside a * container that will eventually be destroyed. * * ISSUE-2001/03/29-timgill Need a better destructor function * * @field LPVOID const | pState | * * State buffer that everybody parties into. * * It too is never destroyed, so once again it should be * a global variable or live inside something else that * will be destroyed. * * @field LPDWORD const | pDevType | * * Array of device type descriptors, indexed by data format * offset. Used to determine whether a particular piece of * data belongs to an axis, button, or POV. * * @field EMULATIONPROC | Acquire | * * Callback function for acquisition and loss thereof. * It is called once when the first client acquires, * and again when the last app unacquires. It is not * informed of nested acquisition. * * @field LONG | cAcquire | * * Number of times the device emulation has been acquired (minus one). * * @field DWORD | cbData | * * Size of the device data type. In other words, size of *

in bytes. * *****************************************************************************/ typedef STDMETHOD(EMULATIONPROC)(struct CEm *, BOOL fAcquire); typedef struct CEd { LPVOID const pState; LPDWORD const pDevType; EMULATIONPROC Acquire; LONG cAcquire; DWORD cbData; ULONG cRef; } CEd, ED, *PED; /***************************************************************************** * * @doc INTERNAL * * @struct CEm | * * Emulation state information. * * @field VXDINSTANCE | vi | * * Information shared with parent device. * * @field PEM | pemNext | * * Next item in linked list of all active device instances. * * @field LPDWORD | rgdwDf | * * Array of items (one for each byte in the device * data format). This maps each device data format byte * into an application device data offset, or -1 if the * application doesn't care about the corresponding object. * * @field ULONG_PTR | dwExtra | * * Extra information passed in the * when the device was created. This is used by each * particular device to encode additional instance infomation. * * @field PED | ped | * * The device that owns this instance. Multiple instances * of the same device share the same . * * @field LONG | cRef | * * Reference count. * * * @field LONG | cAcquire | * * Number of times the device instance has been acquired (minus one). * * * @field BOOL | fWorkerThread | * * This is used by low-level hooks and HID devices, which * require a worker thread to collect the data. * This is not cheap, so * instead, we spin up the thread on the first acquire, and * on the unacquire, we keep the thread around so that the next * acquire is fast. When the last object is released, we finally * kill the thread. * *****************************************************************************/ typedef struct CEm { VXDINSTANCE vi; /* This must be first */ struct CEm *pemNext; LPDWORD rgdwDf; ULONG_PTR dwExtra; PED ped; LONG cAcquire; LONG cRef; #ifdef WORKER_THREAD BOOL fWorkerThread; #endif #ifdef DEBUG DWORD dwSignature; #endif BOOL fHidden; } CEm, EM, *PEM; #define CEM_SIGNATURE 0x4D4D4545 /* "EEMM" */ /***************************************************************************** * * @doc INTERNAL * * @func PEM | pemFromPvi | * * Given an interior pointer to a , retrieve * a pointer to the parent . * * @parm PVXDINSTANCE | pvi | * * The pointer to convert. * *****************************************************************************/ PEM INLINE pemFromPvi(PVXDINSTANCE pvi) { return pvSubPvCb(pvi, FIELD_OFFSET(CEm, vi)); } /***************************************************************************** * * NT low-level hook support * * Low-level hooks live on a separate thread which we spin * up when first requested and take down when the last * DirectInput device that used a thread has been destroyed. * * If we wanted, we could destroy the thread when the * device is unacquired (rather than when the device is * destroyed), but we cache the thread instead, because * a device that once has been acquired will probably be * acquired again. * * To prevent race conditions from crashing us, we addref * our DLL when the thread exists and have the thread * perform a FreeLibrary as its final act. * * Note that this helper thread is also used by the HID data * collector. * *****************************************************************************/ #ifdef USE_SLOW_LL_HOOKS /***************************************************************************** * * @doc INTERNAL * * @struct LLHOOKSTATE | * * Low-level hook information about a single hook. * * @field int | cHook | * * Number of times the hook has been requested. If zero, * then there should be no hook. All modifications to * this field must be interlocked to avoid race conditions * when two threads try to hook or unhook simultaneously. * * @field int | cExcl | * * Number of times the hook has been requested in an exclusive * mode. This value should always be less than or equal to the * cHook value. All modifications to this field must be * interlocked to avoid race conditions when two threads try to * hook or unhook simultaneously. * * @field HHOOK | hhk | * * The actual hook, if it is installed. Only the hook thread * touches this field, so it does not need to be protected. * * @field BOOLEAN | fExcluded | * * Flag to indicate whether or not exclusivity has been applied. * Only the hook thread touches this field, so it does not need to * be protected. * *****************************************************************************/ typedef struct LLHOOKSTATE { int cHook; int cExcl; HHOOK hhk; BOOLEAN fExcluded; } LLHOOKSTATE, *PLLHOOKSTATE; LRESULT CALLBACK CEm_LL_KbdHook(int nCode, WPARAM wp, LPARAM lp); LRESULT CALLBACK CEm_LL_MseHook(int nCode, WPARAM wp, LPARAM lp); #endif /* USE_SLOW_LL_HOOKS */ #ifdef WORKER_THREAD /***************************************************************************** * * @doc INTERNAL * * @struct LLTHREADSTATE | * * Low-level hook state for a thread. Note that this is * a dynamically * allocated structure instead of a static. This avoids various * race conditions where, for example, somebody terminates the * worker thread and somebody else starts it up before the * worker thread is completely gone. * * A pointer to the hThread is passed as the pointer to an array * of two handles in calls to WaitForMultipleObject so hEvent must * follow it directly. * * @field DWORD | idThread | * * The ID of the worker thread. * * @field LONG | cRef | * * Thread reference count. The thread kills itself when this * drops to zero. * * @field LLHOOKSTATE | rglhs[2] | * * Hook states, indexed by LLTS_* values. * * These are used only if low-level hooks are enabled. * * @field HANDLE | hThread | * * The handle (from the create) of the worker thread. * * This is used only if HID support is enabled. * * @field HANDLE | hEvent | * * The handle to the event used to synchronize with the worker thread. * * This is used only if HID support is enabled. * * @field GPA | gpaHid | * * Pointer array of HID devices which are acquired. * * This is used only if HID support is enabled. * * @field PEM | pemCheck | * * Pointer to Emulation state information. * * This is used only if HID support is enabled. * *****************************************************************************/ #define LLTS_KBD 0 #define LLTS_MSE 1 #define LLTS_MAX 2 typedef struct LLTHREADSTATE { DWORD idThread; LONG cRef; #ifdef USE_SLOW_LL_HOOKS LLHOOKSTATE rglhs[LLTS_MAX]; #endif #ifdef HID_SUPPORT HANDLE hThread; /* MUST be followed by hEvent, see above */ HANDLE hEvent; /* MUST follow hThread, see above */ GPA gpaHid; PEM pemCheck; #endif } LLTHREADSTATE, *PLLTHREADSTATE; /***************************************************************************** * * @doc INTERNAL * * @topic Communicating with the worker thread | * * Communication with the worker thread is performed via * messages. Extra care must be taken to make * sure that someone isn't randomly sending messages to us. * * We use the message because there are race * windows where we might post a message to a thread after * it is gone. During this window, the thread ID might get * recycled, and we end up posting the message to some random * thread that isn't ours. By using the message, * we are safe in knowing that the target thread won't barf * on the unexpected message. * * The of the is the magic value * . * * The of the is either a pointer * to the that needs to be refreshed or is * zero if we merely want to check our bearings. * *****************************************************************************/ #define WT_WPARAM 0 #define PostWorkerMessage(thid, lp) \ PostThreadMessage(thid, WM_NULL, WT_WPARAM, (LPARAM)(lp)) \ #define NudgeWorkerThread(thid) \ PostThreadMessage(thid, WM_NULL, WT_WPARAM, (LPARAM)NULL) HRESULT EXTERNAL NudgeWorkerThreadPem( PLLTHREADSTATE plts, PEM pem ); HRESULT EXTERNAL NotifyWorkerThreadPem(DWORD idThread, PEM pem); STDMETHODIMP CEm_GetWorkerThread(PEM pem, PLLTHREADSTATE *pplts); /***************************************************************************** * * @doc INTERNAL * * @global PLLTHREADSTATE | g_plts | * * The thread state of the currently-active thread. * * This variable needs to be externally accessible * because you can't pass instance data to a windows * hook function. (Whose idea was that?) * *****************************************************************************/ extern PLLTHREADSTATE g_plts; void EXTERNAL CEm_Mouse_OnMouseChange(void); #endif /* WORKER_THREAD */ /* * Private helper functions in diem.c */ #define FDUFL_NORMAL 0x0000 /* Nothing unusual */ #define FDUFL_UNPLUGGED VIFL_UNPLUGGED /* Device disconnected */ void EXTERNAL CEm_ForceDeviceUnacquire(PED ped, UINT fdufl); void EXTERNAL CEm_AddState(PED ped, LPVOID pvData, DWORD tm); DWORD EXTERNAL CEm_AddEvent(PED ped, DWORD dwData, DWORD dwOfs, DWORD tm); BOOL EXTERNAL CEm_ContinueEvent(PED ped, DWORD dwData, DWORD dwOfs, DWORD tm, DWORD dwSeq); STDMETHODIMP CEm_LL_Acquire(PEM this, BOOL fAcquire, ULONG fl, UINT ilts); HRESULT EXTERNAL CEm_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut, PED ped); void EXTERNAL CEm_FreeInstance(PEM this); /***************************************************************************** * * @doc INTERNAL * * @func void | CEm_AddRef | * * Bump the reference count because we're doing something with it. * * @parm PEM | this | * * The victim. * *****************************************************************************/ void INLINE CEm_AddRef(PEM this) { AssertF(this->dwSignature == CEM_SIGNATURE); InterlockedIncrement(&this->cRef); } /***************************************************************************** * * @doc INTERNAL * * @func void | CEm_Release | * * Drop the reference count and blow it away if it's gone. * * @parm PEM | this | * * The victim. * *****************************************************************************/ void INLINE CEm_Release(PEM this) { AssertF(this->dwSignature == CEM_SIGNATURE); if (InterlockedDecrement(&this->cRef) == 0) { CEm_FreeInstance(this); } }