xscsize.c, etc.: Report geometry of individual monitors.
[xtoys] / xscsize.c
index 46d58f7..99d729f 100644 (file)
--- a/xscsize.c
+++ b/xscsize.c
 #include <string.h>
 
 #include <X11/Xlib.h>
+#ifdef HAVE_XRANDR
+#  include <X11/extensions/Xrandr.h>
+#endif
 
+#include <mLib/alloc.h>
+#include <mLib/dstr.h>
 #include <mLib/mdwopt.h>
 #include <mLib/quis.h>
 
+/*----- Data structures ---------------------------------------------------*/
+
+struct screen {
+  int x, y;
+  unsigned wd, ht;
+};
+
 /*----- Static variables --------------------------------------------------*/
 
 static unsigned int flags = 0;
@@ -74,8 +86,10 @@ Options:\n\
        fp);
 }
 
-static void print_var(const char *name, unsigned long value)
+static void print_var(const char *name, int index, unsigned long value)
 {
+  dstr d = DSTR_INIT;
+
   if (index >= 0) {
     dstr_putf(&d, "XSCR%d_%s", index, name);
     name = d.buf;
@@ -91,6 +105,14 @@ static void print_var(const char *name, unsigned long value)
   dstr_destroy(&d);
 }
 
+static int compare_screen(const void *a, const void *b)
+{
+  const struct screen *s = a, *t = b;
+  if (s->y != t->y) return (s->y < t->y ? -1 : +1);
+  else if (s->x != t->x) return (s->x < t->x ? -1 : +1);
+  else return (0);
+}
+
 int main(int argc, char *argv[])
 {
   Display *dpy;
@@ -99,8 +121,18 @@ int main(int argc, char *argv[])
   unsigned f = 0;
   unsigned long wd, ht;
   int sc;
+#ifdef HAVE_XRANDR
+  Window root;
+  int rrev, rrerr, rrmaj, rrmin;
+  XRRScreenResources *res;
+  XRRCrtcInfo *crtc;
+  struct screen *scr;
+  size_t nscr, j;
+  int i;
+#endif
 
 #define f_bogus 1u
+#define f_multi 2u
 
   /* --- Parse command line options --- */
 
@@ -114,11 +146,12 @@ int main(int argc, char *argv[])
       { "display",     OPTF_ARGREQ,    0,      'd' },
       { "bourne-shell",        0,              0,      'b' },
       { "c-shell",     0,              0,      'c' },
+      { "multiscreen", 0,              0,      'm' },
       { "export",      0,              0,      'x' },
       {        0,              0,              0,      0 }
     };
 
-    int i = getopt_long(argc, argv, "huv" "d:bcx", opt, 0);
+    int i = getopt_long(argc, argv, "huv" "d:bcmx", opt, 0);
     if (i < 0) break;
     switch (i) {
       case 'h': help(stdout); exit(0); break;
@@ -127,6 +160,7 @@ int main(int argc, char *argv[])
       case 'd': display = optarg; break;
       case 'b': flags |= F_SH; break;
       case 'c': flags |= F_CSH; break;
+      case 'm': f |= f_multi; break;
       case 'x': flags |= F_EXPORT; break;
       default: f |= f_bogus; break;
     }
@@ -153,22 +187,84 @@ int main(int argc, char *argv[])
     exit(EXIT_FAILURE);
   }
 
-  /* --- Get the important information --- */
+  /* --- Open the display --- */
 
   dpy = XOpenDisplay(display);
   if (!dpy) {
     fprintf(stderr, "xscsize: couldn't open display\n");
     exit(EXIT_FAILURE);
   }
+
+  /* --- Fetch the root window size --- *
+   *
+   * We might need this whatever happens, so go with the flow.
+   */
+
   sc = DefaultScreen(dpy);
   wd = DisplayWidth(dpy, sc);
   ht = DisplayHeight(dpy, sc);
-  XCloseDisplay(dpy);
 
-  /* --- Do the output thing --- */
+  /* --- Calculate and produce the necessary output --- *
+   *
+   * If we're meant to report on individual screens then try to collect
+   * information about them using the RANDR extension.  If that doesn't
+   * exist, or the version is too ancient, or its otherwise not going to
+   * work, then pretend there's just one screen that's the size of the root
+   * window.
+   */
+
+  if (f & f_multi) {
+#ifdef HAVE_XRANDR
+    if (XRRQueryExtension(dpy, &rrev, &rrerr) &&
+       XRRQueryVersion(dpy, &rrmaj, &rrmin) &&
+       (rrmaj > 1 || (rrmaj == 1 && rrmin >= 2))) {
+      root = RootWindow(dpy, sc);
+      res = XRRGetScreenResources(dpy, root);
+      scr = xmalloc(res->ncrtc*sizeof(*scr)); j = 0;
+      for (i = 0; i < res->ncrtc; i++) {
+       crtc = XRRGetCrtcInfo(dpy, res, res->crtcs[i]);
+       if (crtc->mode) {
+         scr[j].x = crtc->x; scr[j].wd = crtc->width;
+         scr[j].y = crtc->y; scr[j].ht = crtc->height;
+         j++;
+       }
+       XRRFreeCrtcInfo(crtc);
+      }
+      nscr = j;
+      XRRFreeScreenResources(res);
+    } else
+#endif
+    {
+      /* --- The RANDR extension isn't available --- */
+
+      nscr = 1;
+      scr = xmalloc(sizeof(*scr));
+      scr->x = 0; scr->wd = wd;
+      scr->y = 0; scr->ht = ht;
+    }
+
+    /* --- Sort and report the screens --- *
+     *
+     * The chances are good that the screens reported by RANDR aren't in any
+     * especially useful order.  Sort them into (my) reading order.
+     */
 
-  print_var("XWIDTH", wd);
-  print_var("XHEIGHT", ht);
+    qsort(scr, nscr, sizeof(*scr), compare_screen);
+    print_var("XNSCR", -1, nscr);
+    for (j = 0; j < nscr; j++) {
+      print_var("X", j, scr[j].x);
+      print_var("Y", j, scr[j].y);
+      print_var("WIDTH", j, scr[j].wd);
+      print_var("HEIGHT", j, scr[j].ht);
+    }
+  } else {
+    print_var("XWIDTH", -1, wd);
+    print_var("XHEIGHT", -1, ht);
+  }
+
+  /* --- We're done with the display now --- */
+
+  XCloseDisplay(dpy);
 
   /* --- Done --- */