There's one thing I notice, when I move the trackpoint a little bit the cursor is more likely to move than when I push it hard, then it works fine. Maybe there is a way to also consider the sensitivity, that when pushing the trackpoint only a bit that it still stays in place


#include <windows.h>
#include <chrono>  // For timing
#include <cmath>   // For std::abs
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

std::string trackpointDevicePath;
std::string hDevice;
bool isPreConfigured = false;
const int TOLERANCE = 5;
const int REST_TOLERANCE = 10;

const int SCROLL_SENSITIVITY = 120;
const double INTENSITY_FACTOR = 0.15;

std::chrono::steady_clock::time_point lastScrollTime;
const std::chrono::milliseconds SCROLL_INTERVAL(50);

int currentXPressure = 0;
int currentYPressure = 0;
HWND targetScrollWindow = NULL;

POINT lockedCursorPos;
bool cursorLocked = false;

std::string hexify(HANDLE h) {
    std::stringstream ss;
    ss << std::hex << h;
    return ss.str();
}

void ScrollWindow(HWND targetWindow, int xAmount, int yAmount) {
    if (!targetWindow) return;

    POINT clientPos = lockedCursorPos; // Use the locked cursor position
    ScreenToClient(targetWindow, &clientPos);

    if (yAmount != 0) {
        SendMessage(targetWindow, WM_MOUSEWHEEL,
            MAKEWPARAM(0, -yAmount),
            MAKELPARAM(clientPos.x, clientPos.y));
    }

    if (xAmount != 0) {
        SendMessage(targetWindow, WM_MOUSEHWHEEL,
            MAKEWPARAM(0, xAmount),
            MAKELPARAM(clientPos.x, clientPos.y));
    }
}

VOID CALLBACK ScrollTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
    int xScroll = static_cast<int>(currentXPressure * INTENSITY_FACTOR * SCROLL_SENSITIVITY);
    int yScroll = static_cast<int>(currentYPressure * INTENSITY_FACTOR * SCROLL_SENSITIVITY);

    if (targetScrollWindow && (xScroll != 0 || yScroll != 0)) {
        // Re-validate the window under the locked cursor position
        HWND currentWindow = WindowFromPoint(lockedCursorPos);
        if (currentWindow != targetScrollWindow) {
            targetScrollWindow = currentWindow;
        }

        // Maintain the cursor position
        if (cursorLocked) {
            SetCursorPos(lockedCursorPos.x, lockedCursorPos.y);
        }

        ScrollWindow(targetScrollWindow, xScroll, yScroll);
    }
}

void ProcessTrackPointMovement(RAWINPUT* raw) {
    int xMovement = raw->data.mouse.lLastX;
    int yMovement = raw->data.mouse.lLastY;

    if (std::abs(xMovement) < TOLERANCE) xMovement = 0;
    if (std::abs(yMovement) < TOLERANCE) yMovement = 0;

    currentXPressure = xMovement;
    currentYPressure = yMovement;

    bool atRest = (std::abs(xMovement) < REST_TOLERANCE) &&
        (std::abs(yMovement) < REST_TOLERANCE);

    if (atRest) {
        currentXPressure = 0;
        currentYPressure = 0;
        cursorLocked = false;
        targetScrollWindow = NULL;
    }
    else {
        if (!targetScrollWindow) {
            // Capture the cursor position only when we start scrolling
            GetCursorPos(&lockedCursorPos);
            targetScrollWindow = WindowFromPoint(lockedCursorPos);
            cursorLocked = true;
        }

        // Always restore cursor to the locked position during movement
        if (cursorLocked) {
            SetCursorPos(lockedCursorPos.x, lockedCursorPos.y);
        }
    }
}

void setMovement(std::string devicePath, RAWINPUT* raw) {
    if (trackpointDevicePath == devicePath && hDevice == hexify(raw->header.hDevice)) {
        // Block normal cursor movement processing if we have already locked the cursor
        if (cursorLocked) {
            // Force cursor back to locked position
            SetCursorPos(lockedCursorPos.x, lockedCursorPos.y);
        }

        ProcessTrackPointMovement(raw);
    }
}

bool RegisterTrackPoint(HWND hwnd) {
    RAWINPUTDEVICE rid[1];

    rid[0].usUsagePage = 0x01;
    rid[0].usUsage = 0x02;
    rid[0].dwFlags = RIDEV_INPUTSINK | RIDEV_NOLEGACY;
    rid[0].hwndTarget = hwnd;

    if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) {
        std::cerr << "Failed to register raw input device." << std::endl;
        return false;
    }
    return true;
}

void IdentifyTrackPoint() {
    UINT numDevices;
    GetRawInputDeviceList(nullptr, &numDevices, sizeof(RAWINPUTDEVICELIST));
    std::vector<RAWINPUTDEVICELIST> devices(numDevices);
    GetRawInputDeviceList(devices.data(), &numDevices, sizeof(RAWINPUTDEVICELIST));

    std::cout << "Listing all input devices:" << std::endl;

    std::string tempPath = "0";

    for (UINT i = 0; i < numDevices; ++i) {
        char devicePath[256];
        UINT pathSize = sizeof(devicePath);
        GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, devicePath, &pathSize);

        std::string deviceType;
        switch (devices[i].dwType) {
        case RIM_TYPEMOUSE:    deviceType = "Mouse";     break;
        case RIM_TYPEKEYBOARD: deviceType = "Keyboard";  break;
        case RIM_TYPEHID:      deviceType = "HID";       break;
        default:               deviceType = "Unknown";   break;
        }

        if (!isPreConfigured) {
            std::cout << "Device " << i + 1 << ": " << devicePath
                << " (" << deviceType << ")"
                << " [" << devices[i].hDevice << "]" << std::endl;
        }
        if (isPreConfigured &&
            trackpointDevicePath == devicePath &&
            hDevice == hexify(devices[i].hDevice)) {
            tempPath = devicePath;
            std::cout << "TrackPoint Device Found" << std::endl;
            break;
        }
    }

    if (!isPreConfigured) {
        std::cout << "\nMove Trackpoint\n" << std::endl;
    }

    if (isPreConfigured && tempPath == "0") {
        std::cout << "TrackPoint Device Not Found\nDelete config.ini and try again"
            << std::endl;
    }
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    if (uMsg == WM_INPUT) {
        UINT dwSize = 0;
        GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
        LPBYTE lpb = new BYTE[dwSize];
        if (!lpb) return 0;

        if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
            lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
            std::cerr << "GetRawInputData does not return correct size!" << std::endl;
        }

        RAWINPUT* raw = (RAWINPUT*)lpb;
        if (raw->header.dwType == RIM_TYPEMOUSE) {
            char devicePath[256];
            UINT pathSize = sizeof(devicePath);
            GetRawInputDeviceInfoA(raw->header.hDevice, RIDI_DEVICENAME,
                devicePath, &pathSize);

            if (!isPreConfigured) {
                std::cout << "Details of Input: " << std::endl;
                std::cout << "Device Path : " << devicePath << std::endl;
                std::cout << "hDevice     : " << raw->header.hDevice << std::endl;

                std::ofstream settingsFile("config.ini");
                settingsFile << "devicePath=" << devicePath << std::endl;
                settingsFile << "hDevice=" << raw->header.hDevice << std::endl;
                settingsFile.close();

                trackpointDevicePath = devicePath;
                hDevice = hexify(raw->header.hDevice);

                isPreConfigured = true;
                std::cout << "Config is saved." << std::endl;
                delete[] lpb;
                return 0;
            }
            else {
                setMovement(devicePath, raw);
            }
        }
        delete[] lpb;
        return 0;
    }
    else if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || uMsg == WM_MBUTTONDOWN) {
        targetScrollWindow = NULL;
        cursorLocked = false;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int main() {
    std::cout << "*** Thinkpad Trackpoint Scroll Utility ***" << std::endl;
    std::cout << "Loading config.ini" << std::endl;

    std::ifstream settingsFile("config.ini");
    if (settingsFile.is_open()) {
        std::string line;
        while (std::getline(settingsFile, line)) {
            if (line.rfind("devicePath=", 0) == 0)
                trackpointDevicePath = line.substr(11);
            else if (line.rfind("hDevice=", 0) == 0)
                hDevice = line.substr(8);

            if (!trackpointDevicePath.empty() && !hDevice.empty()) {
                isPreConfigured = true;
                break;
            }
        }
        settingsFile.close();
    }
    else {
        std::cout << "Config not found. Creating config.ini" << std::endl;
        std::cout << "Press Enter to configure TrackPoint, then move ONLY the TrackPoint."
            << std::endl;
        std::cin.get();
    }

    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = "TrackPointScrollClass";
    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0, wc.lpszClassName, "TrackPoint Scroll Utility",
        0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, wc.hInstance, NULL);

    if (!hwnd) {
        std::cerr << "Failed to create window." << std::endl;
        return 1;
    }

    IdentifyTrackPoint();
    if (!RegisterTrackPoint(hwnd)) {
        std::cerr << "Failed to register TrackPoint." << std::endl;
        return 1;
    }

    SetTimer(hwnd, 1, SCROLL_INTERVAL.count(), ScrollTimerProc);

    std::cout << "TrackPoint Scroll Utility is running" << std::endl;
    std::cout << "Position your cursor over a window and hold the TrackPoint to scroll:\n"
        << "  • Up/Down   → Vertical scroll\n"
        << "  • Left/Right→ Horizontal scroll\n"
        << "  • Push harder for faster scrolling\n"
        << "  • Click any button to reset the scroll target\n"
        << std::endl;

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    KillTimer(hwnd, 1);
    return 0;
}