// XTest-based mouse simulator for the tab-drag POC.
//
// Usage:
//   xmouse press   <x> <y>           -- move to (x,y) and press button 1
//   xmouse move    <x> <y>           -- move to (x,y)
//   xmouse smove   <x1> <y1> <x2> <y2> <steps> <delay_ms>
//                                     -- smooth move from (x1,y1) to (x2,y2)
//   xmouse release                   -- release button 1
//   xmouse click   <x> <y>           -- move + press + release
//
// Compile: gcc -o xmouse xmouse.c -lX11 -lXtst

#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
  Display *d = XOpenDisplay(NULL);
  if (!d) { fprintf(stderr, "xmouse: cannot open display\n"); return 1; }
  int screen = DefaultScreen(d);

  if (argc < 2) { fprintf(stderr, "xmouse: missing command\n"); return 1; }

  if (!strcmp(argv[1], "press") && argc == 4) {
    int x = atoi(argv[2]), y = atoi(argv[3]);
    XTestFakeMotionEvent(d, screen, x, y, CurrentTime);
    XSync(d, False);
    usleep(50 * 1000);
    XTestFakeButtonEvent(d, 1, True, CurrentTime);
    XSync(d, False);
  } else if (!strcmp(argv[1], "move") && argc == 4) {
    int x = atoi(argv[2]), y = atoi(argv[3]);
    XTestFakeMotionEvent(d, screen, x, y, CurrentTime);
    XSync(d, False);
  } else if (!strcmp(argv[1], "smove") && argc == 8) {
    int x1 = atoi(argv[2]), y1 = atoi(argv[3]);
    int x2 = atoi(argv[4]), y2 = atoi(argv[5]);
    int steps = atoi(argv[6]);
    int delay_ms = atoi(argv[7]);
    for (int i = 0; i <= steps; i++) {
      int x = x1 + (x2 - x1) * i / steps;
      int y = y1 + (y2 - y1) * i / steps;
      XTestFakeMotionEvent(d, screen, x, y, CurrentTime);
      XSync(d, False);
      usleep(delay_ms * 1000);
    }
  } else if (!strcmp(argv[1], "release")) {
    XTestFakeButtonEvent(d, 1, False, CurrentTime);
    XSync(d, False);
  } else if (!strcmp(argv[1], "click") && argc == 4) {
    int x = atoi(argv[2]), y = atoi(argv[3]);
    XTestFakeMotionEvent(d, screen, x, y, CurrentTime);
    XSync(d, False);
    usleep(50 * 1000);
    XTestFakeButtonEvent(d, 1, True, CurrentTime);
    XSync(d, False);
    usleep(50 * 1000);
    XTestFakeButtonEvent(d, 1, False, CurrentTime);
    XSync(d, False);
  } else {
    fprintf(stderr, "xmouse: bad args\n");
    return 1;
  }

  XCloseDisplay(d);
  return 0;
}
