From f60e4881886ad022b502f03b64164964d42e8e58 Mon Sep 17 00:00:00 2001 From: Jeardey Date: Mon, 22 Jun 2026 23:36:19 +0200 Subject: [PATCH 1/8] build(ci): replace genie with CMake and automate releases --- .github/workflows/build.yml | 38 +++++++++++++++++++++++ CMakeLists.txt | 32 ++++++++++++++++++++ genie.lua | 60 ------------------------------------- sample.bat | 1 - vs2022.bat | 1 - 5 files changed, 70 insertions(+), 62 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 CMakeLists.txt delete mode 100644 genie.lua delete mode 100644 sample.bat delete mode 100644 vs2022.bat diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7d7beca --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,38 @@ +name: Build and Release TouchJoy + +on: + push: + branches: [ "master", "main" ] + pull_request: + branches: [ "master", "main" ] + workflow_dispatch: # Lets you click a button to run it manually + +jobs: + build-windows: + runs-on: windows-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Configure CMake + # This generates the Visual Studio build files automatically + run: cmake -B build -S . + + - name: Compile Project + # This actually compiles the .exe files in Release mode + run: cmake --build build --config Release + + - name: Package Artifacts + # Move the compiled .exe and the data folder into one clean folder + shell: bash + run: | + mkdir -p TouchJoy-Release + cp build/Release/TouchJoy.exe TouchJoy-Release/ + cp -r data TouchJoy-Release/data + + - name: Upload Downloadable Artifact + uses: actions/upload-artifact@v4 + with: + name: TouchJoy-Windows + path: TouchJoy-Release/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..62f7a15 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.15) +project(TouchJoy C) + +# Force standard C +set(CMAKE_C_STANDARD 99) + +# Global Defines (Copied from the old genie.lua) +add_compile_definitions( + _CRT_SECURE_NO_WARNINGS + GB_INI_MAX_SECTION_LENGTH=16 + GB_INI_MAX_NAME_LENGTH=16 +) + +# 1. Main Windowed Application (WIN32 hides the console window) +add_executable(TouchJoy WIN32 + src/gamepad.c + src/gamepad_window.c + src/libs.c + src/main.c + src/utils.c +) + +# 2. Test Console Application +add_executable(TouchJoyTest + src/gamepad.c + src/gamepad_window.c + src/libs.c + src/main.c + src/utils.c +) +# Add the _TEST flag only to the test build +target_compile_definitions(TouchJoyTest PRIVATE _TEST) \ No newline at end of file diff --git a/genie.lua b/genie.lua deleted file mode 100644 index 3bff772..0000000 --- a/genie.lua +++ /dev/null @@ -1,60 +0,0 @@ -solution "touch-joy" - location(_ACTION) - configurations {"Develop"} - platforms {"x64"} - targetdir "bin" - debugdir "data" - - project "touch-joy" - kind "WindowedApp" - language "C" - - defines { - "_CRT_SECURE_NO_WARNINGS", - "GB_INI_MAX_SECTION_LENGTH=16", - "GB_INI_MAX_NAME_LENGTH=16" - } - - files { - "src/*.h", - "src/*.c" - } - - flags { - "ExtraWarnings", - "FatalWarnings", - "OptimizeSize", - "StaticRuntime", - "Symbols", - "WinMain", - "NoEditAndContinue", - "NoNativeWChar", - "NoExceptions", - "NoFramePointer" - } - - project "test" - kind "ConsoleApp" - language "C" - - defines { - "_CRT_SECURE_NO_WARNINGS", - "_TEST", - "GB_INI_MAX_SECTION_LENGTH=16", - "GB_INI_MAX_NAME_LENGTH=16" - } - - files { - "src/*.h", - "src/*.c" - } - - flags { - "FatalWarnings", - "OptimizeSize", - "StaticRuntime", - "Symbols", - "NoEditAndContinue", - "NoNativeWChar", - "NoExceptions" - } diff --git a/sample.bat b/sample.bat deleted file mode 100644 index f2800c8..0000000 --- a/sample.bat +++ /dev/null @@ -1 +0,0 @@ -bin\touch-joy.exe data\sample.ini diff --git a/vs2022.bat b/vs2022.bat deleted file mode 100644 index 73852fb..0000000 --- a/vs2022.bat +++ /dev/null @@ -1 +0,0 @@ -genie vs2022 \ No newline at end of file From 593b215bbbaa487446b82dd572718a4863142857 Mon Sep 17 00:00:00 2001 From: Jeardey Date: Mon, 22 Jun 2026 23:40:04 +0200 Subject: [PATCH 2/8] fix(ci): add config.ini to the workflow --- .github/workflows/build.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d7beca..d947c87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,12 +24,15 @@ jobs: run: cmake --build build --config Release - name: Package Artifacts - # Move the compiled .exe and the data folder into one clean folder shell: bash run: | mkdir -p TouchJoy-Release + # Copy the executable cp build/Release/TouchJoy.exe TouchJoy-Release/ + # Copy the data folder (contains images for the buttons) cp -r data TouchJoy-Release/data + # Create a default config.ini so double-clicking the exe works! + cp data/sample.ini TouchJoy-Release/config.ini - name: Upload Downloadable Artifact uses: actions/upload-artifact@v4 From ace910c6037295b463bb0c659f27b8a3d5964ff7 Mon Sep 17 00:00:00 2001 From: Jeardey Date: Mon, 22 Jun 2026 23:41:06 +0200 Subject: [PATCH 3/8] im stupid :) --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d947c87..f9d09ae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,11 +27,8 @@ jobs: shell: bash run: | mkdir -p TouchJoy-Release - # Copy the executable cp build/Release/TouchJoy.exe TouchJoy-Release/ - # Copy the data folder (contains images for the buttons) cp -r data TouchJoy-Release/data - # Create a default config.ini so double-clicking the exe works! cp data/sample.ini TouchJoy-Release/config.ini - name: Upload Downloadable Artifact From 9e5195e95a6797741aa92391c15eee6aa7575ca8 Mon Sep 17 00:00:00 2001 From: Jeardey Date: Mon, 22 Jun 2026 23:43:36 +0200 Subject: [PATCH 4/8] fix(ci): flatten release artifact to fix image loading paths --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f9d09ae..9436682 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,8 +28,8 @@ jobs: run: | mkdir -p TouchJoy-Release cp build/Release/TouchJoy.exe TouchJoy-Release/ - cp -r data TouchJoy-Release/data - cp data/sample.ini TouchJoy-Release/config.ini + cp data/* TouchJoy-Release/ + mv TouchJoy-Release/sample.ini TouchJoy-Release/config.ini - name: Upload Downloadable Artifact uses: actions/upload-artifact@v4 From b570c95df8d70649cb7d27ec69465a2f15f6133f Mon Sep 17 00:00:00 2001 From: Jeardey Date: Mon, 22 Jun 2026 23:51:35 +0200 Subject: [PATCH 5/8] feat(emulation): implement native Xbox 360 controller emulation via ViGEmBus --- .github/workflows/build.yml | 4 +-- CMakeLists.txt | 21 ++++++++---- src/gamepad_window.c | 65 +++++++++++++++---------------------- src/main.c | 32 ++++++++++++++++++ 4 files changed, 74 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9436682..90bc9a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ on: branches: [ "master", "main" ] pull_request: branches: [ "master", "main" ] - workflow_dispatch: # Lets you click a button to run it manually + workflow_dispatch: jobs: build-windows: @@ -16,11 +16,9 @@ jobs: uses: actions/checkout@v4 - name: Configure CMake - # This generates the Visual Studio build files automatically run: cmake -B build -S . - name: Compile Project - # This actually compiles the .exe files in Release mode run: cmake --build build --config Release - name: Package Artifacts diff --git a/CMakeLists.txt b/CMakeLists.txt index 62f7a15..d7b2f50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,24 @@ cmake_minimum_required(VERSION 3.15) -project(TouchJoy C) +project(TouchJoy C CXX) -# Force standard C set(CMAKE_C_STANDARD 99) -# Global Defines (Copied from the old genie.lua) +# Fetch ViGEmClient automatically for Xbox 360 controller emulation +include(FetchContent) +FetchContent_Declare( + ViGEmClient + GIT_REPOSITORY https://github.com/nefarius/ViGEmClient.git + GIT_TAG master +) +FetchContent_MakeAvailable(ViGEmClient) + add_compile_definitions( _CRT_SECURE_NO_WARNINGS GB_INI_MAX_SECTION_LENGTH=16 GB_INI_MAX_NAME_LENGTH=16 ) -# 1. Main Windowed Application (WIN32 hides the console window) +# 1. Main Windowed Application add_executable(TouchJoy WIN32 src/gamepad.c src/gamepad_window.c @@ -19,6 +26,8 @@ add_executable(TouchJoy WIN32 src/main.c src/utils.c ) +# Link ViGEm and Windows APIs +target_link_libraries(TouchJoy PRIVATE ViGEmClient setupapi.lib winmm.lib) # 2. Test Console Application add_executable(TouchJoyTest @@ -28,5 +37,5 @@ add_executable(TouchJoyTest src/main.c src/utils.c ) -# Add the _TEST flag only to the test build -target_compile_definitions(TouchJoyTest PRIVATE _TEST) \ No newline at end of file +target_compile_definitions(TouchJoyTest PRIVATE _TEST) +target_link_libraries(TouchJoyTest PRIVATE ViGEmClient setupapi.lib winmm.lib) \ No newline at end of file diff --git a/src/gamepad_window.c b/src/gamepad_window.c index 7a38bd9..b89ad13 100644 --- a/src/gamepad_window.c +++ b/src/gamepad_window.c @@ -5,12 +5,19 @@ #include #include +#include + #include "utils.h" #define MOUSEEVENTF_FROMTOUCH 0xFF515700 #define BUTTON(HWND, VAR) \ Button* VAR = (Button*)GetWindowLongPtr(HWND, GWLP_USERDATA); +// EXTERN VIGEM STATE +extern PVIGEM_CLIENT g_client; +extern PVIGEM_TARGET g_pad; +extern XUSB_REPORT g_report; + typedef enum { TOUCH_DOWN, @@ -96,52 +103,32 @@ void HandleWheelButton(Button* button, bool down) SendInput(2, inputs, sizeof(INPUT)); } +// XBOX 360 CONTROLLER JOYSTICK UPDATE void HandleStickButton(Button* button, TouchEvent event, int touchX, int touchY) { - float joyX, joyY; + float joyX = 0.0f; + float joyY = 0.0f; - if (event == TOUCH_UP) + if (event != TOUCH_UP) { - // If the touch is released, the stick moves to its center position - joyX = 0.f; - joyY = 0.f; + // Calculate joystick position (-1.0 to 1.0) + joyX = (float)touchX / (float)button->width * 2.0f - 1.0f; + joyY = (float)touchY / (float)button->height * 2.0f - 1.0f; + + // Clamp values to a circle + if (joyX < -1.0f) joyX = -1.0f; + if (joyX > 1.0f) joyX = 1.0f; + if (joyY < -1.0f) joyY = -1.0f; + if (joyY > 1.0f) joyY = 1.0f; } - else - { - // In other cases, use the real touch position to calculate stick - // position - - joyX = (float)touchX / (float)button->width * 2.f - 1.f; - joyY = (float)touchY / (float)button->height * 2.f - 1.f; - } - - bool newStates[4]; - float threshold = button->extras.stick.threshold; - newStates[STICK_UP] = joyY < -threshold; - newStates[STICK_DOWN] = joyY > threshold; - newStates[STICK_LEFT] = joyX < -threshold; - newStates[STICK_RIGHT] = joyX > threshold; - INPUT inputs[4]; - int numInputs = 0; - - for (int i = 0; i < 4; ++i) - { - if (newStates[i] != button->extras.stick.states[i]) - { - INPUT* input = &inputs[numInputs++]; - input->type = INPUT_KEYBOARD; - input->ki.wVk = button->extras.stick.codes[i]; - input->ki.dwFlags = newStates[i] ? 0 : KEYEVENTF_KEYUP; - input->ki.wScan = 0; - input->ki.time = 0; - input->ki.dwExtraInfo = 0; - } - - button->extras.stick.states[i] = newStates[i]; - } + // Xbox 360 thumbsticks use SHORT values from -32768 to 32767. + // NOTE: Windows screen Y goes down, but Xbox thumbstick Y goes UP. We invert joyY. + g_report.sThumbLX = (SHORT)(joyX * 32767.0f); + g_report.sThumbLY = (SHORT)(joyY * -32767.0f); - SendInput(numInputs, inputs, sizeof(INPUT)); + // Send the updated state to the virtual controller + vigem_target_x360_update(g_client, g_pad, g_report); } void HandleUpDown(Button* button, bool down) diff --git a/src/main.c b/src/main.c index 9072e53..dbaffdf 100644 --- a/src/main.c +++ b/src/main.c @@ -6,12 +6,19 @@ #define VC_EXTRALEAN #include +#include + #include "gamepad.h" #include "gamepad_window.h" #include "utils.h" #define WM_CONFIGCHANGED (WM_USER + 1) +// Global Xbox Controller State +PVIGEM_CLIENT g_client; +PVIGEM_TARGET g_pad; +XUSB_REPORT g_report; + typedef struct { Gamepad gamepad; @@ -127,6 +134,24 @@ int CALLBACK WinMain( return -1; } + // --- VIGEM INITIALIZATION --- + g_client = vigem_alloc(); + if (g_client == NULL) { + MessageBox(NULL, "Not enough memory for ViGEmClient", "Error", MB_OK); + return -1; + } + + VIGEM_ERROR verr = vigem_connect(g_client); + if (!VIGEM_SUCCESS(verr)) { + MessageBox(NULL, "Could not connect to ViGEmBus. Is the driver installed?", "Error", MB_OK); + return -1; + } + + g_pad = vigem_target_x360_alloc(); + vigem_target_add(g_client, g_pad); + XUSB_REPORT_INIT(&g_report); + // ---------------------------- + // Display gamepad RegisterGamepadWindowClass(); InitializeGamepad(&state.gamepad); @@ -172,6 +197,13 @@ int CALLBACK WinMain( DeinitializeGamepad(&state.gamepad); FreeGamepad(&state.gamepad); + // --- VIGEM CLEANUP --- + vigem_target_remove(g_client, g_pad); + vigem_target_free(g_pad); + vigem_disconnect(g_client); + vigem_free(g_client); + // --------------------- + return (int)msg.wParam; } From 22ad77fe583b645787730f5c62c826db697d326f Mon Sep 17 00:00:00 2001 From: Jeardey Date: Mon, 22 Jun 2026 23:58:03 +0200 Subject: [PATCH 6/8] feat(ux): prompt user to download ViGEmBus driver if missing --- CMakeLists.txt | 6 +++--- src/main.c | 14 +++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7b2f50..8f248e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,8 +26,8 @@ add_executable(TouchJoy WIN32 src/main.c src/utils.c ) -# Link ViGEm and Windows APIs -target_link_libraries(TouchJoy PRIVATE ViGEmClient setupapi.lib winmm.lib) +# Link ViGEm, Windows APIs, and Shell32 (for the browser prompt) +target_link_libraries(TouchJoy PRIVATE ViGEmClient setupapi.lib winmm.lib shell32.lib) # 2. Test Console Application add_executable(TouchJoyTest @@ -38,4 +38,4 @@ add_executable(TouchJoyTest src/utils.c ) target_compile_definitions(TouchJoyTest PRIVATE _TEST) -target_link_libraries(TouchJoyTest PRIVATE ViGEmClient setupapi.lib winmm.lib) \ No newline at end of file +target_link_libraries(TouchJoyTest PRIVATE ViGEmClient setupapi.lib winmm.lib shell32.lib) \ No newline at end of file diff --git a/src/main.c b/src/main.c index dbaffdf..2f3b5af 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN #include +#include // Required for ShellExecute (opening the browser) #include @@ -143,7 +144,18 @@ int CALLBACK WinMain( VIGEM_ERROR verr = vigem_connect(g_client); if (!VIGEM_SUCCESS(verr)) { - MessageBox(NULL, "Could not connect to ViGEmBus. Is the driver installed?", "Error", MB_OK); + int response = MessageBox(NULL, + "Could not connect to ViGEmBus.\n\nThe virtual gamepad driver is not installed. Would you like to open your browser to download the official installer now?", + "ViGEmBus Driver Required", + MB_YESNO | MB_ICONWARNING | MB_TOPMOST); + + if (response == IDYES) { + // Opens the user's default browser to the official ViGEmBus download page + ShellExecute(NULL, "open", "https://github.com/nefarius/ViGEmBus/releases/latest", NULL, NULL, SW_SHOWNORMAL); + } + + // Clean up the memory we allocated before closing + vigem_free(g_client); return -1; } From 669b88a12c97ee9067d70884ff457b00296305c3 Mon Sep 17 00:00:00 2001 From: Jeardey Date: Tue, 23 Jun 2026 00:03:17 +0200 Subject: [PATCH 7/8] fix(emulation): map standard button names to Xbox controller inputs --- src/gamepad_window.c | 67 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/gamepad_window.c b/src/gamepad_window.c index b89ad13..a00fc37 100644 --- a/src/gamepad_window.c +++ b/src/gamepad_window.c @@ -4,6 +4,7 @@ #define VC_EXTRALEAN #include #include +#include // Required for _stricmp #include @@ -60,6 +61,54 @@ void HandleKeyButton(Button* button, bool down) SendInput(1, &input, sizeof(INPUT)); } +// --- NEW XBOX BUTTON MAPPING --- +USHORT GetXboxButtonMask(const char* name) +{ + if (_stricmp(name, "a") == 0) return XUSB_GAMEPAD_A; + if (_stricmp(name, "b") == 0) return XUSB_GAMEPAD_B; + if (_stricmp(name, "x") == 0) return XUSB_GAMEPAD_X; + if (_stricmp(name, "y") == 0) return XUSB_GAMEPAD_Y; + if (_stricmp(name, "lb") == 0 || _stricmp(name, "l") == 0) return XUSB_GAMEPAD_LEFT_SHOULDER; + if (_stricmp(name, "rb") == 0 || _stricmp(name, "r") == 0) return XUSB_GAMEPAD_RIGHT_SHOULDER; + if (_stricmp(name, "start") == 0) return XUSB_GAMEPAD_START; + if (_stricmp(name, "select") == 0 || _stricmp(name, "back") == 0) return XUSB_GAMEPAD_BACK; + if (_stricmp(name, "up") == 0) return XUSB_GAMEPAD_DPAD_UP; + if (_stricmp(name, "down") == 0) return XUSB_GAMEPAD_DPAD_DOWN; + if (_stricmp(name, "left") == 0) return XUSB_GAMEPAD_DPAD_LEFT; + if (_stricmp(name, "right") == 0) return XUSB_GAMEPAD_DPAD_RIGHT; + if (_stricmp(name, "ls") == 0 || _stricmp(name, "l3") == 0) return XUSB_GAMEPAD_LEFT_THUMB; + if (_stricmp(name, "rs") == 0 || _stricmp(name, "r3") == 0) return XUSB_GAMEPAD_RIGHT_THUMB; + return 0; +} + +void HandleXboxButton(Button* button, bool down) +{ + // Handle Analog Triggers (LT / RT) + if (_stricmp(button->name, "lt") == 0) { + g_report.bLeftTrigger = down ? 255 : 0; + } + else if (_stricmp(button->name, "rt") == 0) { + g_report.bRightTrigger = down ? 255 : 0; + } + else { + // Handle Digital Buttons + USHORT mask = GetXboxButtonMask(button->name); + if (mask != 0) { + if (down) g_report.wButtons |= mask; + else g_report.wButtons &= ~mask; + } + else { + // Fallback: If it's not a standard Xbox name, send a normal keyboard key + HandleKeyButton(button, down); + return; + } + } + + // Push the updated state to the virtual controller + vigem_target_x360_update(g_client, g_pad, g_report); +} +// ------------------------------- + void HandleQuitButton(Button* button, bool down) { UNUSED(button); @@ -71,17 +120,11 @@ void HandleWheelButton(Button* button, bool down) { if (!down) { return; } - // Scrolling is a bit tricky. - // First we need to move the mouse to the target area. - // Then we simulate a scroll event. INPUT inputs[2]; - // Move the mouse to a point slightly above and to the left of the button's - // top left corner inputs[0].type = INPUT_MOUSE; int x = GetButtonX(button) - 5; int y = GetButtonY(button) - 5; - // Windows uses a weird coordinate system for mouse: [0, 65535] int absX = (int)((float)x / (float)GetSystemMetrics(SM_CXSCREEN) * 65535.f); int absY = (int)((float)y / (float)GetSystemMetrics(SM_CYSCREEN) * 65535.f); inputs[0].mi.dx = absX; @@ -91,7 +134,6 @@ void HandleWheelButton(Button* button, bool down) inputs[0].mi.time = 0; inputs[0].mi.dwExtraInfo = 0; - // Scroll inputs[1].type = INPUT_MOUSE; inputs[1].mi.dx = 0; inputs[1].mi.dy = 0; @@ -103,7 +145,6 @@ void HandleWheelButton(Button* button, bool down) SendInput(2, inputs, sizeof(INPUT)); } -// XBOX 360 CONTROLLER JOYSTICK UPDATE void HandleStickButton(Button* button, TouchEvent event, int touchX, int touchY) { float joyX = 0.0f; @@ -111,23 +152,18 @@ void HandleStickButton(Button* button, TouchEvent event, int touchX, int touchY) if (event != TOUCH_UP) { - // Calculate joystick position (-1.0 to 1.0) joyX = (float)touchX / (float)button->width * 2.0f - 1.0f; joyY = (float)touchY / (float)button->height * 2.0f - 1.0f; - // Clamp values to a circle if (joyX < -1.0f) joyX = -1.0f; if (joyX > 1.0f) joyX = 1.0f; if (joyY < -1.0f) joyY = -1.0f; if (joyY > 1.0f) joyY = 1.0f; } - // Xbox 360 thumbsticks use SHORT values from -32768 to 32767. - // NOTE: Windows screen Y goes down, but Xbox thumbstick Y goes UP. We invert joyY. g_report.sThumbLX = (SHORT)(joyX * 32767.0f); g_report.sThumbLY = (SHORT)(joyY * -32767.0f); - // Send the updated state to the virtual controller vigem_target_x360_update(g_client, g_pad, g_report); } @@ -136,7 +172,8 @@ void HandleUpDown(Button* button, bool down) switch (button->type) { case BTN_KEY: - HandleKeyButton(button, down); + // Route through our new Xbox Button handler! + HandleXboxButton(button, down); break; case BTN_WHEEL: HandleWheelButton(button, down); @@ -150,7 +187,7 @@ void HandleUpDown(Button* button, bool down) LRESULT CALLBACK OnTouch(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { BUTTON(hWnd, button); - TOUCHINPUT touch; //Only handle the first touch on any button + TOUCHINPUT touch; if (GetTouchInputInfo((HTOUCHINPUT)lParam, 1, &touch, sizeof(TOUCHINPUT))) { From b666bdeb930fb94d0022a6e58efe9b3807b7c051 Mon Sep 17 00:00:00 2001 From: Jeardey Date: Tue, 23 Jun 2026 11:57:31 +0200 Subject: [PATCH 8/8] docs: modernize README.md --- README.md | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 2c5e0ef..89d746e 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,42 @@ -TouchJoy - On screen gamepad for touch-screen Windows devices +TouchJoy - On-screen gamepad for touch-screen Windows devices ============================================================= ## What is it? -This program helps you create an on-screen gamepad so that you can game on a touch-enabled Windows device: +This program creates a highly customizable on-screen virtual gamepad so you can play modern PC games on a touch-enabled Windows device (like a Microsoft Surface, ROG Ally, or Windows tablet). ![screenshot](data/screenshot.jpg) -The layout is fully customizable using a simple [ini file](data/sample.ini). -The file is also automatically reloaded whenever it is saved so that you can tweak and adjust it quickly. +Unlike legacy mappers that just send fake keyboard presses, **TouchJoy natively emulates an Xbox 360 Controller**. This means it is instantly recognized by modern games via XInput with full analog joystick and trigger support. -## Why? +The layout is fully customizable using a simple `config.ini` file. The file is automatically reloaded whenever it is saved, so you can tweak and adjust your layout in real-time without restarting the app. -I want to play Windows games lying down and there are not many good on-screen gamepad applications for Windows. -It is also a chance to learn how far I can go with only C99 and as few external libraries as possible. +## Prerequisites -## How to build? +Because TouchJoy emulates a physical hardware controller, it requires the **ViGEmBus** kernel-mode driver to be installed on your system. -You need [GENie](https://github.com/bkaradzic/genie) and Visual Studio 2013+ since the project is written in C99. +* If you do not have it installed, TouchJoy will automatically prompt you on startup and open your browser to the [Official ViGEmBus GitHub Releases](https://github.com/nefarius/ViGEmBus/releases/latest) page so you can download it safely. -`vs2013.bat` can be used to generate a solution for Visual Studio 2013. +## How to install and use? -## How to use? +**The easy way:** +1. Go to the **Actions** or **Releases** tab on this GitHub repository. +2. Download the latest `TouchJoy-Windows.zip` artifact. +3. Extract the folder anywhere on your PC. +4. Double click `TouchJoy.exe`. +5. To change your layout or map different buttons, open `config.ini` in any text editor. (For now, the code and `config.ini` itself are the best manuals). -For now, the code and [this sample](data/sample.ini) are the only manuals. -I'm terribly sorry. +## How to build from source? -## External libraries +The project has been modernized to use **CMake**. It requires Visual Studio 2019/2022 (with the Desktop C++ workload) or any modern C99-compliant compiler. -* [stb_image](https://github.com/nothings/stb): image loading -* [gb_ini](https://github.com/gingerBill/gb): ini parsing -* [utest](https://github.com/evolutional/utest): unit testing +```bash +# Clone the repository +git clone [https://github.com/YOUR-USERNAME/TouchJoy.git](https://github.com/YOUR-USERNAME/TouchJoy.git) +cd TouchJoy -Special thanks to Sean Barrett for providing an useful [list](https://github.com/nothings/stb/blob/master/docs/other_libs.md) of single-header libraries. +# Configure the build system (this will automatically fetch ViGEmClient) +cmake -B build -S . + +# Compile the project +cmake --build build --config Release