You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
425 lines
11 KiB
425 lines
11 KiB
/*------------------------------------------------------------------------------
|
|
SysPress - using pressure input with a system context.
|
|
RICO 6/7/93
|
|
------------------------------------------------------------------------------*/
|
|
|
|
#include <windows.h>
|
|
#include "msgpack.h"
|
|
#include <wintab.h>
|
|
#define PACKETDATA (PK_CURSOR | PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE)
|
|
#define PACKETMODE 0
|
|
#include <pktdef.h>
|
|
#include "syspress.h"
|
|
#ifdef USE_X_LIB
|
|
#include <wintabx.h>
|
|
#endif
|
|
|
|
HANDLE hInst;
|
|
|
|
#define NPACKETQSIZE 32 /* set as needed for your app. */
|
|
PACKET localPacketBuf[NPACKETQSIZE];
|
|
|
|
static HCTX hTab = NULL;
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
|
|
HANDLE hInstance;
|
|
HANDLE hPrevInstance;
|
|
LPSTR lpCmdLine;
|
|
int nCmdShow;
|
|
{
|
|
MSG msg;
|
|
|
|
if (!hPrevInstance)
|
|
if (!InitApplication(hInstance))
|
|
return (FALSE);
|
|
|
|
/* Perform initializations that apply to a specific instance */
|
|
|
|
if (!InitInstance(hInstance, nCmdShow))
|
|
return (FALSE);
|
|
|
|
/* Acquire and dispatch messages until a WM_QUIT message is received. */
|
|
|
|
while (GetMessage(&msg,
|
|
NULL,
|
|
0,
|
|
0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
return (msg.wParam);
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|
|
BOOL InitApplication(hInstance)
|
|
HANDLE hInstance;
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
/* Fill in window class structure with parameters that describe the */
|
|
/* main window. */
|
|
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = MainWndProc;
|
|
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
|
|
wc.lpszMenuName = "SysPressMenu";
|
|
wc.lpszClassName = "SysPressWClass";
|
|
|
|
/* Register the window class and return success/failure code. */
|
|
|
|
return (RegisterClass(&wc));
|
|
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|
|
BOOL InitInstance(hInstance, nCmdShow)
|
|
HANDLE hInstance;
|
|
int nCmdShow;
|
|
{
|
|
HWND hWnd;
|
|
char buf[50];
|
|
|
|
/* Save the instance handle in static variable, which will be used in */
|
|
/* many subsequence calls from this application to Windows. */
|
|
|
|
hInst = hInstance;
|
|
|
|
/* check if WinTab available. */
|
|
if (!WTInfo(0, 0, NULL)) {
|
|
MessageBox(NULL, "WinTab Services Not Available.", "WinTab",
|
|
MB_OK | MB_ICONHAND);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Create a main window for this application instance. */
|
|
|
|
wsprintf(buf, "SysPress:%x", hInst);
|
|
hWnd = CreateWindow(
|
|
"SysPressWClass",
|
|
buf,
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
NULL,
|
|
NULL,
|
|
hInstance,
|
|
NULL
|
|
);
|
|
|
|
/* If window could not be created, return "failure" */
|
|
|
|
if (!hWnd)
|
|
return (FALSE);
|
|
|
|
/* Make the window visible; update its client area; and return "success" */
|
|
|
|
ShowWindow(hWnd, nCmdShow);
|
|
UpdateWindow(hWnd);
|
|
return (TRUE);
|
|
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|
|
HCTX static NEAR TabletInit(HWND hWnd)
|
|
{
|
|
LOGCONTEXT lcMine;
|
|
HCTX hResult;
|
|
|
|
/* get default system context. */
|
|
WTInfo(WTI_DEFSYSCTX, 0, &lcMine);
|
|
|
|
/* modify the digitizing context */
|
|
wsprintf(lcMine.lcName, "SysPress Digitizing %x", hInst);
|
|
|
|
/* same define constants from top of file used in context definition. */
|
|
lcMine.lcPktData = PACKETDATA;
|
|
lcMine.lcPktMode = PACKETMODE;
|
|
lcMine.lcMoveMask = PACKETDATA;
|
|
|
|
/* inherit default button down mask. */
|
|
/* allows tablet control panel to reserve some buttons for special */
|
|
/* functions. */
|
|
/* make sure we get button up AND down reports for all buttons we watch. */
|
|
lcMine.lcBtnUpMask = lcMine.lcBtnDnMask;
|
|
|
|
/* x & y match mouse points. */
|
|
lcMine.lcOutOrgX = 0;
|
|
lcMine.lcOutExtX = GetSystemMetrics(SM_CXSCREEN);
|
|
lcMine.lcOutOrgY = 0;
|
|
/* the negative sign here reverses the sense of the Y axis. */
|
|
/* WinTab uses a lower-left origin; MM_TEXT uses upper left. */
|
|
lcMine.lcOutExtY = -GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
/* open the context */
|
|
hResult = WTOpen(hWnd, &lcMine, TRUE);
|
|
|
|
/* Q size set needs error-checking, but skipped here for */
|
|
/* simplicity's sake. */
|
|
WTQueueSizeSet(hResult, NPACKETQSIZE);
|
|
|
|
return hResult;
|
|
}
|
|
/*------------------------------------------------------------------------------
|
|
The functions PrsInit and PrsAdjust make sure that our pressure out can always
|
|
reach the full 0-255 range we desire, regardless of the button pressed or the
|
|
"pressure button marks" settings.
|
|
------------------------------------------------------------------------------*/
|
|
/* pressure adjuster local state. */
|
|
/* need wOldCsr = -1, so PrsAdjust will call PrsInit first time */
|
|
static UINT wActiveCsr = 0, wOldCsr = (UINT)-1;
|
|
static BYTE wPrsBtn;
|
|
static UINT prsYesBtnOrg, prsYesBtnExt, prsNoBtnOrg, prsNoBtnExt;
|
|
/* -------------------------------------------------------------------------- */
|
|
void PrsInit(void)
|
|
{
|
|
/* browse WinTab's many info items to discover pressure handling. */
|
|
AXIS np;
|
|
LOGCONTEXT lc;
|
|
BYTE logBtns[32];
|
|
UINT btnMarks[2];
|
|
UINT size;
|
|
|
|
/* discover the LOGICAL button generated by the pressure channel. */
|
|
/* get the PHYSICAL button from the cursor category and run it */
|
|
/* through that cursor's button map (usually the identity map). */
|
|
wPrsBtn = (BYTE)-1;
|
|
WTInfo(WTI_CURSORS + wActiveCsr, CSR_NPBUTTON, &wPrsBtn);
|
|
size = WTInfo(WTI_CURSORS + wActiveCsr, CSR_BUTTONMAP, &logBtns);
|
|
if ((UINT)wPrsBtn < size)
|
|
wPrsBtn = logBtns[wPrsBtn];
|
|
|
|
/* get the current context for its device variable. */
|
|
WTGet(hTab, &lc);
|
|
|
|
/* get the size of the pressure axis. */
|
|
WTInfo(WTI_DEVICES + lc.lcDevice, DVC_NPRESSURE, &np);
|
|
prsNoBtnOrg = (UINT)np.axMin;
|
|
prsNoBtnExt = (UINT)np.axMax - (UINT)np.axMin;
|
|
|
|
/* get the button marks (up & down generation thresholds) */
|
|
/* and calculate what pressure range we get when pressure-button is down. */
|
|
btnMarks[1] = 0; /* default if info item not present. */
|
|
WTInfo(WTI_CURSORS + wActiveCsr, CSR_NPBTNMARKS, btnMarks);
|
|
prsYesBtnOrg = btnMarks[1];
|
|
prsYesBtnExt = (UINT)np.axMax - btnMarks[1];
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|
|
UINT PrsAdjust(PACKET p)
|
|
{
|
|
UINT wResult;
|
|
|
|
wActiveCsr = p.pkCursor;
|
|
if (wActiveCsr != wOldCsr) {
|
|
|
|
/* re-init on cursor change. */
|
|
PrsInit();
|
|
wOldCsr = wActiveCsr;
|
|
}
|
|
|
|
/* scaling output range is 0-255 */
|
|
|
|
if (p.pkButtons & (1 << wPrsBtn)) {
|
|
/* if pressure-activated button is down, */
|
|
/* scale pressure output to compensate btn marks */
|
|
wResult = p.pkNormalPressure - prsYesBtnOrg;
|
|
wResult = MulDiv(wResult, 255, prsYesBtnExt);
|
|
}
|
|
else {
|
|
/* if pressure-activated button is not down, */
|
|
/* scale pressure output directly */
|
|
wResult = p.pkNormalPressure - prsNoBtnOrg;
|
|
wResult = MulDiv(wResult, 255, prsNoBtnExt);
|
|
}
|
|
|
|
return wResult;
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|
|
LRESULT FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
|
|
HWND hWnd;
|
|
unsigned message;
|
|
WPARAM wParam;
|
|
LPARAM lParam;
|
|
{
|
|
FARPROC lpProcAbout;
|
|
static POINT ptOrg;
|
|
static POINT ptOld, ptNew;
|
|
static UINT prsOld, prsNew;
|
|
static DWORD btnOld, btnNew;
|
|
HDC hDC;
|
|
int nPackets;
|
|
BOOL fHandled = TRUE;
|
|
LRESULT lResult = 0L;
|
|
|
|
|
|
switch (message) {
|
|
|
|
case WM_CREATE:
|
|
hTab = TabletInit(hWnd);
|
|
if (!hTab) {
|
|
MessageBox(NULL, " Could Not Open Tablet Context.", "WinTab",
|
|
MB_OK | MB_ICONHAND);
|
|
|
|
SendMessage(hWnd, WM_DESTROY, 0, 0L);
|
|
}
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
case WM_MOVE:
|
|
ptOrg.x = ptOrg.y = 0;
|
|
ClientToScreen(hWnd, &ptOrg);
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
break;
|
|
|
|
case WM_ACTIVATE:
|
|
/* This call puts your context (active area) on top the */
|
|
/* context overlap order. */
|
|
if (hTab && GET_WM_ACTIVATE_STATE(wParam, lParam))
|
|
WTOverlap(hTab, TRUE);
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
case WM_MBUTTONDBLCLK:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_RBUTTONDBLCLK:
|
|
/* ANY mouse msg */
|
|
|
|
if (IsIconic(hWnd))
|
|
break;
|
|
|
|
/* empty the queue to a local buffer. */
|
|
if (nPackets = WTPacketsGet(hTab, NPACKETQSIZE, &localPacketBuf)) {
|
|
int i;
|
|
|
|
if (hDC = GetDC(hWnd)) {
|
|
HBRUSH hBrOld = NULL;
|
|
HPEN hPenOld;
|
|
|
|
hPenOld = SelectObject(hDC, GetStockObject(NULL_PEN));
|
|
|
|
for (i = 0; i < nPackets; i++) {
|
|
DWORD btnChange;
|
|
|
|
btnOld = btnNew;
|
|
btnNew = localPacketBuf[i].pkButtons;
|
|
btnChange = btnOld ^ btnNew;
|
|
|
|
if (btnNew & btnChange) {
|
|
/* a button went down. */
|
|
MessageBeep(0);
|
|
}
|
|
|
|
if (btnNew) {
|
|
|
|
/* save last packet's data. */
|
|
ptOld = ptNew;
|
|
prsOld = prsNew;
|
|
|
|
/* get new point. */
|
|
ptNew.x = (UINT)localPacketBuf[i].pkX;
|
|
ptNew.y = (UINT)localPacketBuf[i].pkY;
|
|
|
|
/* translate coordinates to client area. */
|
|
ptNew.x -= ptOrg.x;
|
|
ptNew.y -= ptOrg.y;
|
|
|
|
prsNew = PrsAdjust(localPacketBuf[i]);
|
|
if (hBrOld == NULL) {
|
|
hBrOld = SelectObject(hDC,
|
|
CreateSolidBrush(
|
|
RGB(prsNew, prsNew, prsNew)));
|
|
}
|
|
else if (prsNew != prsOld) {
|
|
DeleteObject(SelectObject(hDC, hBrOld));
|
|
hBrOld = SelectObject(hDC,
|
|
CreateSolidBrush(
|
|
RGB(prsNew, prsNew, prsNew)));
|
|
}
|
|
if (ptNew.x != ptOld.x ||
|
|
ptNew.y != ptOld.y ||
|
|
prsNew != prsOld) {
|
|
Ellipse(hDC, ptNew.x - 3, ptNew.y - 3,
|
|
ptNew.x + 3, ptNew.y + 3);
|
|
}
|
|
}
|
|
}
|
|
SelectObject(hDC, hPenOld);
|
|
if (hBrOld)
|
|
DeleteObject(SelectObject(hDC, hBrOld));
|
|
ReleaseDC(hWnd, hDC);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
if (GET_WM_COMMAND_ID(wParam, lParam) == IDM_ABOUT) {
|
|
lpProcAbout = MakeProcInstance(About, hInst);
|
|
|
|
DialogBox(hInst,
|
|
"AboutBox",
|
|
hWnd,
|
|
lpProcAbout);
|
|
|
|
FreeProcInstance(lpProcAbout);
|
|
}
|
|
else
|
|
fHandled = FALSE;
|
|
break;
|
|
|
|
case WT_INFOCHANGE: /* IF some info item changed */
|
|
PrsInit(); /* update the pressure mapping state vars. */
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (hTab)
|
|
WTClose(hTab);
|
|
#ifdef USE_X_LIB
|
|
_UnlinkWintab();
|
|
#endif
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
default:
|
|
fHandled = FALSE;
|
|
break;
|
|
}
|
|
if (fHandled)
|
|
return (lResult);
|
|
else
|
|
return (DefWindowProc(hWnd, message, wParam, lParam));
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|
|
BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
|
|
HWND hDlg;
|
|
unsigned message;
|
|
WPARAM wParam;
|
|
LPARAM lParam;
|
|
{
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
return (TRUE);
|
|
|
|
case WM_COMMAND:
|
|
if (GET_WM_COMMAND_ID(wParam, lParam) == IDOK
|
|
|| GET_WM_COMMAND_ID(wParam, lParam) == IDCANCEL) {
|
|
EndDialog(hDlg, TRUE);
|
|
return (TRUE);
|
|
}
|
|
break;
|
|
}
|
|
return (FALSE);
|
|
}
|
|
/* -------------------------------------------------------------------------- */
|