--- /dev/null
+/* -*-c-*-
+ *
+ * Warp pointer to a specific location
+ *
+ * (c) 2022 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Edgeware X tools collection.
+ *
+ * X tools is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * X tools is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with X tools. If not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <X11/Xlib.h>
+
+#include <mLib/alloc.h>
+#include <mLib/macros.h>
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+
+/*----- Help and version information --------------------------------------*/
+
+static void version(void)
+ { pquis(stdout, "$ version " VERSION "\n"); }
+
+static void usage(FILE *fp)
+ { pquis(fp, "Usage: $ [-d DISPLAY] X Y\n"); }
+
+static void help(void)
+{
+ version(); putchar('\n');
+ usage(stdout);
+ fputs("\n\
+Warp mouse pointer to position X, Y on the screen.\n\
+\n\
+Command-line options:\n\
+\n\
+-h, --help Display this help.\n\
+-v, --version Display program's version number.\n\
+-u, --usage Display short usage summary.\n\
+\n\
+-d, --display=DISPLAY Connect to X DISPLAY.\n",
+ stdout);
+}
+
+/*----- Main program ------------------------------------------------------*/
+
+static int parse_int(const char *p)
+{
+ char *q;
+ int err;
+ long i;
+
+ err = errno; errno = 0;
+ if (!*p || ISSPACE(*p)) goto bad;
+ i = strtol(p, &q, 0);
+ if (err || *q || i < INT_MIN || i > INT_MAX) goto bad;
+ return ((int)i);
+
+bad:
+ die(1, "bad integer `%s'", p);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *display = 0;
+ Display *dpy = 0;
+ int sc;
+ Window w;
+ char **fixups; int nfixups, i;
+ int x, y, xmin, xmax, ymin, ymax, wd, ht;
+
+ unsigned f = 0;
+#define f_bogus 1u
+#define f_relative 2u
+#define f_neg 4u
+
+ ego(argv[0]);
+
+ /* An initial pass over the argument vector.
+ *
+ * Negative numbers look like options, but we don't want them treated that
+ * way. So we're going to hide them from the option parser for a bit.
+ */
+ fixups = xmalloc(argc*sizeof(char *));
+ for (i = 0, nfixups = 0; i < argc; i++)
+ if (argv[i][0] == '-' && ISDIGIT(argv[i][1]))
+ { fixups[nfixups++] = argv[i]; argv[i][0] = '?'; }
+
+ /* Parse arguments. */
+ for (;;) {
+ static struct option opt[] = {
+ { "help", 0, 0, 'h' },
+ { "usage", 0, 0, 'u' },
+ { "version", 0, 0, 'v' },
+ { "display", OPTF_ARGREQ, 0, 'd' },
+ { "relative", 0, 0, 'r' },
+ { 0, 0, 0, 0 }
+ };
+
+ i = mdwopt(argc, argv, "huvd:r", opt, 0, 0, 0); if (i < 0) break;
+ switch (i) {
+ case 'h': help(); exit(0);
+ case 'u': usage(stdout); exit(0);
+ case 'v': version(); exit(0);
+ case 'd': display = optarg; break;
+ case 'r': f |= f_relative; break;
+ default: f |= f_bogus; break;
+ }
+ }
+ if ((f&f_bogus) || argc - optind != 2) {
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Undo the fixups and fish out the numbers. */
+ for (i = 0; i < nfixups; i++) *fixups[i] = '-';
+ x = parse_int(argv[optind]);
+ y = parse_int(argv[optind + 1]);
+
+ /* Open the display and the screen size. */
+ if ((dpy = XOpenDisplay(display)) == 0)
+ die(EXIT_FAILURE, "couldn't open display");
+ sc = DefaultScreen(dpy);
+ wd = DisplayWidth(dpy, sc);
+ ht = DisplayHeight(dpy, sc);
+
+ /* Interpret the coordinates and check that they're in range. */
+ if (f&f_relative) {
+ w = None;
+ xmin = -wd + 1; ymin = -ht + 1; xmax = wd - 1; ymax = ht - 1;
+ } else {
+ if (x < 0) x += wd;
+ if (y < 0) y += ht;
+ w = RootWindow(dpy, sc);
+ xmin = ymin = 0; xmax = wd - 1; ymax = ht - 1;
+ }
+ if (x < xmin || x > xmax || y < ymin || y > ymax)
+ die(1, "coordinates out of range");
+
+ /* Do the work. */
+ XWarpPointer(dpy, None, w, 0, 0, 0, 0, x, y);
+
+ /* Done. */
+ XCloseDisplay(dpy);
+ return (0);
+
+#undef f_bogus
+#undef f_relative
+}
+
+/*----- That's all, folks -------------------------------------------------*/