Skip to content

Instantly share code, notes, and snippets.

@arpruss
Created February 17, 2024 04:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arpruss/026e9c06562adaeb094f07960dbff8e4 to your computer and use it in GitHub Desktop.
Save arpruss/026e9c06562adaeb094f07960dbff8e4 to your computer and use it in GitHub Desktop.
//x86_64-w64-mingw32-gcc -o disable-two-finger-click -mwindows -O99 disable-two-finger-click.cpp
#include <thread>
#include <Windows.h>
#include <stdio.h>
#include <time.h>
HHOOK miHook;
const bool ALLOW_DOUBLE_TAP_RIGHT_CLICK = true;
const unsigned TWOFINGER_DETECT_DELAY_CLOCKS = CLOCKS_PER_SEC * 50 / 1000;
const unsigned CLICK_DETECT_DELAY_CLOCKS = CLOCKS_PER_SEC * 50 / 1000;
const unsigned OUT_BUFFER_SIZE = 2048;
const unsigned MAX_HID = 512;
unsigned num_fingers = 0;
clock_t last_click = 0;
clock_t last_two_finger_time = 0;
unsigned char outBuffer[OUT_BUFFER_SIZE];
unsigned outBufferHead;
unsigned outBufferTail;
HANDLE queueReady;
bool running = true;
int popBuffer() {
if (outBufferHead == outBufferTail)
return -1;
unsigned char c = outBuffer[outBufferHead];
outBufferHead = (outBufferHead+1) % OUT_BUFFER_SIZE;
return c;
}
int pushBuffer(unsigned char c) {
unsigned newTail = (outBufferTail+1) % OUT_BUFFER_SIZE;
if (newTail == outBufferHead)
return -1;
outBuffer[outBufferTail] = c;
outBufferTail = newTail;
return 0;
}
DWORD WINAPI handleQueue(void* arg) {
INPUT ip;
ip.type = INPUT_MOUSE;
ip.mi.dx = 0;
ip.mi.dy = 0;
ip.mi.mouseData = 0;
ip.mi.time = 0;
while(running) {
WaitForSingleObject(queueReady, INFINITE);
int c;
while (running && (c = popBuffer()) >= 0) {
if (c)
ip.mi.dwFlags = WM_RBUTTONDOWN;
else
ip.mi.dwFlags = WM_RBUTTONUP;
SendInput(1,&ip,sizeof(INPUT));
}
}
ExitThread(0);
return 0;
}
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
static bool remapped_down = false;
if(nCode == HC_ACTION) {
if(wParam == WM_RBUTTONDOWN) {
clock_t t = clock();
if ( (num_fingers > 1 && t-last_two_finger_time<TWOFINGER_DETECT_DELAY_CLOCKS) &&
(ALLOW_DOUBLE_TAP_RIGHT_CLICK || t-last_click<CLICK_DETECT_DELAY_CLOCKS) ) {
pushBuffer(1);
SetEvent(queueReady);
remapped_down = true;
return 1;
}
}
else if (wParam == WM_RBUTTONUP && remapped_down) {
pushBuffer(0);
SetEvent(queueReady);
remapped_down = false;
return 1;
}
}
return CallNextHookEx(miHook, nCode, wParam, lParam); // Important! Otherwise other mouse hooks may misbehave
}
// https://gist.github.com/luluco250/ac79d72a734295f167851ffdb36d77ee
LRESULT CALLBACK EventHandler(
HWND hwnd,
unsigned event,
WPARAM wparam,
LPARAM lparam
) {
static RAWINPUT buffer[sizeof(RAWINPUT)+MAX_HID];
switch (event) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_INPUT: {
unsigned size = sizeof(buffer);
int res = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buffer, &size, sizeof(RAWINPUTHEADER));
if (res < 0 || size == 0) {
return 0;
}
if (buffer->header.dwType == RIM_TYPEHID && buffer->data.hid.dwSizeHid > 28) {
BYTE* data = buffer->data.hid.bRawData;
num_fingers = data[28];
clock_t t = clock();
if (num_fingers>=2)
last_two_finger_time = t;
if(buffer->data.hid.dwSizeHid > 29 && data[29])
last_click = t;
}
} return 0;
}
return DefWindowProc(hwnd, event, wparam, lparam);
}
//main()
int WINAPI WinMain(HINSTANCE instance, HINSTANCE hPrevInstance,
PSTR lpCmdLine, int nCmdShow)
{
const char* class_name = "fix-touchpad-right-click-class";
//HINSTANCE instance = GetModuleHandle(0);
WNDCLASS window_class = {};
window_class.lpfnWndProc = EventHandler;
window_class.hInstance = instance;
window_class.lpszClassName = class_name;
if (!RegisterClass(&window_class))
return -1;
HWND window = CreateWindow(class_name, "fix-touchpad-right-click", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0);
if (window == nullptr)
return -1;
RAWINPUTDEVICE rid[1];
rid[0].usUsagePage = 0x0D;
rid[0].usUsage = 0x05;
rid[0].dwFlags = RIDEV_INPUTSINK;
rid[0].hwndTarget = window;
RegisterRawInputDevices(rid, 1, sizeof(rid[0]));
miHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)(&LowLevelMouseProc), 0, 0);
queueReady = CreateEvent(NULL, FALSE, FALSE, (LPTSTR)("queueReady"));
HANDLE queueThread = CreateThread(NULL, 0, handleQueue, NULL, 0, NULL);
MSG message;
while(GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
running = 0;
SetEvent(queueReady);
UnhookWindowsHookEx(miHook);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment