summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Gerecke <killertofu@gmail.com>2011-09-09 15:43:29 -0700
committerJason Gerecke <killertofu@gmail.com>2011-09-27 15:42:46 -0700
commit6da898eba50a8499d770214684bd3d2d46033037 (patch)
tree16f2d070b5f26e4c27f4e80f1bc6bc34e5460d84
parentf09def951b7bd8ca57894e15ea3a47cda96c6a00 (diff)
Add 'set_output_next' helper for MapToOutput
Adds a helper function for MapToOutput which determines which output the device is currently mapped to, and moves to the next available output. Repeatedly calling this function will result in the device being mapped to every available output (including the desktop as a whole) in turn.
-rw-r--r--man/xsetwacom.man8
-rw-r--r--tools/xsetwacom.c151
2 files changed, 156 insertions, 3 deletions
diff --git a/man/xsetwacom.man b/man/xsetwacom.man
index 9375e5b..10832ad 100644
--- a/man/xsetwacom.man
+++ b/man/xsetwacom.man
@@ -123,9 +123,11 @@ Default: 0
Map the tablet's input area to the given output (e.g. "VGA1"), or the entire
desktop if no output is provided. Output names may either be the name of
a head available through the XRandR extension, or an X11 geometry string of
-the from WIDTHxHEIGHT+X+Y. Users of the NVIDIA binary driver should use the
-output names "HEAD-0" and "HEAD-1" until the driver supports XRandr 1.2 or
-later.
+the form WIDTHxHEIGHT+X+Y. To switch to the next available output, the "next"
+keyword is also supported. This will cycle between the individual monitors
+connected to the system, and then the entire desktop. Users of the NVIDIA
+binary driver should use the output names "HEAD-0" and "HEAD-1" until the
+driver supports XRandR 1.2 or later.
The output mapping configuration is a onetime setting and does not track output
reconfigurations; the command needs to be re-run whenever the output
diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c
index 97bdeb3..0852f81 100644
--- a/tools/xsetwacom.c
+++ b/tools/xsetwacom.c
@@ -1958,6 +1958,83 @@ static Bool need_xinerama(Display *dpy)
return False;
}
+/**
+ * Uses the area of the desktop and the server's transformation
+ * matrix to calculate the dimensions and location of the area
+ * the given device is mapped to. If the matrix describes a
+ * non-rectangular transform (e.g. rotation or shear), this
+ * function returns False.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param width[out] Width of the mapped area
+ * @param height[out] Height of the mapped area
+ * @param x_org[out] Offset from the desktop origin to the mapped area's left edge
+ * @param y_org[out] Offset from the desktop origin to the mapped area's top edge
+ * @return True if the function could determine the mapped area
+ */
+Bool get_mapped_area(Display *dpy, XDevice *dev, int *width, int *height, int *x_org, int *y_org)
+{
+ Atom matrix_prop = XInternAtom(dpy, "Coordinate Transformation Matrix", True);
+ Atom type;
+ int format;
+ unsigned long nitems, bytes_after;
+ float *data;
+ Bool matrix_is_valid = True;
+ int i;
+
+ int display_width = DisplayWidth(dpy, DefaultScreen(dpy));
+ int display_height = DisplayHeight(dpy, DefaultScreen(dpy));
+ TRACE("Desktop width: %d, height: %d\n", display_width, display_height);
+
+ if (!matrix_prop)
+ {
+ fprintf(stderr, "Server does not support transformation\n");
+ return False;
+ }
+
+ XGetDeviceProperty(dpy, dev, matrix_prop, 0, 9, False,
+ AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, (unsigned char**)&data);
+
+ if (format != 32 || type != XInternAtom(dpy, "FLOAT", True))
+ {
+ fprintf(stderr,"Property for '%s' has unexpected type - this is a bug.\n",
+ "Coordinate Transformation Matrix");
+ XFree(data);
+ return False;
+ }
+
+ TRACE("Current transformation matrix:\n");
+ TRACE(" [ %f %f %f ]\n", data[0], data[1], data[2]);
+ TRACE(" [ %f %f %f ]\n", data[3], data[4], data[5]);
+ TRACE(" [ %f %f %f ]\n", data[6], data[7], data[8]);
+
+ for (i = 0; i < nitems && matrix_is_valid; i++)
+ {
+ switch (i) {
+ case 0: *width = rint(display_width * data[i]); break;
+ case 2: *x_org = rint(display_width * data[i]); break;
+ case 4: *height = rint(display_height * data[i]); break;
+ case 5: *y_org = rint(display_height * data[i]); break;
+ case 8:
+ if (data[i] != 1)
+ matrix_is_valid = False;
+ break;
+ default:
+ if (data[i] != 0)
+ matrix_is_valid = False;
+ break;
+ }
+ }
+ XFree(data);
+
+ if (!matrix_is_valid)
+ fprintf(stderr, "Non-rectangular transformation matrix detected.\n");
+
+ return matrix_is_valid;
+}
+
static void _set_matrix_prop(Display *dpy, XDevice *dev, const float fmatrix[9])
{
Atom matrix_prop = XInternAtom(dpy, "Coordinate Transformation Matrix", True);
@@ -2147,6 +2224,78 @@ out:
XFree(screens);
}
+/**
+ * Adjust the transformation matrix based on the desktop size.
+ * This function will attempt to map the given device to the entire
+ * desktop.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ */
+static void set_output_desktop(Display *dpy, XDevice *dev)
+{
+ int display_width = DisplayWidth(dpy, DefaultScreen(dpy));
+ int display_height = DisplayHeight(dpy, DefaultScreen(dpy));
+
+ set_output_area(dpy, dev, 0, 0, display_width, display_height);
+}
+
+/**
+ * Adjust the transformation matrix based on its current value. This
+ * function will attempt to map the given device to the next output
+ * exposed in the list of Xinerama heads. If not mapped to a Xinerama
+ * head, it maps to the first head. If mapped to the last Xinerama
+ * head, it maps to the entire desktop.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ */
+static void set_output_next(Display *dpy, XDevice *dev)
+{
+ XineramaScreenInfo *screens;
+ int event, error, nscreens, head;
+ int width, height, x_org, y_org;
+ Bool success = False;
+
+ if (!get_mapped_area(dpy, dev, &width, &height, &x_org, &y_org))
+ return;
+
+ if (!XineramaQueryExtension(dpy, &event, &error))
+ {
+ fprintf(stderr, "Unable to get screen mapping. Xinerama extension not found\n");
+ return;
+ }
+
+ screens = XineramaQueryScreens(dpy, &nscreens);
+
+ if (nscreens == 0)
+ {
+ fprintf(stderr, "Xinerama failed to query screens.\n");
+ goto out;
+ }
+
+ TRACE("Remapping to next available output.\n");
+ for (head = 0; head < nscreens && !success; head++)
+ {
+ if (screens[head].width == width && screens[head].height == height &&
+ screens[head].x_org == x_org && screens[head].y_org == y_org)
+ {
+ success = True;
+
+ if (head + 1 < nscreens)
+ set_output_xinerama(dpy, dev, head+1);
+ else
+ set_output_desktop(dpy, dev);
+ }
+ }
+
+ if (!success)
+ set_output_xinerama(dpy, dev, 0);
+
+out:
+ XFree(screens);
+}
+
static void set_output(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv)
{
int head_no;
@@ -2171,6 +2320,8 @@ static void set_output(Display *dpy, XDevice *dev, param_t *param, int argc, cha
if (MaskIsSet(flags, XValue|YValue|WidthValue|HeightValue))
set_output_area(dpy, dev, x, y, width, height);
+ else if (strcasecmp(argv[0], "next") == 0)
+ set_output_next(dpy, dev);
else if (!need_xinerama(dpy))
set_output_xrandr(dpy, dev, argv[0]);
else if (convert_value_from_user(param, argv[0], &head_no))