Adds an optional "KeepShape" paramater to the MapToOutput command.
If provided, the AspectRatio property is updated to match the aspect
ratio of the new output. If not provided, the AspectRatio property is
reset to allow full use of the tablet's area.

Signed-off-by: Jason Gerecke <[email protected]>
---
 Changes from v2:
  * Corresponds to patch v2 9/9

  * Boolean "match" argument replaced with the "KeepShape" keyword for
    more semantic meaning.

  * Helper functions (finally) modified to return booleans

  * '_set_aspect' renamed to '_update_aspect'

  * Helper functions no longer take an additional boolean argument;
    '_update_aspect' should be called manually.

 man/xsetwacom.man |   19 ++++---
 tools/xsetwacom.c |  146 +++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 126 insertions(+), 39 deletions(-)

diff --git a/man/xsetwacom.man b/man/xsetwacom.man
index edc97c4..d87f24d 100644
--- a/man/xsetwacom.man
+++ b/man/xsetwacom.man
@@ -119,15 +119,18 @@ the device will ignore events from other tools. A serial 
of 0 means the
 device is unbound and will react to any tool of the matching type.
 Default: 0
 .TP
-\fBMapToOutput\fR [output]
+\fBMapToOutput\fR [output] [match_aspect]
 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 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 proper
-XRandR support is included.
+desktop if no output is provided. If the match_aspect option is 'true', the
+the active area of the tablet will be restricted to match the aspect ratio of
+the output; if 'false' or unspecified, the entire tablet area will be usable.
+
+Output names may either be the name of a head available through the XRandR
+extension, or an X11 geometry string of the form WIDTHxHEIGHT+X+Y. The
+relative output "next" will cycle the mapping through all available outputs,
+including the desktop as a whole. Users of the NVIDIA binary driver should
+use the output names "HEAD-0" and "HEAD-1" until proper XRandR support is
+included.
 
 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 5377c97..d3a237f 100644
--- a/tools/xsetwacom.c
+++ b/tools/xsetwacom.c
@@ -410,7 +410,7 @@ static param_t parameters[] =
                .name = "MapToOutput",
                .desc = "Map the device to the given output. ",
                .set_func = set_output,
-               .arg_count = 1,
+               .arg_count = 2,
                .prop_flags = PROP_FLAG_WRITEONLY | PROP_FLAG_OUTPUT,
        },
        {
@@ -2036,7 +2036,61 @@ Bool get_mapped_area(Display *dpy, XDevice *dev, int 
*width, int *height, int *x
        return matrix_is_valid;
 }
 
-static void _set_matrix_prop(Display *dpy, XDevice *dev, const float 
fmatrix[9])
+/**
+ * Updates device's aspect ratio property so that it matches that of
+ * the currently defied output area (if 'do_match' is true), or to
+ * allow use of the entire area (if 'do_match' is false). If this
+ * function succeeds in modifying the aspect ratio, it returns 'true'.
+ */
+static Bool _update_aspect(Display *dpy, XDevice *dev, Bool do_match)
+{
+       Atom aspect_prop = XInternAtom(dpy, "Wacom Aspect Ratio", True);
+       Atom type;
+       int format;
+       unsigned long nitems, bytes_after;
+       char *data = NULL;
+       int w, h, x_org, y_org;
+       Bool success = False;
+
+       XGetDeviceProperty(dpy, dev, aspect_prop, 0, 2, False,
+                          AnyPropertyType, &type, &format, &nitems,
+                          &bytes_after, (unsigned char**)&data);
+
+       if (format != 32 || nitems != 2)
+       {
+               fprintf(stderr, "Property for '%s' has unexpected type - this 
is a bug.\n",
+                       "Wacom Aspect Ratio");
+               return success;
+       }
+
+       if (!get_mapped_area(dpy, dev, &w, &h, &x_org, &y_org))
+               return False;
+
+       if (!do_match) {
+               w = 0;
+               h = 0;
+       }
+
+       TRACE("Setting aspect ratio of device to %d:%d.\n", w, h);
+
+       ((int*)data)[0] = w;
+       ((int*)data)[1] = h;
+       XChangeDeviceProperty(dpy, dev, aspect_prop, type, format,
+                             PropModeReplace, (unsigned char*)data, nitems);
+
+       XFlush(dpy);
+       free(data);
+
+       return True;
+}
+
+/**
+ * Modifies the server's transformation matrix property for the given
+ * device. It takes as input a 9-element array of floats interpreted
+ * as the row-major 3x3 matrix to be set. If this function succeeds
+ * in modifying the transformation matrix, it returns 'true'.
+ */
+static Bool _set_matrix_prop(Display *dpy, XDevice *dev, const float 
fmatrix[9])
 {
        Atom matrix_prop = XInternAtom(dpy, "Coordinate Transformation Matrix", 
True);
        Atom type;
@@ -2049,7 +2103,7 @@ static void _set_matrix_prop(Display *dpy, XDevice *dev, 
const float fmatrix[9])
        if (!matrix_prop)
        {
                fprintf(stderr, "Server does not support transformation\n");
-               return;
+               return False;
        }
 
        /* XI1 expects 32 bit properties (including float) as long,
@@ -2065,21 +2119,24 @@ static void _set_matrix_prop(Display *dpy, XDevice 
*dev, const float fmatrix[9])
        {
                fprintf(stderr, "Property for '%s' has unexpected type - this 
is a bug.\n",
                        "Coordinate Transformation Matrix");
-               return;
+               return False;
        }
 
        XChangeDeviceProperty(dpy, dev, matrix_prop, type, format,
                              PropModeReplace, (unsigned char*)matrix, 9);
        XFree(data);
        XFlush(dpy);
+
+       return True;
 }
 
 /**
  * Adjust the transformation matrix based on a user-defined area.
  * This function will attempt to map the given pointer to an arbitrary
- * rectangular portion of the desktop.
+ * rectangular portion of the desktop. If this function succeeds in
+ * modifying the transformation matrix, it returns 'true'.
  */
-static void set_output_area(Display *dpy, XDevice *dev,
+static Bool set_output_area(Display *dpy, XDevice *dev,
                        int offset_x, int offset_y,
                        int output_width, int output_height)
 {
@@ -2110,15 +2167,17 @@ static void set_output_area(Display *dpy, XDevice *dev,
        TRACE(" [ %f %f %f ]\n", matrix[3], matrix[4], matrix[5]);
        TRACE(" [ %f %f %f ]\n", matrix[6], matrix[7], matrix[8]);
 
-       _set_matrix_prop(dpy, dev, matrix);
+       return _set_matrix_prop(dpy, dev, matrix);
 }
 
+
 /**
  * Adjust the transformation matrix based on RandR settings. This function
  * will attempt to map the given device to the output with the given RandR
- * output name.
+ * output name. If this function succeeds in modifying the transformation
+ * matrix, it returns 'true'.
  */
-static void set_output_xrandr(Display *dpy, XDevice *dev, char *output_name)
+static Bool set_output_xrandr(Display *dpy, XDevice *dev, char *output_name)
 {
        int i, found = 0;
        int x, y, width, height;
@@ -2158,33 +2217,38 @@ static void set_output_xrandr(Display *dpy, XDevice 
*dev, char *output_name)
        if (found)
        {
                TRACE("Setting CRTC %s\n", output_name);
-               set_output_area(dpy, dev, x, y, width, height);
+               return set_output_area(dpy, dev, x, y, width, height);
        } else
+       {
                printf("Unable to find output '%s'. "
                        "Output may not be connected.\n", output_name);
 
+               return False;
+       }
 }
 
 /**
  * Adjust the transformation matrix based on the Xinerama settings. This
  * function will attempt to map the given device to the specified Xinerama
- * head number.
+ * head number. If this function succeeds in modifying the transformation
+ * matrix, it returns 'true'.
  *
  * For TwinView This would better be done with libXNVCtrl but until they
  * learn to package it properly, we need to rely on Xinerama. Besides,
  * libXNVCtrl isn't available on RHEL, so we'd have to do it through
  * Xinerama there anyway.
  */
-static void set_output_xinerama(Display *dpy, XDevice *dev, int head)
+static Bool set_output_xinerama(Display *dpy, XDevice *dev, int head)
 {
        int event, error;
        XineramaScreenInfo *screens;
        int nscreens;
+       Bool success = False;
 
        if (!XineramaQueryExtension(dpy, &event, &error))
        {
                fprintf(stderr, "Unable to set screen mapping. Xinerama 
extension not found\n");
-               return;
+               return success;
        }
 
        screens = XineramaQueryScreens(dpy, &nscreens);
@@ -2202,12 +2266,13 @@ static void set_output_xinerama(Display *dpy, XDevice 
*dev, int head)
 
        TRACE("Setting xinerama head %d\n", head);
 
-       set_output_area(dpy, dev,
+       success = set_output_area(dpy, dev,
                    screens[head].x_org, screens[head].y_org,
                    screens[head].width, screens[head].height);
 
 out:
        XFree(screens);
+       return success;
 }
 
 /**
@@ -2215,12 +2280,12 @@ out:
  * This function will attempt to map the given device to the entire
  * desktop.
  */
-static void set_output_desktop(Display *dpy, XDevice *dev)
+static Bool 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);
+       return set_output_area(dpy, dev, 0, 0, display_width, display_height);
 }
 
 /**
@@ -2230,7 +2295,7 @@ static void set_output_desktop(Display *dpy, XDevice *dev)
  * head, it maps to the first head. If mapped to the last Xinerama
  * head, it maps to the entire desktop.
  */
-static void set_output_next(Display *dpy, XDevice *dev)
+static Bool set_output_next(Display *dpy, XDevice *dev)
 {
        XineramaScreenInfo *screens;
        int event, error, nscreens, head;
@@ -2238,12 +2303,12 @@ static void set_output_next(Display *dpy, XDevice *dev)
        Bool success = False;
 
        if (!get_mapped_area(dpy, dev, &width, &height, &x_org, &y_org))
-               return;
+               return success;
 
        if (!XineramaQueryExtension(dpy, &event, &error))
        {
                fprintf(stderr, "Unable to get screen mapping. Xinerama 
extension not found\n");
-               return;
+               return success;
        }
 
        screens = XineramaQueryScreens(dpy, &nscreens);
@@ -2260,30 +2325,43 @@ static void set_output_next(Display *dpy, XDevice *dev)
                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);
+                               success = set_output_xinerama(dpy, dev, head+1);
                        else
-                               set_output_desktop(dpy, dev);
+                               success = set_output_desktop(dpy, dev);
+
+                       if (!success)
+                               goto out;
                }
        }
 
        if (!success)
-               set_output_xinerama(dpy, dev, 0);
+               success = set_output_xinerama(dpy, dev, 0);
 
 out:
        XFree(screens);
+       return success;
 }
 
 static void set_output(Display *dpy, XDevice *dev, param_t *param, int argc, 
char **argv)
 {
        int tmp_int[2];
        unsigned int tmp_uint[2];
+       Bool success = False;
 
-       if (argc != param->arg_count)
+       if (argc == 0)
        {
-               fprintf(stderr, "'%s' requires exactly %d value(s).\n", 
param->name,
+               fprintf(stderr, "'%s' requires at least one argument.\n", 
param->name);
+               return;
+       }
+       else if (argc == param->arg_count && strcasecmp(argv[1], "KeepShape") 
!= 0)
+       {
+               fprintf(stderr, "'%s' could not understand the provided 
argument '%s'.\n",
+                       param->name, argv[1]);
+       }
+       else if (argc > param->arg_count)
+       {
+               fprintf(stderr, "'%s' accepts no more than %d value(s).\n", 
param->name,
                        param->arg_count);
                return;
        }
@@ -2291,28 +2369,34 @@ static void set_output(Display *dpy, XDevice *dev, 
param_t *param, int argc, cha
        if (MaskIsSet(XParseGeometry(argv[0], &tmp_int[0], &tmp_int[1], 
&tmp_uint[0], &tmp_uint[1]),
            XValue|YValue|WidthValue|HeightValue))
        {
-               set_output_area(dpy, dev, tmp_int[0], tmp_int[1], tmp_uint[0], 
tmp_uint[1]);
+               success = set_output_area(dpy, dev, tmp_int[0], tmp_int[1], 
tmp_uint[0], tmp_uint[1]);
        }
        else if (strcasecmp(argv[0], "next") == 0)
        {
-               set_output_next(dpy, dev);
+               success = set_output_next(dpy, dev);
        }
        else if (strcasecmp(argv[0], "desktop") == 0)
        {
-               set_output_desktop(dpy, dev);
+               success = set_output_desktop(dpy, dev);
        }
        else if (!need_xinerama(dpy))
        {
-               set_output_xrandr(dpy, dev, argv[0]);
+               success = set_output_xrandr(dpy, dev, argv[0]);
        }
        else if  (convert_value_from_user(param, argv[0], &tmp_int[0]))
        {
-               set_output_xinerama(dpy, dev, tmp_int[0]);
+               success = set_output_xinerama(dpy, dev, tmp_int[0]);
        }
        else
        {
                fprintf(stderr, "Unable to find an output '%s'.\n", argv[0]);
        }
+
+       if (!success)
+               return;
+
+       Bool do_match = argc > 1 && strcasecmp(argv[1], "KeepShape") == 0;
+       _update_aspect(dpy, dev, do_match);
 }
 
 
-- 
1.7.6


------------------------------------------------------------------------------
Using storage to extend the benefits of virtualization and iSCSI
Virtualization increases hardware utilization and delivers a new level of
agility. Learn what those decisions are and how to modernize your storage 
and backup environments for virtualization.
http://www.accelacomm.com/jaw/sfnl/114/51434361/
_______________________________________________
Linuxwacom-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel

Reply via email to