Last active
August 4, 2024 19:00
-
-
Save nm004/e34b40ef3c2ab3ab8ec3eab2376ca7ab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* autoholdclick - auto clicking while holding a button (for Linux only). | |
* | |
* Build: | |
* cc -o autoholdclick autoholdclick.c $(pkg-config --cflags --libs libevdev) | |
* | |
* This software is in the public domain. | |
*/ | |
#include <libevdev/libevdev-uinput.h> | |
#include <sys/time.h> | |
#include <signal.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <string.h> | |
#include <unistd.h> | |
char *program_invocation_name; | |
const char YES_AUTO_CLICK = !0; | |
int is_interrupted_by_user = 0; | |
struct libevdev_uinput *uinput_dev = NULL; | |
int current_key_code = 0; | |
static inline void | |
click () | |
{ | |
libevdev_uinput_write_event (uinput_dev, EV_KEY, current_key_code, 1); | |
libevdev_uinput_write_event (uinput_dev, EV_SYN, SYN_REPORT, 0); | |
libevdev_uinput_write_event (uinput_dev, EV_KEY, current_key_code, 0); | |
libevdev_uinput_write_event (uinput_dev, EV_SYN, SYN_REPORT, 0); | |
} | |
static void | |
on_sigalrm (int sig) | |
{ | |
click (); | |
} | |
static void | |
on_sigint (int sig) | |
{ | |
is_interrupted_by_user = 1; | |
} | |
static inline void | |
set_sig (void) | |
{ | |
sigset_t block_set; | |
sigfillset(&block_set); | |
sigdelset(&block_set, SIGALRM); | |
sigdelset(&block_set, SIGTERM); | |
sigdelset(&block_set, SIGHUP); | |
sigdelset(&block_set, SIGINT); | |
sigdelset(&block_set, SIGUSR1); | |
sigdelset(&block_set, SIGUSR2); | |
sigprocmask(SIG_BLOCK, &block_set, NULL); | |
sigaction (SIGALRM, &(struct sigaction){ .sa_handler = on_sigalrm }, NULL); | |
sigaction (SIGTERM, &(struct sigaction){ .sa_handler = on_sigint }, NULL); | |
sigaction (SIGHUP, &(struct sigaction){ .sa_handler = on_sigint }, NULL); | |
sigaction (SIGINT, &(struct sigaction){ .sa_handler = on_sigint }, NULL); | |
sigaction (SIGUSR1, &(struct sigaction){ .sa_handler = on_sigint }, NULL); | |
sigaction (SIGUSR2, &(struct sigaction){ .sa_handler = on_sigint }, NULL); | |
} | |
static inline void | |
usage (void) | |
{ | |
printf( | |
"Usage: %s [OPTION]... [BTN]... INPUT_DEV \n" | |
"Click repeatedly while holding a button BTN of a device INPUT_DEV.\n" | |
"\n" | |
" BTN, Buttons subject to auto click. Default is None (no buttons\n" | |
" to auto click). Valid button name is BTN_LEFT, BTN_RIGHT,\n" | |
" BTN_MIDDLE, BTN_THUMB, BTN_4, BTN_5, etc.\n" | |
" INPUT_DEV, evdev device (need read permission)\n" | |
"\n" | |
"Options:\n" | |
" -c, Use the complement of BTNs.\n" | |
" -T, Use hardware timing to auto-click. Default is off (use software timing).\n" | |
"\n" | |
"Also, need read/write permission for /dev/uinput.\n" | |
"\n" | |
"Example: %s BTN_MIDDLE BTN_LEFT /dev/input/by-id/your-event-mouse\n" | |
, program_invocation_name, program_invocation_name | |
); | |
} | |
int | |
main (int argc, | |
char **argv) | |
{ | |
int rc = 0; | |
int in_dev_fd = -1; | |
struct libevdev* in_dev = NULL; | |
int use_hw_timing = 0; | |
char auto_click_flag_tbl[KEY_CNT] = {0}; | |
/* TODO?: make them user options. */ | |
const suseconds_t interval_usec = 32 * 1000; | |
const suseconds_t start_delay_usec = 200 * 1000; | |
program_invocation_name = argv[0]; | |
if (argc < 2) | |
{ | |
usage (); | |
return !0; | |
} | |
while ((rc = getopt(argc, argv, "cT")) != -1) | |
{ | |
switch (rc) | |
{ | |
case 'c': | |
/* Set every buttons' auto click flag up */ | |
memset (auto_click_flag_tbl, YES_AUTO_CLICK, sizeof(auto_click_flag_tbl)); | |
break; | |
case 'T': | |
use_hw_timing = 1; | |
break; | |
default: | |
usage (); | |
return !0; | |
} | |
} | |
/* Set each BTN's auto click flag */ | |
for (int i = optind; i < argc-1; i++) | |
{ | |
int code = libevdev_event_code_from_code_name(argv[i]); | |
if (code == -1) | |
{ | |
fprintf(stderr, "%s is not a valid button name.\n", argv[i]); | |
return !0; | |
} | |
auto_click_flag_tbl[code] = !auto_click_flag_tbl[code]; | |
} | |
in_dev_fd = open (argv[argc-1], O_RDONLY); | |
if (in_dev_fd == -1) | |
{ | |
perror("failed to open device file"); | |
return !0; | |
} | |
/* Obtain input device */ | |
rc = libevdev_new_from_fd (in_dev_fd, &in_dev); | |
if (rc < 0) | |
{ | |
perror("failed to obtain input device"); | |
return !0; | |
} | |
/* Create simulated input device */ | |
rc = libevdev_uinput_create_from_device (in_dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uinput_dev); | |
if (rc < 0) | |
{ | |
perror("failed to create uinput device"); | |
libevdev_free (in_dev); | |
return !0; | |
} | |
set_sig(); | |
puts ("Auto click mode is starting in 1 sec. DON'T PRESS ANY BUTTONS/KEYS."); | |
sleep (1); | |
/* Event loop */ | |
libevdev_grab (in_dev, LIBEVDEV_GRAB); | |
puts ("Auto click is enabled"); | |
do | |
{ | |
struct input_event ev; | |
if (is_interrupted_by_user) | |
{ | |
break; | |
} | |
rc = libevdev_next_event (in_dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); | |
if (rc != LIBEVDEV_READ_STATUS_SUCCESS) | |
{ | |
continue; | |
} | |
/* Pass through events */ | |
libevdev_uinput_write_event (uinput_dev, ev.type, ev.code, ev.value); | |
/* When not key events, nor the key subject to auto click, then just pass through events */ | |
if (ev.type != EV_KEY | |
|| auto_click_flag_tbl[ev.code] != YES_AUTO_CLICK) | |
{ | |
continue; | |
} | |
/* Process button press events */ | |
if (use_hw_timing) | |
{ | |
if (ev.value == 2) | |
{ | |
current_key_code = ev.code; | |
click (); | |
} | |
} | |
/* Use software timing */ | |
else | |
{ | |
/* Arm the timer */ | |
if (ev.value == 1) | |
{ | |
current_key_code = ev.code; | |
setitimer (ITIMER_REAL, &(struct itimerval){{0,interval_usec},{0,start_delay_usec}}, NULL); | |
} | |
/* Disarm the timer */ | |
else if (ev.value == 0) | |
{ | |
setitimer (ITIMER_REAL, &(struct itimerval){0}, NULL); | |
} | |
} | |
} | |
while (rc == LIBEVDEV_READ_STATUS_SYNC | |
|| rc == LIBEVDEV_READ_STATUS_SUCCESS | |
|| rc == -EAGAIN | |
|| rc == -EINTR); | |
libevdev_grab (in_dev, LIBEVDEV_UNGRAB); | |
puts ("Auto click is disabled"); | |
setitimer (ITIMER_REAL, &(struct itimerval){0}, NULL); | |
libevdev_uinput_destroy (uinput_dev); | |
libevdev_free (in_dev); | |
close (in_dev_fd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment