initial commit
This commit is contained in:
commit
d4af9eb218
3 changed files with 961 additions and 0 deletions
246
swaystick.c
Normal file
246
swaystick.c
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* swaystick: control Sway with a DualSense gamepad
|
||||
* Copyright (c) 2024 Jeremy Baxter.
|
||||
*/
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <linux/joystick.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* DualSense button mapping */
|
||||
enum DSButton {
|
||||
DS_CROSS = 0,
|
||||
DS_CIRCLE = 1,
|
||||
DS_TRIANGLE = 2,
|
||||
DS_SQUARE = 3,
|
||||
DS_L1 = 4,
|
||||
DS_R1 = 5,
|
||||
DS_L2 = 6,
|
||||
DS_R2 = 7,
|
||||
DS_CREATE = 8,
|
||||
DS_OPTIONS = 9,
|
||||
DS_PLAYSTATION = 10,
|
||||
DS_LSTICK = 11,
|
||||
DS_RSTICK = 12
|
||||
};
|
||||
|
||||
int
|
||||
read_event(int fd, struct js_event *event)
|
||||
{
|
||||
ssize_t bytes;
|
||||
|
||||
bytes = read(fd, event, sizeof(*event));
|
||||
|
||||
if (bytes == sizeof(*event))
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t
|
||||
get_axis_count(int fd)
|
||||
{
|
||||
__u8 axes;
|
||||
|
||||
if (ioctl(fd, JSIOCGAXES, &axes) == -1)
|
||||
return 0;
|
||||
|
||||
return axes;
|
||||
}
|
||||
|
||||
size_t
|
||||
get_button_count(int fd)
|
||||
{
|
||||
__u8 buttons;
|
||||
if (ioctl(fd, JSIOCGBUTTONS, &buttons) == -1)
|
||||
return 0;
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
char *
|
||||
make_move_command(int pos, int x)
|
||||
{
|
||||
char *buffer;
|
||||
size_t len;
|
||||
|
||||
len = 32;
|
||||
buffer = malloc(len * sizeof(char));
|
||||
|
||||
snprintf(buffer, len, "move %s %d",
|
||||
x ? "right" : "down",
|
||||
pos / 2000);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *
|
||||
make_resize_command(int pos, int x)
|
||||
{
|
||||
char *buffer;
|
||||
size_t len;
|
||||
|
||||
len = 32;
|
||||
buffer = malloc(len * sizeof(char));
|
||||
|
||||
snprintf(buffer, len, "resize %s %s %d",
|
||||
pos > 0 ? "grow" : "shrink",
|
||||
x ? "width" : "height",
|
||||
pos / 2000);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void
|
||||
swaymsg(const char *msg)
|
||||
{
|
||||
const char *prog = "swaymsg";
|
||||
pid_t child;
|
||||
int wstatus;
|
||||
|
||||
child = fork();
|
||||
switch (child) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
break;
|
||||
case 0:
|
||||
if (execlp(prog, prog, msg, NULL) == -1)
|
||||
perror("execlp");
|
||||
break;
|
||||
default:
|
||||
if (wait(&wstatus) == -1)
|
||||
perror("wait");
|
||||
}
|
||||
}
|
||||
|
||||
struct axis_state {
|
||||
short x, y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Keeps track of the current axis state.
|
||||
*
|
||||
* NOTE: This function assumes that axes are numbered starting from 0,
|
||||
* and that the X axis is an even number, and the Y axis is an odd number.
|
||||
* However, this is usually a safe assumption.
|
||||
*
|
||||
* Returns the axis that the event indicated.
|
||||
*/
|
||||
size_t
|
||||
get_axis_state(struct js_event *event, struct axis_state axes[3])
|
||||
{
|
||||
size_t axis = event->number / 2;
|
||||
|
||||
if (axis < 3) {
|
||||
if (event->number % 2 == 0)
|
||||
axes[axis].x = event->value;
|
||||
else
|
||||
axes[axis].y = event->value;
|
||||
}
|
||||
|
||||
return axis;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct axis_state axes[3] = {0};
|
||||
struct js_event event;
|
||||
const char *device;
|
||||
size_t axis;
|
||||
int js;
|
||||
int moving;
|
||||
|
||||
moving = 0;
|
||||
|
||||
if (argc > 1)
|
||||
device = argv[1];
|
||||
else
|
||||
device = "/dev/input/js0";
|
||||
|
||||
js = open(device, O_RDONLY);
|
||||
|
||||
if (js == -1)
|
||||
perror(device);
|
||||
|
||||
/* This loop will exit if the controller is unplugged. */
|
||||
while (read_event(js, &event) == 0) {
|
||||
if (moving)
|
||||
swaymsg("move position cursor");
|
||||
|
||||
switch (event.type) {
|
||||
case JS_EVENT_BUTTON:
|
||||
if (event.value) { /* pressed */
|
||||
switch (event.number) {
|
||||
case DS_CROSS:
|
||||
swaymsg("kill");
|
||||
break;
|
||||
case DS_TRIANGLE:
|
||||
moving = 1;
|
||||
break;
|
||||
case DS_LSTICK:
|
||||
swaymsg("focus prev");
|
||||
break;
|
||||
case DS_RSTICK:
|
||||
swaymsg("focus next");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* released */
|
||||
switch (event.number) {
|
||||
case DS_CIRCLE:
|
||||
swaymsg("focus mode_toggle");
|
||||
break;
|
||||
case DS_TRIANGLE:
|
||||
moving = 0;
|
||||
break;
|
||||
case DS_SQUARE:
|
||||
swaymsg("floating toggle");
|
||||
break;
|
||||
case DS_L1:
|
||||
swaymsg("workspace prev");
|
||||
break;
|
||||
case DS_R1:
|
||||
swaymsg("workspace next");
|
||||
break;
|
||||
case DS_CREATE:
|
||||
swaymsg("layout tabbed");
|
||||
break;
|
||||
case DS_OPTIONS:
|
||||
swaymsg("layout toggle split");
|
||||
break;
|
||||
case DS_PLAYSTATION:
|
||||
swaymsg("exec 'emacsclient -c'");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case JS_EVENT_AXIS:
|
||||
axis = get_axis_state(&event, axes);
|
||||
|
||||
if (axis > 2)
|
||||
break;
|
||||
/* no activity? */
|
||||
if (axes[axis].x > -2000 && axes[axis].x < 2000
|
||||
&& axes[axis].y > -2000 && axes[axis].y < 2000)
|
||||
break;
|
||||
|
||||
if (axis == 0) { /* left X/Y */
|
||||
swaymsg(make_move_command(axes[axis].x, 1));
|
||||
swaymsg(make_move_command(axes[axis].y, 0));
|
||||
}
|
||||
default:
|
||||
/* ignore init events */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(js);
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue