Skip to content

Instantly share code, notes, and snippets.

@taeber
Created February 13, 2024 03:44
Show Gist options
  • Select an option

  • Save taeber/37a3ef124bbcc6f4806413f57c94a7a9 to your computer and use it in GitHub Desktop.

Select an option

Save taeber/37a3ef124bbcc6f4806413f57c94a7a9 to your computer and use it in GitHub Desktop.

Revisions

  1. taeber created this gist Feb 13, 2024.
    147 changes: 147 additions & 0 deletions mousectl.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,147 @@
    // mousectl - Swap mouse buttons from the macOS Terminal

    // From https://superuser.com/a/1782251
    // with changes by taeber@rapczak.com

    // Compile:
    // clang -framework IOKit -framework Foundation -o mousectl mousectl.c

    #include <IOKit/hidsystem/IOHIDLib.h>
    #include <IOKit/hidsystem/IOHIDParameter.h>
    #include <IOKit/hidsystem/event_status_driver.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    static char *describe(IOHIDButtonModes mode) {
    switch (mode) {
    case kIOHIDButtonMode_BothLeftClicks: // 0
    return "Both Left Clicks";
    case kIOHIDButtonMode_ReverseLeftRightClicks: // 1
    return "Reverse Left Right Clicks";
    case kIOHIDButtonMode_EnableRightClick: // 2
    return "Enable Right Click";
    default:
    return "<unknown>";
    }
    }

    static bool GetButtonMode(io_connect_t handle, IOHIDButtonModes *mode) {
    kern_return_t ret;
    IOByteCount actualSize;
    ret = IOHIDGetParameter(handle, CFSTR(kIOHIDPointerButtonMode), sizeof *mode,
    mode, &actualSize);
    if (ret != KERN_SUCCESS) {
    fprintf(stderr, "Error! IOHIDGetParameter failed: %d.\n", ret);
    return false;
    }

    if (actualSize != sizeof *mode) {
    // TODO: maybe currentButtonStatus should be void* with an explicit cast
    // to IOHIDButtonModes
    fprintf(
    stderr,
    "Warning! IOHIDGetParameter returned unexpected actualSize! (Got %d; "
    "wanted %ld)\n",
    (int)actualSize, sizeof *mode);
    }

    return true;
    }

    static bool SetButtonMode(io_connect_t handle, IOHIDButtonModes newMode) {
    kern_return_t ret;
    IOByteCount actualSize;
    ret = IOHIDSetParameter(handle, CFSTR(kIOHIDPointerButtonMode), &newMode,
    sizeof newMode);
    if (ret != KERN_SUCCESS) {
    fprintf(stderr, "Error! IOHIDSetParameter failed: %d\n", ret);
    return ret;
    }
    return true;
    }

    static int print(io_connect_t handle) {
    IOHIDButtonModes buttonMode;
    if (!GetButtonMode(handle, &buttonMode))
    return 1;

    printf("Current mouse button mode: %s\n", describe(buttonMode));
    return 0;
    }

    static int swap(io_connect_t handle) {
    IOHIDButtonModes currentButtonStatus;
    if (!GetButtonMode(handle, &currentButtonStatus))
    return 1;

    IOHIDButtonModes newButtonStatus;
    switch (currentButtonStatus) {
    case kIOHIDButtonMode_BothLeftClicks: // 0
    case kIOHIDButtonMode_ReverseLeftRightClicks: // 1
    newButtonStatus = kIOHIDButtonMode_EnableRightClick;
    break;
    case kIOHIDButtonMode_EnableRightClick: // 2
    newButtonStatus = kIOHIDButtonMode_ReverseLeftRightClicks;
    break;
    default:
    fprintf(stderr, "Unknown IOHIDButtonModes value: %d\n",
    currentButtonStatus);
    return 1;
    }

    if (!SetButtonMode(handle, newButtonStatus))
    return 1;

    printf("New mouse button mode is : %s\n", describe(newButtonStatus));
    printf("Old mouse button mode was: %s\n", describe(currentButtonStatus));

    return 0;
    }

    static char cmd[] = "mousectl";

    static int usage(bool helpWanted) {
    FILE *fp = helpWanted ? stdout : stderr;
    fprintf(fp, "Usage: %s <subcommand>\n\n", cmd);
    fprintf(fp, "%s\n", "Subcommands:");
    fprintf(fp, "%s\n", "\tprint Prints the current mouse button mode.");
    fprintf(fp, "%s\n", "\tswap Swaps mouse buttons.");
    fprintf(fp, "%s\n", "\thelp Prints this usage.");
    return helpWanted ? 0 : 1;
    }

    int main(int argc, char *argv[]) {
    if (argc <= 0)
    return 42;

    if (argc == 1)
    return usage(false);

    char *subcmd = argv[1];
    int (*sub)(io_connect_t);
    if (strcmp("print", subcmd) == 0) {
    sub = print;
    } else if (strcmp("swap", subcmd) == 0) {
    sub = swap;
    } else if (strcmp("help", subcmd) == 0 || strcmp("-h", subcmd) == 0 ||
    strcmp("-help", subcmd) == 0 || strcmp("--help", subcmd) == 0 ||
    strcmp("/?", subcmd) == 0) {
    return usage(true);
    } else {
    fprintf(stderr, "Error! Unrecognized subcommand: %s\n", subcmd);
    return usage(false);
    }

    io_connect_t handle = NXOpenEventStatus();
    if (!handle) {
    fprintf(stderr, "%s\n", "NXOpenEventStatus failed!");
    return 1;
    }

    int ret = sub(handle);
    NXCloseEventStatus(handle);

    return ret;
    }