mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-01 23:05:46 +00:00
![Jacek Caban](/assets/img/avatar_default.png)
Signed-off-by: Jacek Caban <jacek@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
1135 lines
32 KiB
C
1135 lines
32 KiB
C
/*
|
|
* Tablet Context
|
|
*
|
|
* Copyright 2002 Patrik Stridvall
|
|
* Copyright 2003 CodeWeavers, Aric Stewart
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "windef.h"
|
|
#include "winerror.h"
|
|
#include "winbase.h"
|
|
#include "winuser.h"
|
|
#include "winnls.h"
|
|
|
|
#include "wintab.h"
|
|
#include "wintab_internal.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(wintab32);
|
|
|
|
/*
|
|
* Documentation found at
|
|
* http://www.csl.sony.co.jp/projects/ar/restricted/wintabl.html
|
|
*/
|
|
|
|
static LPOPENCONTEXT gOpenContexts;
|
|
static HCTX gTopContext = (HCTX)0xc00;
|
|
|
|
static void LOGCONTEXTAtoW(const LOGCONTEXTA *in, LOGCONTEXTW *out)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, in->lcName, -1, out->lcName, LCNAMELEN);
|
|
out->lcName[LCNAMELEN - 1] = 0;
|
|
/* we use the fact that the fields after lcName are the same in LOGCONTEXTA and W */
|
|
memcpy(&out->lcOptions, &in->lcOptions, sizeof(LOGCONTEXTA) - FIELD_OFFSET(LOGCONTEXTA, lcOptions));
|
|
}
|
|
|
|
static void LOGCONTEXTWtoA(const LOGCONTEXTW *in, LOGCONTEXTA *out)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, in->lcName, LCNAMELEN, out->lcName, LCNAMELEN, NULL, NULL);
|
|
out->lcName[LCNAMELEN - 1] = 0;
|
|
/* we use the fact that the fields after lcName are the same in LOGCONTEXTA and W */
|
|
memcpy(&out->lcOptions, &in->lcOptions, sizeof(LOGCONTEXTW) - FIELD_OFFSET(LOGCONTEXTW, lcOptions));
|
|
}
|
|
|
|
static BOOL is_logcontext_category(UINT wCategory)
|
|
{
|
|
return wCategory == WTI_DEFSYSCTX || wCategory == WTI_DEFCONTEXT || wCategory == WTI_DDCTXS;
|
|
}
|
|
|
|
static BOOL is_string_field(UINT wCategory, UINT nIndex)
|
|
{
|
|
if (wCategory == WTI_INTERFACE && nIndex == IFC_WINTABID)
|
|
return TRUE;
|
|
if (is_logcontext_category(wCategory) && nIndex == CTX_NAME)
|
|
return TRUE;
|
|
if ((wCategory >= WTI_CURSORS && wCategory <= WTI_CURSORS + 9) &&
|
|
(nIndex == CSR_NAME || nIndex == CSR_BTNNAMES))
|
|
return TRUE;
|
|
if (wCategory == WTI_DEVICES && (nIndex == DVC_NAME || nIndex == DVC_PNPID))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static const char* DUMPBITS(int x)
|
|
{
|
|
char buf[200];
|
|
buf[0] = 0;
|
|
if (x&PK_CONTEXT) strcat(buf,"PK_CONTEXT ");
|
|
if (x&PK_STATUS) strcat(buf, "PK_STATUS ");
|
|
if (x&PK_TIME) strcat(buf, "PK_TIME ");
|
|
if (x&PK_CHANGED) strcat(buf, "PK_CHANGED ");
|
|
if (x&PK_SERIAL_NUMBER) strcat(buf, "PK_SERIAL_NUMBER ");
|
|
if (x&PK_CURSOR) strcat(buf, "PK_CURSOR ");
|
|
if (x&PK_BUTTONS) strcat(buf, "PK_BUTTONS ");
|
|
if (x&PK_X) strcat(buf, "PK_X ");
|
|
if (x&PK_Y) strcat(buf, "PK_Y ");
|
|
if (x&PK_Z) strcat(buf, "PK_Z ");
|
|
if (x&PK_NORMAL_PRESSURE) strcat(buf, "PK_NORMAL_PRESSURE ");
|
|
if (x&PK_TANGENT_PRESSURE) strcat(buf, "PK_TANGENT_PRESSURE ");
|
|
if (x&PK_ORIENTATION) strcat(buf, "PK_ORIENTATION ");
|
|
if (x&PK_ROTATION) strcat(buf, "PK_ROTATION ");
|
|
return wine_dbg_sprintf("{%s}",buf);
|
|
}
|
|
|
|
static inline void DUMPPACKET(WTPACKET packet)
|
|
{
|
|
TRACE("pkContext: %p pkStatus: 0x%x pkTime : 0x%x pkChanged: 0x%x pkSerialNumber: 0x%x pkCursor : %i pkButtons: %x pkX: %i pkY: %i pkZ: %i pkNormalPressure: %i pkTangentPressure: %i pkOrientation: (%i,%i,%i) pkRotation: (%i,%i,%i)\n",
|
|
packet.pkContext, packet.pkStatus, packet.pkTime, packet.pkChanged, packet.pkSerialNumber,
|
|
packet.pkCursor, packet.pkButtons, packet.pkX, packet.pkY, packet.pkZ,
|
|
packet.pkNormalPressure, packet.pkTangentPressure,
|
|
packet.pkOrientation.orAzimuth, packet.pkOrientation.orAltitude, packet.pkOrientation.orTwist,
|
|
packet.pkRotation.roPitch, packet.pkRotation.roRoll, packet.pkRotation.roYaw);
|
|
}
|
|
|
|
static inline void DUMPCONTEXT(LOGCONTEXTW lc)
|
|
{
|
|
TRACE("Name: %s, Options: %x, Status: %x, Locks: %x, MsgBase: %x, Device: %x\n",
|
|
wine_dbgstr_w(lc.lcName), lc.lcOptions, lc.lcStatus, lc.lcLocks, lc.lcMsgBase, lc.lcDevice);
|
|
TRACE("PktRate %x\n", lc.lcPktRate);
|
|
TRACE("PktData 0x%04x %s\n", lc.lcPktData, DUMPBITS(lc.lcPktData));
|
|
TRACE("PktMode 0x%04x %s\n", lc.lcPktMode, DUMPBITS(lc.lcPktMode));
|
|
TRACE("MovMask 0x%04x %s\n", lc.lcMoveMask, DUMPBITS(lc.lcMoveMask));
|
|
TRACE("BtnDnMask: %x, BtnUpMask: %x\n", lc.lcBtnDnMask, lc.lcBtnUpMask);
|
|
TRACE("InOrgX: %i, InOrgY: %i, InOrgZ: %i\n", lc.lcInOrgX, lc.lcInOrgY, lc.lcInOrgZ);
|
|
TRACE("InExtX: %i, InExtY: %i, InExtZ: %i\n", lc.lcInExtX, lc.lcInExtY, lc.lcInExtZ);
|
|
TRACE("OutOrgX: %i, OutOrgY: %i, OutOrgZ: %i\n", lc.lcOutOrgX, lc.lcOutOrgY, lc.lcOutOrgZ);
|
|
TRACE("OutExtX: %i, OutExtY: %i, OutExtZ: %i\n", lc.lcOutExtX, lc.lcOutExtY, lc.lcOutExtZ);
|
|
TRACE("SensX: %i, SensY: %i, SensZ: %i\n", lc.lcSensX, lc.lcSensY, lc.lcSensZ);
|
|
TRACE("SysMode: %i\n", lc.lcSysMode);
|
|
TRACE("SysOrgX: %i, SysOrgY: %i\n", lc.lcSysOrgX, lc.lcSysOrgY);
|
|
TRACE("SysExtX: %i, SysExtY: %i\n", lc.lcSysExtX, lc.lcSysExtY);
|
|
TRACE("SysSensX: %i, SysSensY: %i\n", lc.lcSysSensX, lc.lcSysSensY);
|
|
}
|
|
|
|
|
|
/* Find an open context given the handle */
|
|
static LPOPENCONTEXT TABLET_FindOpenContext(HCTX hCtx)
|
|
{
|
|
LPOPENCONTEXT ptr = gOpenContexts;
|
|
while (ptr)
|
|
{
|
|
if (ptr->handle == hCtx) return ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static inline BOOL LoadTablet(void)
|
|
{
|
|
static enum {TI_START = 0, TI_OK, TI_FAIL} loaded = TI_START;
|
|
|
|
if (loaded == TI_START)
|
|
{
|
|
if (pLoadTabletInfo && pLoadTabletInfo(hwndDefault))
|
|
{
|
|
TRACE("Initialized the tablet to hwnd %p\n", hwndDefault);
|
|
loaded = TI_OK;
|
|
}
|
|
else
|
|
{
|
|
TRACE("Failed to initialize the tablet to hwnd %p\n", hwndDefault);
|
|
loaded = TI_FAIL;
|
|
}
|
|
}
|
|
|
|
return loaded == TI_OK;
|
|
}
|
|
|
|
int TABLET_PostTabletMessage(LPOPENCONTEXT newcontext, UINT msg, WPARAM wParam,
|
|
LPARAM lParam, BOOL send_always)
|
|
{
|
|
if ((send_always) || (newcontext->context.lcOptions & CXO_MESSAGES))
|
|
{
|
|
TRACE("Posting message %x to %p\n",msg, newcontext->hwndOwner);
|
|
return PostMessageA(newcontext->hwndOwner, msg, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline DWORD ScaleForContext(DWORD In, LONG InOrg, LONG InExt, LONG OutOrg, LONG OutExt)
|
|
{
|
|
if (((InExt > 0 )&&(OutExt > 0)) || ((InExt<0) && (OutExt < 0)))
|
|
return MulDiv(In - InOrg, abs(OutExt), abs(InExt)) + OutOrg;
|
|
else
|
|
return MulDiv(abs(InExt) - (In - InOrg), abs(OutExt), abs(InExt)) + OutOrg;
|
|
}
|
|
|
|
LPOPENCONTEXT AddPacketToContextQueue(LPWTPACKET packet, HWND hwnd)
|
|
{
|
|
LPOPENCONTEXT ptr=NULL;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
ptr = gOpenContexts;
|
|
while (ptr)
|
|
{
|
|
TRACE("Trying Queue %p (%p %p)\n", ptr->handle, hwnd, ptr->hwndOwner);
|
|
|
|
if (ptr->hwndOwner == hwnd)
|
|
{
|
|
int tgt;
|
|
if (!ptr->enabled)
|
|
{
|
|
ptr = ptr->next;
|
|
continue;
|
|
}
|
|
|
|
tgt = ptr->PacketsQueued;
|
|
|
|
packet->pkContext = ptr->handle;
|
|
|
|
/* translate packet data to the context */
|
|
packet->pkChanged = packet->pkChanged & ptr->context.lcPktData;
|
|
|
|
/* Scale as per documentation */
|
|
packet->pkY = ScaleForContext(packet->pkY, ptr->context.lcInOrgY,
|
|
ptr->context.lcInExtY, ptr->context.lcOutOrgY,
|
|
ptr->context.lcOutExtY);
|
|
|
|
packet->pkX = ScaleForContext(packet->pkX, ptr->context.lcInOrgX,
|
|
ptr->context.lcInExtX, ptr->context.lcOutOrgX,
|
|
ptr->context.lcOutExtX);
|
|
|
|
/* flip the Y axis */
|
|
if (ptr->context.lcOutExtY > 0)
|
|
packet->pkY = ptr->context.lcOutExtY - packet->pkY;
|
|
else if (ptr->context.lcOutExtY < 0)
|
|
{
|
|
int y = ptr->context.lcOutExtY + packet->pkY;
|
|
packet->pkY = abs(y);
|
|
}
|
|
|
|
DUMPPACKET(*packet);
|
|
|
|
if (tgt == ptr->QueueSize)
|
|
{
|
|
TRACE("Queue Overflow %p\n",ptr->handle);
|
|
ptr->PacketQueue[tgt-1].pkStatus |= TPS_QUEUE_ERR;
|
|
}
|
|
else
|
|
{
|
|
TRACE("Placed in queue %p index %i\n",ptr->handle,tgt);
|
|
ptr->PacketQueue[tgt] = *packet;
|
|
ptr->PacketsQueued++;
|
|
|
|
if (ptr->ActiveCursor != packet->pkCursor)
|
|
{
|
|
ptr->ActiveCursor = packet->pkCursor;
|
|
if (ptr->context.lcOptions & CXO_CSRMESSAGES)
|
|
TABLET_PostTabletMessage(ptr, _WT_CSRCHANGE(ptr->context.lcMsgBase),
|
|
(WPARAM)packet->pkSerialNumber, (LPARAM)ptr->handle,
|
|
FALSE);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
ptr = ptr->next;
|
|
}
|
|
LeaveCriticalSection(&csTablet);
|
|
TRACE("Done (%p)\n",ptr);
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
* Flushes all packets from the queue.
|
|
*/
|
|
static inline void TABLET_FlushQueue(LPOPENCONTEXT context)
|
|
{
|
|
context->PacketsQueued = 0;
|
|
}
|
|
|
|
static inline int CopyTabletData(LPVOID target, LPVOID src, INT size)
|
|
{
|
|
memcpy(target,src,size);
|
|
return(size);
|
|
}
|
|
|
|
static INT TABLET_FindPacket(LPOPENCONTEXT context, UINT wSerial,
|
|
LPWTPACKET *pkt)
|
|
{
|
|
int loop;
|
|
int index = -1;
|
|
for (loop = 0; loop < context->PacketsQueued; loop++)
|
|
if (context->PacketQueue[loop].pkSerialNumber == wSerial)
|
|
{
|
|
index = loop;
|
|
*pkt = &context->PacketQueue[loop];
|
|
break;
|
|
}
|
|
|
|
TRACE("%i .. %i\n",context->PacketsQueued,index);
|
|
|
|
return index;
|
|
}
|
|
|
|
|
|
static LPVOID TABLET_CopyPacketData(LPOPENCONTEXT context, LPVOID lpPkt,
|
|
LPWTPACKET wtp)
|
|
{
|
|
LPBYTE ptr;
|
|
|
|
ptr = lpPkt;
|
|
TRACE("Packet Bits %s\n",DUMPBITS(context->context.lcPktData));
|
|
|
|
if (context->context.lcPktData & PK_CONTEXT)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkContext,sizeof(HCTX));
|
|
if (context->context.lcPktData & PK_STATUS)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkStatus,sizeof(UINT));
|
|
if (context->context.lcPktData & PK_TIME)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkTime,sizeof(LONG));
|
|
if (context->context.lcPktData & PK_CHANGED)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkChanged,sizeof(WTPKT));
|
|
if (context->context.lcPktData & PK_SERIAL_NUMBER)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkSerialNumber,sizeof(UINT));
|
|
if (context->context.lcPktData & PK_CURSOR)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkCursor,sizeof(UINT));
|
|
if (context->context.lcPktData & PK_BUTTONS)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkButtons,sizeof(DWORD));
|
|
if (context->context.lcPktData & PK_X)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkX,sizeof(DWORD));
|
|
if (context->context.lcPktData & PK_Y)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkY,sizeof(DWORD));
|
|
if (context->context.lcPktData & PK_Z)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkZ,sizeof(DWORD));
|
|
if (context->context.lcPktData & PK_NORMAL_PRESSURE)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkNormalPressure,sizeof(UINT));
|
|
if (context->context.lcPktData & PK_TANGENT_PRESSURE)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkTangentPressure,sizeof(UINT));
|
|
if (context->context.lcPktData & PK_ORIENTATION)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkOrientation,sizeof(ORIENTATION));
|
|
if (context->context.lcPktData & PK_ROTATION)
|
|
ptr+=CopyTabletData(ptr,&wtp->pkRotation,sizeof(ROTATION));
|
|
|
|
/*TRACE("Copied %i bytes\n",(INT)ptr - (INT)lpPkt); */
|
|
return ptr;
|
|
}
|
|
|
|
static VOID TABLET_BlankPacketData(LPOPENCONTEXT context, LPVOID lpPkt, INT n)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (context->context.lcPktData & PK_CONTEXT)
|
|
rc +=sizeof(HCTX);
|
|
if (context->context.lcPktData & PK_STATUS)
|
|
rc +=sizeof(UINT);
|
|
if (context->context.lcPktData & PK_TIME)
|
|
rc += sizeof(LONG);
|
|
if (context->context.lcPktData & PK_CHANGED)
|
|
rc += sizeof(WTPKT);
|
|
if (context->context.lcPktData & PK_SERIAL_NUMBER)
|
|
rc += sizeof(UINT);
|
|
if (context->context.lcPktData & PK_CURSOR)
|
|
rc += sizeof(UINT);
|
|
if (context->context.lcPktData & PK_BUTTONS)
|
|
rc += sizeof(DWORD);
|
|
if (context->context.lcPktData & PK_X)
|
|
rc += sizeof(DWORD);
|
|
if (context->context.lcPktData & PK_Y)
|
|
rc += sizeof(DWORD);
|
|
if (context->context.lcPktData & PK_Z)
|
|
rc += sizeof(DWORD);
|
|
if (context->context.lcPktData & PK_NORMAL_PRESSURE)
|
|
rc += sizeof(UINT);
|
|
if (context->context.lcPktData & PK_TANGENT_PRESSURE)
|
|
rc += sizeof(UINT);
|
|
if (context->context.lcPktData & PK_ORIENTATION)
|
|
rc += sizeof(ORIENTATION);
|
|
if (context->context.lcPktData & PK_ROTATION)
|
|
rc += sizeof(ROTATION);
|
|
|
|
rc *= n;
|
|
memset(lpPkt,0,rc);
|
|
}
|
|
|
|
|
|
static UINT WTInfoT(UINT wCategory, UINT nIndex, LPVOID lpOutput, BOOL bUnicode)
|
|
{
|
|
UINT result;
|
|
|
|
if (!LoadTablet()) return 0;
|
|
|
|
TRACE("(%d, %d, %p, %d)\n", wCategory, nIndex, lpOutput, bUnicode);
|
|
|
|
/*
|
|
* Handle system extents here, as we can use user32.dll code to set them.
|
|
*/
|
|
if(wCategory == WTI_DEFSYSCTX)
|
|
{
|
|
switch(nIndex)
|
|
{
|
|
case CTX_SYSEXTX:
|
|
if(lpOutput != NULL)
|
|
*(LONG*)lpOutput = GetSystemMetrics(SM_CXSCREEN);
|
|
return sizeof(LONG);
|
|
case CTX_SYSEXTY:
|
|
if(lpOutput != NULL)
|
|
*(LONG*)lpOutput = GetSystemMetrics(SM_CYSCREEN);
|
|
return sizeof(LONG);
|
|
/* No action, delegate to X11Drv */
|
|
}
|
|
}
|
|
|
|
if (is_logcontext_category(wCategory) && nIndex == 0)
|
|
{
|
|
if (lpOutput)
|
|
{
|
|
LOGCONTEXTW buf;
|
|
pWTInfoW(wCategory, nIndex, &buf);
|
|
|
|
/* Handle system extents here, as we can use user32.dll code to set them */
|
|
if(wCategory == WTI_DEFSYSCTX)
|
|
{
|
|
buf.lcSysExtX = GetSystemMetrics(SM_CXSCREEN);
|
|
buf.lcSysExtY = GetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
|
|
if (bUnicode)
|
|
memcpy(lpOutput, &buf, sizeof(buf));
|
|
else
|
|
LOGCONTEXTWtoA(&buf, lpOutput);
|
|
}
|
|
|
|
result = bUnicode ? sizeof(LOGCONTEXTW) : sizeof(LOGCONTEXTA);
|
|
}
|
|
else if (is_string_field(wCategory, nIndex) && !bUnicode)
|
|
{
|
|
int size = pWTInfoW(wCategory, nIndex, NULL);
|
|
WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, size);
|
|
pWTInfoW(wCategory, nIndex, buf);
|
|
result = WideCharToMultiByte(CP_ACP, 0, buf, size/sizeof(WCHAR), lpOutput, lpOutput ? 2*size : 0, NULL, NULL);
|
|
HeapFree(GetProcessHeap(), 0, buf);
|
|
}
|
|
else
|
|
result = pWTInfoW(wCategory, nIndex, lpOutput);
|
|
|
|
TRACE("returns %d\n", result);
|
|
return result;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTInfoA (WINTAB32.20)
|
|
*/
|
|
UINT WINAPI WTInfoA(UINT wCategory, UINT nIndex, LPVOID lpOutput)
|
|
{
|
|
return WTInfoT(wCategory, nIndex, lpOutput, FALSE);
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* WTInfoW (WINTAB32.1020)
|
|
*/
|
|
UINT WINAPI WTInfoW(UINT wCategory, UINT nIndex, LPVOID lpOutput)
|
|
{
|
|
return WTInfoT(wCategory, nIndex, lpOutput, TRUE);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTOpenW (WINTAB32.1021)
|
|
*/
|
|
HCTX WINAPI WTOpenW(HWND hWnd, LPLOGCONTEXTW lpLogCtx, BOOL fEnable)
|
|
{
|
|
LPOPENCONTEXT newcontext;
|
|
|
|
if (!LoadTablet()) return 0;
|
|
|
|
TRACE("hWnd=%p, lpLogCtx=%p, fEnable=%u\n", hWnd, lpLogCtx, fEnable);
|
|
DUMPCONTEXT(*lpLogCtx);
|
|
|
|
newcontext = HeapAlloc(GetProcessHeap(), 0 , sizeof(OPENCONTEXT));
|
|
newcontext->context = *lpLogCtx;
|
|
newcontext->hwndOwner = hWnd;
|
|
newcontext->ActiveCursor = -1;
|
|
newcontext->QueueSize = 10;
|
|
newcontext->PacketsQueued = 0;
|
|
newcontext->PacketQueue=HeapAlloc(GetProcessHeap(),0,sizeof(WTPACKET)*10);
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
newcontext->handle = gTopContext++;
|
|
newcontext->next = gOpenContexts;
|
|
gOpenContexts = newcontext;
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
pAttachEventQueueToTablet(hWnd);
|
|
|
|
TABLET_PostTabletMessage(newcontext, _WT_CTXOPEN(newcontext->context.lcMsgBase), (WPARAM)newcontext->handle,
|
|
newcontext->context.lcStatus, TRUE);
|
|
|
|
if (fEnable)
|
|
{
|
|
newcontext->enabled = TRUE;
|
|
/* TODO: Add to top of overlap order */
|
|
newcontext->context.lcStatus = CXS_ONTOP;
|
|
}
|
|
else
|
|
{
|
|
newcontext->enabled = FALSE;
|
|
newcontext->context.lcStatus = CXS_DISABLED;
|
|
}
|
|
|
|
TABLET_PostTabletMessage(newcontext, _WT_CTXOVERLAP(newcontext->context.lcMsgBase),
|
|
(WPARAM)newcontext->handle,
|
|
newcontext->context.lcStatus, TRUE);
|
|
|
|
return newcontext->handle;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTOpenA (WINTAB32.21)
|
|
*/
|
|
HCTX WINAPI WTOpenA(HWND hWnd, LPLOGCONTEXTA lpLogCtx, BOOL fEnable)
|
|
{
|
|
LOGCONTEXTW logCtxW;
|
|
|
|
LOGCONTEXTAtoW(lpLogCtx, &logCtxW);
|
|
return WTOpenW(hWnd, &logCtxW, fEnable);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTClose (WINTAB32.22)
|
|
*/
|
|
BOOL WINAPI WTClose(HCTX hCtx)
|
|
{
|
|
LPOPENCONTEXT context,ptr;
|
|
|
|
TRACE("(%p)\n", hCtx);
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
ptr = context = gOpenContexts;
|
|
|
|
while (context && (context->handle != hCtx))
|
|
{
|
|
ptr = context;
|
|
context = context->next;
|
|
}
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return TRUE;
|
|
}
|
|
|
|
if (context == gOpenContexts)
|
|
gOpenContexts = context->next;
|
|
else
|
|
ptr->next = context->next;
|
|
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
TABLET_PostTabletMessage(context, _WT_CTXCLOSE(context->context.lcMsgBase), (WPARAM)context->handle,
|
|
context->context.lcStatus,TRUE);
|
|
|
|
HeapFree(GetProcessHeap(),0,context->PacketQueue);
|
|
HeapFree(GetProcessHeap(),0,context);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTPacketsGet (WINTAB32.23)
|
|
*/
|
|
int WINAPI WTPacketsGet(HCTX hCtx, int cMaxPkts, LPVOID lpPkts)
|
|
{
|
|
int limit;
|
|
LPOPENCONTEXT context;
|
|
LPVOID ptr = lpPkts;
|
|
|
|
TRACE("(%p, %d, %p)\n", hCtx, cMaxPkts, lpPkts);
|
|
|
|
if (!hCtx)
|
|
return 0;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return 0;
|
|
}
|
|
|
|
if (lpPkts != NULL)
|
|
TABLET_BlankPacketData(context,lpPkts,cMaxPkts);
|
|
|
|
if (context->PacketsQueued == 0)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return 0;
|
|
}
|
|
|
|
limit = min(cMaxPkts,context->PacketsQueued);
|
|
|
|
if(ptr != NULL)
|
|
{
|
|
int i = 0;
|
|
for(i = 0; i < limit; i++)
|
|
ptr=TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[i]);
|
|
}
|
|
|
|
|
|
if (limit < context->PacketsQueued)
|
|
{
|
|
memmove(context->PacketQueue, &context->PacketQueue[limit],
|
|
(context->PacketsQueued - (limit))*sizeof(WTPACKET));
|
|
}
|
|
context->PacketsQueued -= limit;
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
TRACE("Copied %i packets\n",limit);
|
|
|
|
return limit;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTPacket (WINTAB32.24)
|
|
*/
|
|
BOOL WINAPI WTPacket(HCTX hCtx, UINT wSerial, LPVOID lpPkt)
|
|
{
|
|
int rc = 0;
|
|
LPOPENCONTEXT context;
|
|
LPWTPACKET wtp = NULL;
|
|
|
|
TRACE("(%p, %d, %p)\n", hCtx, wSerial, lpPkt);
|
|
|
|
if (!hCtx)
|
|
return FALSE;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
rc = TABLET_FindPacket(context ,wSerial, &wtp);
|
|
|
|
if (rc >= 0)
|
|
{
|
|
if (lpPkt)
|
|
TABLET_CopyPacketData(context ,lpPkt, wtp);
|
|
|
|
if ((rc+1) < context->QueueSize)
|
|
{
|
|
memmove(context->PacketQueue, &context->PacketQueue[rc+1],
|
|
(context->PacketsQueued - (rc+1))*sizeof(WTPACKET));
|
|
}
|
|
context->PacketsQueued -= (rc+1);
|
|
}
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
TRACE("Returning %i\n",rc+1);
|
|
return rc+1;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTEnable (WINTAB32.40)
|
|
*/
|
|
BOOL WINAPI WTEnable(HCTX hCtx, BOOL fEnable)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("hCtx=%p, fEnable=%u\n", hCtx, fEnable);
|
|
|
|
if (!hCtx) return FALSE;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
/* if we want to enable and it is not enabled then */
|
|
if(fEnable && !context->enabled)
|
|
{
|
|
context->enabled = TRUE;
|
|
/* TODO: Add to top of overlap order */
|
|
context->context.lcStatus = CXS_ONTOP;
|
|
TABLET_PostTabletMessage(context,
|
|
_WT_CTXOVERLAP(context->context.lcMsgBase),
|
|
(WPARAM)context->handle,
|
|
context->context.lcStatus, TRUE);
|
|
}
|
|
/* if we want to disable and it is not disabled then */
|
|
else if (!fEnable && context->enabled)
|
|
{
|
|
context->enabled = FALSE;
|
|
/* TODO: Remove from overlap order?? needs a test */
|
|
context->context.lcStatus = CXS_DISABLED;
|
|
TABLET_FlushQueue(context);
|
|
TABLET_PostTabletMessage(context,
|
|
_WT_CTXOVERLAP(context->context.lcMsgBase),
|
|
(WPARAM)context->handle,
|
|
context->context.lcStatus, TRUE);
|
|
}
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTOverlap (WINTAB32.41)
|
|
*
|
|
* Move context to top or bottom of overlap order
|
|
*/
|
|
BOOL WINAPI WTOverlap(HCTX hCtx, BOOL fToTop)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("hCtx=%p, fToTop=%u\n", hCtx, fToTop);
|
|
|
|
if (!hCtx) return FALSE;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
/* if we want to send to top and it's not already there */
|
|
if (fToTop && context->context.lcStatus != CXS_ONTOP)
|
|
{
|
|
/* TODO: Move context to top of overlap order */
|
|
FIXME("Not moving context to top of overlap order\n");
|
|
context->context.lcStatus = CXS_ONTOP;
|
|
TABLET_PostTabletMessage(context,
|
|
_WT_CTXOVERLAP(context->context.lcMsgBase),
|
|
(WPARAM)context->handle,
|
|
context->context.lcStatus, TRUE);
|
|
}
|
|
else if (!fToTop)
|
|
{
|
|
/* TODO: Move context to bottom of overlap order */
|
|
FIXME("Not moving context to bottom of overlap order\n");
|
|
context->context.lcStatus = CXS_OBSCURED;
|
|
TABLET_PostTabletMessage(context,
|
|
_WT_CTXOVERLAP(context->context.lcMsgBase),
|
|
(WPARAM)context->handle,
|
|
context->context.lcStatus, TRUE);
|
|
}
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTConfig (WINTAB32.60)
|
|
*/
|
|
BOOL WINAPI WTConfig(HCTX hCtx, HWND hWnd)
|
|
{
|
|
FIXME("(%p, %p): stub\n", hCtx, hWnd);
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTGetA (WINTAB32.61)
|
|
*/
|
|
BOOL WINAPI WTGetA(HCTX hCtx, LPLOGCONTEXTA lpLogCtx)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("(%p, %p)\n", hCtx, lpLogCtx);
|
|
|
|
if (!hCtx) return FALSE;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
LOGCONTEXTWtoA(&context->context, lpLogCtx);
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTGetW (WINTAB32.1061)
|
|
*/
|
|
BOOL WINAPI WTGetW(HCTX hCtx, LPLOGCONTEXTW lpLogCtx)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("(%p, %p)\n", hCtx, lpLogCtx);
|
|
|
|
if (!hCtx) return FALSE;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
memmove(lpLogCtx,&context->context,sizeof(LOGCONTEXTW));
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTSetA (WINTAB32.62)
|
|
*/
|
|
BOOL WINAPI WTSetA(HCTX hCtx, LPLOGCONTEXTA lpLogCtx)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("hCtx=%p, lpLogCtx=%p\n", hCtx, lpLogCtx);
|
|
|
|
if (!hCtx || !lpLogCtx) return FALSE;
|
|
|
|
/* TODO: if cur process not owner of hCtx only modify
|
|
* attribs not locked by owner */
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
LOGCONTEXTAtoW(lpLogCtx, &context->context);
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTSetW (WINTAB32.1062)
|
|
*/
|
|
BOOL WINAPI WTSetW(HCTX hCtx, LPLOGCONTEXTW lpLogCtx)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("hCtx=%p, lpLogCtx=%p\n", hCtx, lpLogCtx);
|
|
|
|
if (!hCtx || !lpLogCtx) return FALSE;
|
|
|
|
/* TODO: if cur process not hCtx owner only modify
|
|
* attribs not locked by owner */
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
memmove(&context->context, lpLogCtx, sizeof(LOGCONTEXTW));
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTExtGet (WINTAB32.63)
|
|
*/
|
|
BOOL WINAPI WTExtGet(HCTX hCtx, UINT wExt, LPVOID lpData)
|
|
{
|
|
FIXME("(%p, %u, %p): stub\n", hCtx, wExt, lpData);
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTExtSet (WINTAB32.64)
|
|
*/
|
|
BOOL WINAPI WTExtSet(HCTX hCtx, UINT wExt, LPVOID lpData)
|
|
{
|
|
FIXME("(%p, %u, %p): stub\n", hCtx, wExt, lpData);
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTSave (WINTAB32.65)
|
|
*/
|
|
BOOL WINAPI WTSave(HCTX hCtx, LPVOID lpSaveInfo)
|
|
{
|
|
FIXME("(%p, %p): stub\n", hCtx, lpSaveInfo);
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTRestore (WINTAB32.66)
|
|
*/
|
|
HCTX WINAPI WTRestore(HWND hWnd, LPVOID lpSaveInfo, BOOL fEnable)
|
|
{
|
|
FIXME("(%p, %p, %u): stub\n", hWnd, lpSaveInfo, fEnable);
|
|
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTPacketsPeek (WINTAB32.80)
|
|
*/
|
|
int WINAPI WTPacketsPeek(HCTX hCtx, int cMaxPkts, LPVOID lpPkts)
|
|
{
|
|
int limit;
|
|
LPOPENCONTEXT context;
|
|
LPVOID ptr = lpPkts;
|
|
|
|
TRACE("(%p, %d, %p)\n", hCtx, cMaxPkts, lpPkts);
|
|
|
|
if (!hCtx || !lpPkts) return 0;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
|
|
if (!context || context->PacketsQueued == 0)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return 0;
|
|
}
|
|
|
|
for (limit = 0; limit < cMaxPkts && limit < context->PacketsQueued; limit++)
|
|
ptr = TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[limit]);
|
|
|
|
LeaveCriticalSection(&csTablet);
|
|
TRACE("Copied %i packets\n",limit);
|
|
return limit;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTDataGet (WINTAB32.81)
|
|
*/
|
|
int WINAPI WTDataGet(HCTX hCtx, UINT wBegin, UINT wEnd,
|
|
int cMaxPkts, LPVOID lpPkts, LPINT lpNPkts)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
LPVOID ptr = lpPkts;
|
|
INT bgn = 0;
|
|
INT end = 0;
|
|
INT num = 0;
|
|
|
|
TRACE("(%p, %u, %u, %d, %p, %p)\n",
|
|
hCtx, wBegin, wEnd, cMaxPkts, lpPkts, lpNPkts);
|
|
|
|
if (!hCtx) return 0;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
|
|
if (!context || context->PacketsQueued == 0)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return 0;
|
|
}
|
|
|
|
while (bgn < context->PacketsQueued &&
|
|
context->PacketQueue[bgn].pkSerialNumber != wBegin)
|
|
bgn++;
|
|
|
|
end = bgn;
|
|
while (end < context->PacketsQueued &&
|
|
context->PacketQueue[end].pkSerialNumber != wEnd)
|
|
end++;
|
|
|
|
if ((bgn == end) && (end == context->PacketsQueued))
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return 0;
|
|
}
|
|
|
|
for (num = bgn; num <= end; num++)
|
|
ptr = TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[num]);
|
|
|
|
/* remove read packets */
|
|
if ((end+1) < context->PacketsQueued)
|
|
memmove( &context->PacketQueue[bgn], &context->PacketQueue[end+1],
|
|
(context->PacketsQueued - (end+1)) * sizeof (WTPACKET));
|
|
|
|
context->PacketsQueued -= ((end-bgn)+1);
|
|
*lpNPkts = ((end-bgn)+1);
|
|
|
|
LeaveCriticalSection(&csTablet);
|
|
TRACE("Copied %i packets\n",*lpNPkts);
|
|
return (end - bgn)+1;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTDataPeek (WINTAB32.82)
|
|
*/
|
|
int WINAPI WTDataPeek(HCTX hCtx, UINT wBegin, UINT wEnd,
|
|
int cMaxPkts, LPVOID lpPkts, LPINT lpNPkts)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
LPVOID ptr = lpPkts;
|
|
INT bgn = 0;
|
|
INT end = 0;
|
|
INT num = 0;
|
|
|
|
TRACE("(%p, %u, %u, %d, %p, %p)\n",
|
|
hCtx, wBegin, wEnd, cMaxPkts, lpPkts, lpNPkts);
|
|
|
|
if (!hCtx || !lpPkts) return 0;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
|
|
if (!context || context->PacketsQueued == 0)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return 0;
|
|
}
|
|
|
|
while (bgn < context->PacketsQueued &&
|
|
context->PacketQueue[bgn].pkSerialNumber != wBegin)
|
|
bgn++;
|
|
|
|
end = bgn;
|
|
while (end < context->PacketsQueued &&
|
|
context->PacketQueue[end].pkSerialNumber != wEnd)
|
|
end++;
|
|
|
|
if (bgn == context->PacketsQueued || end == context->PacketsQueued)
|
|
{
|
|
TRACE("%i %i %i\n", bgn, end, context->PacketsQueued);
|
|
LeaveCriticalSection(&csTablet);
|
|
return 0;
|
|
}
|
|
|
|
for (num = bgn; num <= end; num++)
|
|
ptr = TABLET_CopyPacketData(context ,ptr, &context->PacketQueue[num]);
|
|
|
|
*lpNPkts = ((end-bgn)+1);
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
TRACE("Copied %i packets\n",*lpNPkts);
|
|
return (end - bgn)+1;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTQueuePacketsEx (WINTAB32.200)
|
|
*/
|
|
BOOL WINAPI WTQueuePacketsEx(HCTX hCtx, UINT *lpOld, UINT *lpNew)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("(%p, %p, %p)\n", hCtx, lpOld, lpNew);
|
|
|
|
if (!hCtx) return FALSE;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
|
|
if (context && context->PacketsQueued)
|
|
{
|
|
*lpOld = context->PacketQueue[0].pkSerialNumber;
|
|
*lpNew = context->PacketQueue[context->PacketsQueued-1].pkSerialNumber;
|
|
}
|
|
else
|
|
{
|
|
TRACE("No packets\n");
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTQueueSizeGet (WINTAB32.84)
|
|
*/
|
|
int WINAPI WTQueueSizeGet(HCTX hCtx)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
int queueSize = 0;
|
|
|
|
TRACE("(%p)\n", hCtx);
|
|
|
|
if (!hCtx) return 0;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (context)
|
|
queueSize = context->QueueSize;
|
|
LeaveCriticalSection(&csTablet);
|
|
return queueSize;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WTQueueSizeSet (WINTAB32.85)
|
|
*/
|
|
BOOL WINAPI WTQueueSizeSet(HCTX hCtx, int nPkts)
|
|
{
|
|
LPOPENCONTEXT context;
|
|
|
|
TRACE("(%p, %d)\n", hCtx, nPkts);
|
|
|
|
if (!hCtx) return FALSE;
|
|
|
|
EnterCriticalSection(&csTablet);
|
|
|
|
context = TABLET_FindOpenContext(hCtx);
|
|
if (!context)
|
|
{
|
|
LeaveCriticalSection(&csTablet);
|
|
return FALSE;
|
|
}
|
|
|
|
context->PacketQueue = HeapReAlloc(GetProcessHeap(), 0,
|
|
context->PacketQueue, sizeof(WTPACKET)*nPkts);
|
|
|
|
context->QueueSize = nPkts;
|
|
LeaveCriticalSection(&csTablet);
|
|
|
|
return nPkts;
|
|
}
|