From bdff7d84ef1f18f8af9d9e6fa6150a0b3412517b Mon Sep 17 00:00:00 2001 From: Martin Piatka <piatka@cesnet.cz> Date: Thu, 15 Sep 2022 16:18:11 +0200 Subject: [PATCH] Add volume-knob helper --- volume-knob-powermate-1.0/Makefile | 13 +++ volume-knob-powermate-1.0/README.md | 9 ++ volume-knob-powermate-1.0/findpowermate.c | 57 +++++++++++ volume-knob-powermate-1.0/findpowermate.h | 7 ++ volume-knob-powermate-1.0/pulseled.c | 114 ++++++++++++++++++++++ volume-knob-powermate-1.0/rotomatic.c | 100 +++++++++++++++++++ 6 files changed, 300 insertions(+) create mode 100644 volume-knob-powermate-1.0/Makefile create mode 100644 volume-knob-powermate-1.0/README.md create mode 100644 volume-knob-powermate-1.0/findpowermate.c create mode 100644 volume-knob-powermate-1.0/findpowermate.h create mode 100644 volume-knob-powermate-1.0/pulseled.c create mode 100644 volume-knob-powermate-1.0/rotomatic.c diff --git a/volume-knob-powermate-1.0/Makefile b/volume-knob-powermate-1.0/Makefile new file mode 100644 index 0000000..bf4e74a --- /dev/null +++ b/volume-knob-powermate-1.0/Makefile @@ -0,0 +1,13 @@ +all: rotomatic pulseled + +rotomatic: rotomatic.o findpowermate.o + $(CC) findpowermate.o rotomatic.o -o rotomatic -lxdo + +pulseled: pulseled.o findpowermate.o + $(CC) findpowermate.o pulseled.o -o pulseled + +clean: + rm -f *.o *~ rotomatic pulseled + +%.o: %.c + $(CC) -O2 -Wall -c $< -o $@ diff --git a/volume-knob-powermate-1.0/README.md b/volume-knob-powermate-1.0/README.md new file mode 100644 index 0000000..7f43776 --- /dev/null +++ b/volume-knob-powermate-1.0/README.md @@ -0,0 +1,9 @@ +Tools to support the Griffin powermate volume knob + +The source was taken from https://sowerbutts.com/powermate/ and modified to +send mouse scroll wheel events. + +Run rotomatic with the input device path as an argument. +``` +./rotomatic /dev/input/event<num> +``` diff --git a/volume-knob-powermate-1.0/findpowermate.c b/volume-knob-powermate-1.0/findpowermate.c new file mode 100644 index 0000000..9e0e251 --- /dev/null +++ b/volume-knob-powermate-1.0/findpowermate.c @@ -0,0 +1,57 @@ +#include <linux/input.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include "findpowermate.h" + +#define NUM_VALID_PREFIXES 2 + +static const char *valid_prefix[NUM_VALID_PREFIXES] = { + "Griffin PowerMate", + "Griffin SoundKnob" +}; + +#define NUM_EVENT_DEVICES 16 + +int open_powermate(const char *dev, int mode) +{ + int fd = open(dev, mode); + int i; + char name[255]; + + if(fd < 0){ + fprintf(stderr, "Unable to open \"%s\": %s\n", dev, strerror(errno)); + return -1; + } + + if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0){ + fprintf(stderr, "\"%s\": EVIOCGNAME failed: %s\n", dev, strerror(errno)); + close(fd); + return -1; + } + + // it's the correct device if the prefix matches what we expect it to be: + for(i=0; i<NUM_VALID_PREFIXES; i++) + if(!strncasecmp(name, valid_prefix[i], strlen(valid_prefix[i]))) + return fd; + + close(fd); + return -1; +} + +int find_powermate(int mode) +{ + char devname[256]; + int i, r; + + for(i=0; i<NUM_EVENT_DEVICES; i++){ + sprintf(devname, "/dev/input/event%d", i); + r = open_powermate(devname, mode); + if(r >= 0) + return r; + } + + return -1; +} diff --git a/volume-knob-powermate-1.0/findpowermate.h b/volume-knob-powermate-1.0/findpowermate.h new file mode 100644 index 0000000..2cf6f9b --- /dev/null +++ b/volume-knob-powermate-1.0/findpowermate.h @@ -0,0 +1,7 @@ +#ifndef __FIND_POWERMATE__DOT_H__SENTRY__ +#define __FIND_POWERMATE__DOT_H__SENTRY__ + +int open_powermate(const char *dev, int mode); +int find_powermate(int mode); + +#endif diff --git a/volume-knob-powermate-1.0/pulseled.c b/volume-knob-powermate-1.0/pulseled.c new file mode 100644 index 0000000..b3b7477 --- /dev/null +++ b/volume-knob-powermate-1.0/pulseled.c @@ -0,0 +1,114 @@ +#include <linux/input.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include "findpowermate.h" + +#ifndef MSC_PULSELED +/* this may not have made its way into the kernel headers yet ... */ +#define MSC_PULSELED 0x01 +#endif + +void powermate_pulse_led(int fd, int static_brightness, int pulse_speed, int pulse_table, int pulse_asleep, int pulse_awake) +{ + struct input_event ev; + memset(&ev, 0, sizeof(struct input_event)); + + static_brightness &= 0xFF; + + if(pulse_speed < 0) + pulse_speed = 0; + if(pulse_speed > 510) + pulse_speed = 510; + if(pulse_table < 0) + pulse_table = 0; + if(pulse_table > 2) + pulse_table = 2; + pulse_asleep = !!pulse_asleep; + pulse_awake = !!pulse_awake; + + ev.type = EV_MSC; + ev.code = MSC_PULSELED; + ev.value = static_brightness | (pulse_speed << 8) | (pulse_table << 17) | (pulse_asleep << 19) | (pulse_awake << 20); + + if(write(fd, &ev, sizeof(struct input_event)) != sizeof(struct input_event)) + fprintf(stderr, "write(): %s\n", strerror(errno)); +} + +void usage(void) +{ + printf("Usage:\n"); + printf("pulseled [options]\n"); + printf("\t--device [filename]\tEvent device to use\n"); + printf("\t--level [level] \tLED intensity with pulsing disabled (0-255)\n"); + printf("\t--speed [rate] \tPulsing speed (0-510); around 255 works best\n"); + printf("\t--style [style] \tPulsing style (0, 1 or 2)\n"); + printf("\t--pulseawake \tEnable pulsing whilst host is awake\n"); + printf("\t--nopulseawake \tDisable pulsing whilst host is awake\n"); + printf("\t--pulseasleep \tEnable pulsing whilst host is asleep\n"); + printf("\t--nopulseasleep \tDisable pulsing whilst host is asleep\n"); +} + +#define TAKE_NEXT_ARG {if(++args_processed >= argc){fprintf(stderr, "Missing argument to %s!\n", argv[args_processed-1]);return 1;}} + +int main(int argc, char *argv[]) +{ + int powermate = -1; + int args_processed = 1; + int static_brightness = 0x80; + int pulse_speed = 255; + int pulse_table = 0; + int pulse_asleep = 1; + int pulse_awake = 0; + + const char *device = 0; + + while(args_processed < argc){ + if(!strcasecmp(argv[args_processed], "--device")){ + TAKE_NEXT_ARG; + device = argv[args_processed]; + }else if(!strcasecmp(argv[args_processed], "--level")){ + TAKE_NEXT_ARG; + static_brightness = atoi(argv[args_processed]); + }else if(!strcasecmp(argv[args_processed], "--speed")){ + TAKE_NEXT_ARG; + pulse_speed = atoi(argv[args_processed]); + }else if(!strcasecmp(argv[args_processed], "--style")){ + TAKE_NEXT_ARG; + pulse_table = atoi(argv[args_processed]); + }else if(!strcasecmp(argv[args_processed], "--pulseawake")){ + pulse_awake = 1; + }else if(!strcasecmp(argv[args_processed], "--pulseasleep")){ + pulse_asleep = 1; + }else if(!strcasecmp(argv[args_processed], "--nopulseawake")){ + pulse_awake = 0; + }else if(!strcasecmp(argv[args_processed], "--nopulseasleep")){ + pulse_asleep = 0; + }else{ + usage(); + return 1; + } + + args_processed++; + } + + if(device) + powermate = open_powermate(device, O_WRONLY); + else + powermate = find_powermate(O_WRONLY); + + if(powermate < 0){ + fprintf(stderr, "Unable to locate powermate\n"); + fprintf(stderr, "Try: %s [device]\n", argv[0]); + return 1; + } + + powermate_pulse_led(powermate, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake); + + close(powermate); + + return 0; +} diff --git a/volume-knob-powermate-1.0/rotomatic.c b/volume-knob-powermate-1.0/rotomatic.c new file mode 100644 index 0000000..dbf5b56 --- /dev/null +++ b/volume-knob-powermate-1.0/rotomatic.c @@ -0,0 +1,100 @@ +#include <linux/input.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <xdo.h> +#include "findpowermate.h" + +int abs_offset = 0; + +xdo_t *x_ctx = NULL; + +void process_event(struct input_event *ev) +{ +#ifdef VERBOSE + fprintf(stderr, "type=0x%04x, code=0x%04x, value=%d\n", + ev->type, ev->code, (int)ev->value); +#endif + + switch(ev->type){ + case EV_MSC: + printf("The LED pulse settings were changed; code=0x%04x, value=0x%08x\n", ev->code, ev->value); + break; + case EV_REL: + if(ev->code != REL_DIAL) + fprintf(stderr, "Warning: unexpected rotation event; ev->code = 0x%04x\n", ev->code); + else{ + abs_offset += (int)ev->value; + printf("Button was rotated %d units; Offset from start is now %d units\n", (int)ev->value, abs_offset); + for(int i = (int) ev->value; i > 0; i--){ + xdo_mouse_down(x_ctx, CURRENTWINDOW, 5); + xdo_mouse_up(x_ctx, CURRENTWINDOW, 5); + } + for(int i = (int) ev->value; i < 0; i++){ + xdo_mouse_down(x_ctx, CURRENTWINDOW, 4); + xdo_mouse_up(x_ctx, CURRENTWINDOW, 4); + } + } + break; + case EV_KEY: + if(ev->code != BTN_0) + fprintf(stderr, "Warning: unexpected key event; ev->code = 0x%04x\n", ev->code); + else + printf("Button was %s\n", ev->value? "pressed":"released"); + break; + default: + fprintf(stderr, "Warning: unexpected event type; ev->type = 0x%04x\n", ev->type); + } + + fflush(stdout); +} + +#define BUFFER_SIZE 32 +void watch_powermate(int fd) +{ + struct input_event ibuffer[BUFFER_SIZE]; + int r, events, i; + + while(1){ + r = read(fd, ibuffer, sizeof(struct input_event) * BUFFER_SIZE); + if( r > 0 ){ + events = r / sizeof(struct input_event); + for(i=0; i<events; i++) + process_event(&ibuffer[i]); + }else{ + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + return; + } + } +} + +int main(int argc, char *argv[]) +{ + int powermate = -1; + + if(argc == 1) + powermate = find_powermate(O_RDONLY); + else + powermate = open_powermate(argv[1], O_RDONLY); + + if(powermate < 0){ + fprintf(stderr, "Unable to locate powermate\n"); + fprintf(stderr, "Try: %s [device]\n", argv[0]); + return 1; + } + + x_ctx = xdo_new(NULL); + if(!x_ctx){ + printf("Unable to init xdo\n"); + close(powermate); + return -1; + } + + watch_powermate(powermate); + + close(powermate); + + return 0; +} -- GitLab