aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <yalhcru@gmail.com>2020-05-16 05:55:13 -0400
committerB. Watson <yalhcru@gmail.com>2020-05-16 05:55:13 -0400
commitf6690f23148f7bf1445dc6435742fccff0cb1b3e (patch)
treedb2fe0a659d49b4f06a8b69e428d988c43e17041
downloadunsaver-f6690f23148f7bf1445dc6435742fccff0cb1b3e.tar.gz
initial commit
-rw-r--r--Makefile41
-rw-r--r--jsmond.1119
-rw-r--r--jsmond.c287
-rw-r--r--jsmond.rst134
4 files changed, 581 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3c1379f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,41 @@
+# GNU Makefile for jsmond
+PROJ=jsmond
+
+# the .rst is the authoritative source for the version number.
+VERSION=$(shell fgrep '.. |version| replace::' $(PROJ).rst | cut -d' ' -f4)
+
+# maximum number of devices we can monitor. 16 seems like an awful lot...
+MAX_STICKS=16
+
+# base name of joystick devices on your OS, gets numbers 0 to MAX_STICKS
+# appended to it during autodetection
+JSDEVBASE="/dev/input/js"
+
+# Override this, not CFLAGS
+OPTFLAGS=-Wall -ansi -pedantic -std=c89 -g
+
+# If you can think of a reason to compile without X11 support,
+# set this to 0 (or anything other than 1). Note that you can
+# compile with X11 and then disable it at runtime with -x.
+HAVE_X11=1
+
+DEFINES=-DVERSION=\"$(VERSION)\" -DMAX_STICKS=$(MAX_STICKS) -DJSDEVBASE=\"$(JSDEVBASE)\"
+
+ifeq ($(HAVE_X11),1)
+CFLAGS+=$(shell pkg-config --cflags x11)
+LDFLAGS+=$(shell pkg-config --libs x11)
+DEFINES+=-DHAVE_X11
+endif
+
+
+CFLAGS=$(OPTFLAGS) $(DEFINES)
+
+all: jsmond
+
+man: jsmond.rst
+ rst2man.py jsmond.rst > jsmond.1
+
+clean:
+ rm -f $(PROJ)
+
+.PHONY: all man clean
diff --git a/jsmond.1 b/jsmond.1
new file mode 100644
index 0000000..df25a5e
--- /dev/null
+++ b/jsmond.1
@@ -0,0 +1,119 @@
+.\" Man page generated from reStructuredText.
+.
+.TH JSMOND 1 "2020-05-15" "0.1.0" "Urchlay"
+.SH NAME
+jsmond \- disable screensaver on joystick activity
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.\" RST source for jsmond(1) man page. Convert with:
+.
+.\" rst2man.py jsmond.rst > jsmond.1
+.
+.SH SYNOPSIS
+.sp
+jsmond [\fB\-c command\fP] [\fB\-i seconds\fP] [\fB\-d\fP] [\fB\-x\fP] [\fBjoydev [joydev ...]\fP]
+.SH DESCRIPTION
+.sp
+jsmond lets you play games with your joysticks/gamepads without
+xscreensaver activating due to lack of keyboard/mouse input.
+.sp
+Up to 16 joystick devices can be monitored. By default, jsmond searches
+for these and monitors all the joysticks it finds. You can override
+this on the command line by providing one or more \fBjoydev\fP arguments,
+in which case no autodetection will be done.
+.sp
+Every \fIinterval\fP seconds (60, or whatever \fB\-i\fP is set to), jsmond
+checks to see if there\(aqs been any activity on any of the devices it\(aqs
+monitoring. If so, it runs the \fBxscreensaver\-command \-deactivate\fP,
+or whatever the \fB\-c\fP argument is set to.
+.sp
+The command will be run no more than once every \fIinterval\fP seconds.
+.sp
+jsmond should be started from your \fB\&.xinitrc\fP or whatever X startup
+script your windowmanager or desktop environment uses. By default, it
+will exit when the X server does.
+.sp
+If you can think of a use for jsmond outside of X, give it the \-x argument
+so it won\(aqt complain about not being able to connect to the X server. This
+will also prevent it from exiting when the X server does. When using
+\-x, be careful not to spawn multiple instances of jsmond (although they
+won\(aqt hurt anything, just waste resources).
+.SH OPTIONS
+.INDENT 0.0
+.TP
+.B \-\-help
+Print usage summary
+.TP
+.BI \-c \ <command>
+Run <command> when activity was detected during the
+last \fIinterval\fP\&.
+.TP
+.BI \-i \ <seconds>
+Interval to check for activity. Should be set lower
+than your xscreensaver timeout. Setting this too low
+will waste resources. Default: 60.
+.TP
+.B \-d
+Debug mode: print verbose messages and don\(aqt fork()
+into the background.
+.TP
+.B \-x
+Don\(aqt try to connect to X server.
+.UNINDENT
+.SH EXIT STATUS
+.sp
+Without the \-d option, the exit status is 0 (success) if jsmond
+successfully opened at least one joystick and forked into the background.
+.sp
+A non\-zero exit status means no joysticks were found, or else fork()
+failed. No daemon will be running in this case.
+.sp
+With the \-d option, jsmond never exits until it\(aqs killed.
+.SH BUGS
+.sp
+jsmond only searches for joysticks when it\(aqs started. If USB joysticks are
+plugged in while jsmond is running, it won\(aqt detect them. This includes
+unplugging the device and plugging it back in: jsmond won\(aqt complain,
+but it\(aqll never report activity on that device again.
+.sp
+jsmond isn\(aqt portable. It only works on Linux, at least for now.
+.\" EXAMPLES
+.
+.\" ========
+.
+.SH LICENSE
+.sp
+jsmond is released under the WTFPL: Do WTF you want with this.
+.SH AUTHORS
+.sp
+jsmond was written by B. Watson <\fI\%yalhcru@gmail.com\fP>.
+.SH SEE ALSO
+.sp
+sdl\-jstest(1), sdl2\-jstest(2)
+.\" Generated by docutils manpage writer.
+.
diff --git a/jsmond.c b/jsmond.c
new file mode 100644
index 0000000..f62da29
--- /dev/null
+++ b/jsmond.c
@@ -0,0 +1,287 @@
+/* need this, or else we don't get a prototype for strdup() from string.h */
+#define _POSIX_C_SOURCE 200809L
+
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <linux/joystick.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef HAVE_X11
+# include <X11/Xlib.h>
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* -c option: */
+const char *command = "xscreensaver-command -deactivate";
+/* -i option: */
+int interval = 60;
+/* cleared if user supplies one or more joysticks on command line: */
+int autodiscover = 1;
+/* -d option: */
+int debug = 0;
+
+char *joynames[MAX_STICKS + 1];
+int last_joyname = 0;
+
+const char *self;
+
+#ifdef HAVE_X11
+Display *xdisp;
+int use_x = 1;
+#endif
+
+void usage(void) {
+ printf("jsmond v" VERSION " by B. Watson, WTFPL\n");
+ printf("Usage: %s [-c cmd] [-i interval] "
+#ifdef HAVE_X11
+ "[-x] "
+#endif
+ "[joystick [...]]\n", self);
+ printf("Build options: "
+#ifndef HAVE_X11
+ "no "
+#endif
+ "X11 support, MAX_STICKS %d, JSDEVBASE " JSDEVBASE "\n",
+ MAX_STICKS);
+ printf("See man page for details\n");
+ exit(0);
+}
+
+void die(const char *msg) {
+ fprintf(stderr, "%s: %s\n", self, msg);
+ exit(1);
+}
+
+#ifdef HAVE_X11
+/* make some trivial Xlib call that will result in Xlib killing
+ this process if it fails. XNoOp looks like it was meant for
+ exactly this, but it keeps returning 1 even if the X server is killed. */
+void ping_x_server(void) {
+ (void)XPending(xdisp);
+ if(debug) fprintf(stderr, "X server is still alive\n");
+}
+#endif
+
+void main_loop(void) {
+ struct js_event e;
+ int joyfds[MAX_STICKS + 1];
+ int i, fdcount, active;
+
+ fprintf(stderr, "entering main_loop()\n");
+
+ while(1) {
+ active = 0;
+#ifdef HAVE_X11
+ if(use_x) ping_x_server();
+#endif
+
+ /* open all the files named in the list.
+ doing it this way is more work than opening each one once before
+ main_loop() and keeping it open, but this lets us easily track
+ when devices appear and disappear. */
+ fdcount = 0;
+ for(i = 0; i < last_joyname; i++) {
+ int synthev = 0;
+ joyfds[i] = open(joynames[i], O_RDONLY | O_NONBLOCK);
+ if(joyfds[i] > -1) {
+ fdcount++;
+ while( (read(joyfds[i], &e, sizeof(e)) > 0) && (e.type & JS_EVENT_INIT) )
+ synthev++;
+ if(debug)
+ fprintf(stderr, "opened %s, fd %d, skipped %d synthetic events\n",
+ joynames[i], joyfds[i], synthev);
+ }
+ }
+ if(debug) fprintf(stderr, "opened %d devices, sleeping %d sec\n", fdcount, interval);
+
+ /* let them gather events as we sleep */
+ sleep(interval);
+
+ /* now see if any of the fds got any events while we slept */
+ active = 0;
+ for(i = 0; i < last_joyname; i++) {
+ if(joyfds[i] < 0) continue;
+
+ if(debug) fprintf(stderr, "reading device %d, fd %d\n", i, joyfds[i]);
+
+ /* count events. any we get here *should* be real, but it doesn't
+ hurt to mask out synthetic ones again */
+ while(read(joyfds[i], &e, sizeof(e)) > 0) {
+ if(!(e.type & JS_EVENT_INIT)) active++;
+ }
+
+ /* EAGAIN just means there are no more events to read.
+ anything else is a problem, though not fatal */
+ if(errno != EAGAIN) {
+ if(debug) fprintf(stderr, "got error on device %d: %s\n", i, strerror(errno));
+ }
+ }
+
+ /* if we got any activity on any of the fds, do our thing */
+ if(active) {
+ int res;
+ if(debug) fprintf(stderr, "*** got activity! executing: %s\n", command);
+ res = system(command);
+ if(debug) fprintf(stderr, "exit status %d (%s)\n", res, (res ? "failed" : "OK"));
+ } else if(debug) fprintf(stderr, "no activity\n");
+
+ /* close 'em all */
+ for(i = 0; i < last_joyname; i++)
+ close(joyfds[i]);
+ }
+}
+
+/* make self point to our executable name without any directory */
+void set_exe_name(const char *argv0) {
+ const char *p;
+ self = argv0;
+ for(p = self; *p; p++)
+ if(p[0] == '/' && p[1]) self = p + 1;
+}
+
+/* add a joystick by pathname or number. note this only gets
+ called once, at startup: the list never gets modified after that. */
+void add_joystick_name(const char *name) {
+ if(last_joyname >= MAX_STICKS) {
+ fprintf(stderr,
+ "%s: too many device names, monitoring only the first %d\n",
+ self,
+ last_joyname);
+ return;
+ }
+
+ if(*name >= '0' && *name <= '9') {
+ char buf[PATH_MAX + 1];
+ sprintf(buf, JSDEVBASE "%d", atoi(name));
+ add_joystick_name(buf);
+ return;
+ }
+
+ if( !(joynames[last_joyname] = strdup(name)) )
+ die(strerror(errno));
+
+ if(debug)
+ fprintf(stderr, "added device name %d: %s\n", last_joyname, name);
+
+ last_joyname++;
+}
+
+void enumerate_names(void) {
+ int i;
+ char buf[PATH_MAX + 1];
+
+ for(i = 0; i < MAX_STICKS; i++) {
+ sprintf(buf, "%s%d", JSDEVBASE, i);
+ add_joystick_name(buf);
+ }
+}
+
+void parse_args(int argc, char **argv) {
+ if(argc > 1 && strcmp(argv[1], "--help") == 0) usage();
+ while(++argv, --argc) {
+ if(argv[0][0] == '-') {
+ char *nextarg = argv[1];
+ switch(argv[0][1]) {
+#ifdef HAVE_X11
+ case 'x': use_x = 0; break;
+#endif
+ case 'c':
+ if(nextarg) {
+ command = nextarg;
+ argv++, argc--;
+ } else {
+ die("-c requires a command argument");
+ }
+ break;
+ case 'i':
+ if(nextarg && (interval = atoi(nextarg)) > 0)
+ argv++, argc--;
+ else
+ die("-i requires a positive integer argument in seconds");
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ default:
+ die("unrecognized argument, try --help");
+ break;
+ }
+ } else {
+ add_joystick_name(*argv);
+ autodiscover = 0;
+ }
+ }
+}
+
+/* make ourselves a daemon, double-fork technique */
+void daemonize(void) {
+ pid_t pid;
+
+ if((pid = fork()) < 0) {
+ /* in parent, fork failed */
+ fprintf(stderr, "%s: %s\n", self, strerror(errno));
+ die("failed to daemonize");
+ } else if(pid) {
+ /* in parent, fork succeeded */
+ exit(0);
+ }
+
+ /* the only reason for the first fork() was because setsid() can't
+ be run in the parent process */
+ setsid();
+
+ /* go on, do it again */
+ if((pid = fork()) < 0) {
+ /* in parent, fork failed */
+ fprintf(stderr, "%s: %s\n", self, strerror(errno));
+ die("failed to daemonize");
+ } else if(pid) {
+ /* in parent, fork succeeded */
+ exit(0);
+ }
+
+ /* we no longer need stdin/out/err */
+ close(0);
+ close(1);
+ close(2);
+
+ /* do this so we don't keep a filesystem from being umounted because
+ it's still busy (as our cwd) */
+ chdir("/");
+}
+
+#ifdef HAVE_X11
+void connect_to_x(void) {
+ if(debug) fprintf(stderr, "connecting to X display...\n");
+ xdisp = XOpenDisplay(getenv("DISPLAY"));
+ if(!xdisp) die("can't connect to X server");
+ if(debug) fprintf(stderr, "connected to X display\n");
+}
+#endif
+
+int main(int argc, char **argv) {
+ set_exe_name(argv[0]);
+
+ parse_args(argc, argv);
+
+ if(autodiscover) enumerate_names();
+
+#ifdef HAVE_X11
+ if(use_x) connect_to_x();
+#endif
+
+ if(!debug) daemonize();
+
+ main_loop();
+
+ return 0;
+}
diff --git a/jsmond.rst b/jsmond.rst
new file mode 100644
index 0000000..575e76f
--- /dev/null
+++ b/jsmond.rst
@@ -0,0 +1,134 @@
+.. RST source for jsmond(1) man page. Convert with:
+.. rst2man.py jsmond.rst > jsmond.1
+
+.. |version| replace:: 0.1.0
+.. |date| date::
+
+======
+jsmond
+======
+
+----------------------------------------
+disable screensaver on joystick activity
+----------------------------------------
+
+:Manual section: 1
+:Manual group: Urchlay
+:Date: |date|
+:Version: |version|
+
+SYNOPSIS
+========
+
+jsmond [**-c command**] [**-i seconds**] [**-d**] [**-x**] [**joydev [joydev ...]**]
+
+DESCRIPTION
+===========
+
+jsmond lets you play games with your joysticks/gamepads without
+xscreensaver activating due to lack of keyboard/mouse input.
+
+Multiple joystick devices can be monitored. By default, jsmond
+monitors up to 16 devices, named /dev/input/js0 through js15.
+These devices don't have to actually exist: they can come and go
+as joysticks are plugged in and unplugged.
+
+Every *interval* seconds (60, or whatever **-i** is set to), jsmond
+checks to see if there's been any activity on any of the devices it's
+monitoring. If so, it runs the **xscreensaver-command -deactivate**
+(or whatever the **-c** argument is set to). The command will be run no
+more than once every *interval* seconds.
+
+It's recommended to let jsmond find the joysticks itself. However,
+you can pass one or more device names (or just numbers) if the default
+doesn't do the right thing for you. In this case, only these devices
+will be monitored (no search is done).
+
+OPTIONS
+=======
+
+--help Print usage summary
+
+-c <command> Run <command> when activity was detected during the
+ last *interval*.
+
+-i <seconds> Interval to check for activity. Should be set a minute or
+ so less than your xscreensaver timeout. Setting this too low
+ will waste resources. Default: 60.
+
+-d Debug mode: run in foreground and print verbose messages.
+
+-x Don't try to connect to X server (and don't exit until killed).
+
+NOTES
+=====
+
+A space is required between an option and its argument, as shown
+above. Use e.g. **-i 120**, not **-i120**.
+
+By default, jsmond searches for and monitors all the joysticks it can
+find, up to MAX_STICKS (normally 16; see the **--help** output to find
+the compiled-in default). You can override the search on the command
+line by providing one or more **joydev** arguments, in which case only
+those devices will be monitored.
+
+**joydev** arguments can be either a path to a device node (e.g.
+*/dev/input/js0* or similar), or a number, which will have the default
+device basename prepended to it. This is normally "/dev/input/js", but
+can be changed at compile time (see the **--help** output to find the
+compiled-in default).
+
+Note that it's *not* an error to give nonexistent joystick device names.
+jsmond will wait for devices to come into existence (e.g. as created
+by **udev**).
+
+jsmond should be started from your **.xinitrc** or whatever X startup
+script your windowmanager or desktop environment uses. By default, it
+will exit when the X server does.
+
+If you can think of a use for jsmond outside of X, give it the -x argument
+so it won't complain about not being able to connect to the X server. This
+will also prevent it from exiting when the X server does. When using
+-x, be careful not to spawn multiple instances of jsmond (although they
+won't hurt anything, just waste resources).
+
+There's no PID file. Just use "pkill jsmond".
+
+EXIT STATUS
+===========
+
+Without the -d option, the exit status is 0 (success) if jsmond
+successfully forked into the background.
+
+A non-zero exit status means an error in the command line arguments,
+or else fork() failed. No daemon will be running in this case.
+
+With the -d option, jsmond never exits until it's killed.
+
+BUGS
+====
+
+There's no way to distinguish between an invalid device name and a
+device name that doesn't happen to exist yet because its device hasn't
+been plugged in yet. Try to avoid typos, if you really have to use device
+names (better to autodetect).
+
+jsmond isn't portable. It only works on Linux, at least for now.
+
+.. EXAMPLES
+.. ========
+
+LICENSE
+=======
+
+jsmond is released under the WTFPL: Do WTF you want with this.
+
+AUTHORS
+=======
+
+jsmond was written by B. Watson <yalhcru@gmail.com>.
+
+SEE ALSO
+========
+
+sdl-jstest(1), sdl2-jstest(2)