diff --git a/driver-win32/.gitignore b/driver-win32/.gitignore new file mode 100644 index 0000000..3562c17 --- /dev/null +++ b/driver-win32/.gitignore @@ -0,0 +1,11 @@ +/Debug/ +/Release/ +/ipch/ +/driver-win32.aps +/driver-win32.sdf +/driver-win32.suo +/*.vcxproj.user +/driver-win32.sln +Browse.VC.* +*.ipch +*.suo diff --git a/driver-win32/driver-win32.cpp b/driver-win32/driver-win32.cpp new file mode 100644 index 0000000..8ec10d2 --- /dev/null +++ b/driver-win32/driver-win32.cpp @@ -0,0 +1,334 @@ +#include "stdafx.h" +#include "driver-win32.h" +#include + +#pragma comment(lib, "Ws2_32.lib") + +#define MAX_LOADSTRING 100 +//#define SIMULATE_HOVER_TROUGH_LIGHT_TOUCH 1 + +// Global Variables: +HINSTANCE hInst; // current instance +TCHAR szTitle[MAX_LOADSTRING]; // The title bar text +TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +int RunDriver(); + +int APIENTRY _tWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // TODO: Place code here. + MSG msg; + HACCEL hAccelTable; + + // Initialize global strings + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(hInstance, IDC_DRIVERWIN32, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DRIVERWIN32)); + + // + PostMessage(NULL, WM_USER+1,0,0); + + // Main message loop: + while (GetMessage(&msg, NULL, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int) msg.wParam; +} + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DRIVERWIN32)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_DRIVERWIN32); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassEx(&wcex); +} + +#define WM_USER_SHELLICON (WM_USER+1) + +NOTIFYICONDATA nidApp; + +void CreateTrayIcon(HWND hWnd) +{ + nidApp.cbSize = sizeof(NOTIFYICONDATA); + nidApp.hWnd = (HWND) hWnd; + nidApp.uID = IDI_DRIVERWIN32; + nidApp.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nidApp.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DRIVERWIN32) ); + nidApp.uCallbackMessage = WM_USER_SHELLICON; + _tcscpy( nidApp.szTip, _T("GfxTablet windows driver") ); + + Shell_NotifyIcon(NIM_ADD, &nidApp); +} + +void RemoveTrayIcon(HWND hWnd) +{ + Shell_NotifyIcon(NIM_DELETE, &nidApp); +} + +DWORD dwDriverThreadId = 0; +DWORD WINAPI DriverThreadStart(LPVOID lpThreadParameter) +{ + return RunDriver(); +} + +HWND hWnd = NULL; + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); + + if (!hWnd) + { + return FALSE; + } + + CreateTrayIcon(hWnd); + + CreateThread(NULL, 500000, DriverThreadStart, NULL, 0, &dwDriverThreadId); + + return TRUE; +} + +void ShowTrayIconContextMenu(HWND hWnd) +{ + POINT lpClickPoint; + UINT uFlag = MF_BYPOSITION|MF_STRING; + GetCursorPos(&lpClickPoint); + HMENU hPopMenu = CreatePopupMenu(); + InsertMenu(hPopMenu,0xFFFFFFFF,MF_BYPOSITION|MF_STRING,IDM_EXIT,_T("Exit")); + TrackPopupMenu(hPopMenu,TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_BOTTOMALIGN, + lpClickPoint.x, lpClickPoint.y,0,hWnd,NULL); +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + + switch (message) + { + case WM_USER_SHELLICON: + switch(LOWORD(lParam)) + { + case WM_RBUTTONDOWN: + ShowTrayIconContextMenu(hWnd); + } + break; + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDM_EXIT: + DestroyWindow(hWnd); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + break; + case WM_DESTROY: + RemoveTrayIcon(hWnd); + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +int udp_socket; + +void die(TCHAR* fmt, ...) +{ + va_list args; + va_start(args,fmt); + TCHAR msgbuf[4096]; + _vsntprintf_s(msgbuf, _countof(msgbuf), fmt, args); + MessageBox(NULL, msgbuf, _T("GfxTablet Win32 driver error"), MB_ICONERROR|MB_OK); + + RemoveTrayIcon(hWnd); + ExitProcess(EXIT_FAILURE); +} + +int prepare_socket() +{ + int s; + struct sockaddr_in addr; + + WSADATA wsadata; + WSAStartup(WINSOCK_VERSION, &wsadata); + + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + die(_T("error: prepare_socket()")); + + ZeroMemory(&addr, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_port = htons(GFXTABLET_PORT); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) + { + DWORD dwErr = WSAGetLastError(); + if (dwErr == WSAEADDRINUSE) + die(_T("error: prepare_socket(). Port already in use") ); + else + die(_T("error: prepare_socket(). Error = 0x%08x"), dwErr ); + } + + return s; +} + +void quit(int signal) +{ + closesocket(udp_socket); +} + +int RunDriver(void) +{ + int device; + struct event_packet ev_pkt; + + udp_socket = prepare_socket(); + + printf("GfxTablet driver (protocol version %u) is ready and listening on 0.0.0.0:%u (UDP)\n" + "Hint: Make sure that this port is not blocked by your firewall.\n", PROTOCOL_VERSION, GFXTABLET_PORT); + + signal(SIGINT, quit); + signal(SIGTERM, quit); + + BOOL isDown = false; + + while (recv(udp_socket, (char*)&ev_pkt, sizeof(ev_pkt), 0) >= 9) { // every packet has at least 9 bytes + printf("."); fflush(0); + + if (memcmp(ev_pkt.signature, "GfxTablet", 9) != 0) { + fprintf(stderr, "\nGot unknown packet on port %i, ignoring\n", GFXTABLET_PORT); + continue; + } + ev_pkt.version = ntohs(ev_pkt.version); + if (ev_pkt.version != PROTOCOL_VERSION) { + fprintf(stderr, "\nGfxTablet app speaks protocol version %i but driver speaks version %i, please update\n", + ev_pkt.version, PROTOCOL_VERSION); + break; + } + + ev_pkt.x = ntohs(ev_pkt.x); + ev_pkt.y = ntohs(ev_pkt.y); + ev_pkt.pressure = ntohs(ev_pkt.pressure); + printf("x: %hi, y: %hi, pressure: %hi\n", ev_pkt.x, ev_pkt.y, ev_pkt.pressure); + + //windows uses 0-65535, while GfxTablet uses 0-32767 + int absoluteX = ((int)ev_pkt.x)*2; + int absoluteY = ((int)ev_pkt.y)*2; + + INPUT inputs[1]; + ZeroMemory(&inputs[0],sizeof(inputs)); + inputs[0].type = INPUT_MOUSE; + inputs[0].mi.dx = absoluteX; + inputs[0].mi.dy = absoluteY; + inputs[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE; + + //no support for pressure on windows :-(( + //This is only possible with an HID miniport driver. + //GIMP uses DirectInput - probably the HID miniport driver would be visible trough HID + //If not a DirectInput driver would be necessary too + //send_event(device, EV_ABS, ABS_PRESSURE, ev_pkt.pressure); + +#if SIMULATE_HOVER_TROUGH_LIGHT_TOUCH + //hack: ignores EVENT_TYPE_BUTTON (android touch events), but generates them + //when pressure is over a specific limit, so it is possible to move the finger + //around the tablet without drawing + if(ev_pkt.type==EVENT_TYPE_BUTTON && ev_pkt.down) + { + ev_pkt.type=EVENT_TYPE_MOTION; + } + else if(ev_pkt.type==EVENT_TYPE_MOTION && ev_pkt.pressure>18000) + { + if (!isDown) + ev_pkt.type=EVENT_TYPE_BUTTON; + } +#endif + + switch (ev_pkt.type) { + case EVENT_TYPE_MOTION: + inputs[0].mi.dwFlags |= MOUSEEVENTF_MOVE; + SendInput(1, &inputs[0], sizeof(inputs[0])); + break; + case EVENT_TYPE_BUTTON: + if (ev_pkt.button == 0) + { + inputs[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_MOVE; + SendInput(1, &inputs[0], sizeof(inputs[0])); + } + + if (ev_pkt.down) + { + inputs[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTDOWN; + isDown = true; + } + else + { + inputs[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE|MOUSEEVENTF_LEFTUP; + isDown = false; + } + SendInput(1, &inputs[0], sizeof(inputs[0])); + break; + + } + } + closesocket(udp_socket); + + + printf("GfxTablet driver shut down gracefully\n"); + return 0; +} diff --git a/driver-win32/driver-win32.h b/driver-win32/driver-win32.h new file mode 100644 index 0000000..d00d47e --- /dev/null +++ b/driver-win32/driver-win32.h @@ -0,0 +1,3 @@ +#pragma once + +#include "resource.h" diff --git a/driver-win32/driver-win32.ico b/driver-win32/driver-win32.ico new file mode 100644 index 0000000..4262841 Binary files /dev/null and b/driver-win32/driver-win32.ico differ diff --git a/driver-win32/driver-win32.rc b/driver-win32/driver-win32.rc new file mode 100644 index 0000000..659ba89 Binary files /dev/null and b/driver-win32/driver-win32.rc differ diff --git a/driver-win32/driver-win32.vcxproj b/driver-win32/driver-win32.vcxproj new file mode 100644 index 0000000..7e081fe --- /dev/null +++ b/driver-win32/driver-win32.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {F1323D24-2E93-466B-83D9-01042B36DE6A} + Win32Proj + driverwin32 + 10.0.17763.0 + + + + Application + true + Unicode + v141 + + + Application + false + true + Unicode + v141 + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + \ No newline at end of file diff --git a/driver-win32/driver-win32.vcxproj.filters b/driver-win32/driver-win32.vcxproj.filters new file mode 100644 index 0000000..5b2c262 --- /dev/null +++ b/driver-win32/driver-win32.vcxproj.filters @@ -0,0 +1,52 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Resource Files + + + Resource Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/driver-win32/resource.h b/driver-win32/resource.h new file mode 100644 index 0000000..57895e4 Binary files /dev/null and b/driver-win32/resource.h differ diff --git a/driver-win32/small.ico b/driver-win32/small.ico new file mode 100644 index 0000000..d551aa3 Binary files /dev/null and b/driver-win32/small.ico differ diff --git a/driver-win32/stdafx.cpp b/driver-win32/stdafx.cpp new file mode 100644 index 0000000..bac7dd8 --- /dev/null +++ b/driver-win32/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// driver-win32.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/driver-win32/stdafx.h b/driver-win32/stdafx.h new file mode 100644 index 0000000..315205c --- /dev/null +++ b/driver-win32/stdafx.h @@ -0,0 +1,35 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include + +// C RunTime Header Files +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; +typedef char int8_t; + +#include "../driver-uinput/protocol.h" diff --git a/driver-win32/targetver.h b/driver-win32/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/driver-win32/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include