Skip to content

Instantly share code, notes, and snippets.

@taeber
Created February 13, 2024 03:44
Show Gist options
  • Save taeber/37a3ef124bbcc6f4806413f57c94a7a9 to your computer and use it in GitHub Desktop.
Save taeber/37a3ef124bbcc6f4806413f57c94a7a9 to your computer and use it in GitHub Desktop.
mousectl - Swap mouse buttons from the macOS Terminal
// mousectl - Swap mouse buttons from the macOS Terminal
// From https://superuser.com/a/1782251
// with changes by [email protected]
// 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment