[PATCH xinput] Add support for device-to-screen mapping

Peter Hutterer peter.hutterer at who-t.net
Sun Jul 17 16:56:55 PDT 2011


On Sat, Jul 16, 2011 at 07:00:23PM +0200, Simon Thum wrote:
> I like this patch, but I'd add a check for absolute devices. It's
> neither currently possible nor feasible with relative devices.

true, but the server's definition of relative devices is different to the
driver's and client's definition of relative devices. a relative device can
still send absolute coordinates to the server (and thus not trigger the
matrix) and vice versa. So it's not that trivial for a client to tell which
device really is relative/absolute.

I'll just add a blurb to the man page that this parameter has no effect on
relative devices and hope that it's not too confusing to a user if it
doesn't work for one specific device :)

> In any case, it's
> 
> Reviewed-by: Simon Thum <simon.thum at gmx.de>

thanks.

Cheers,
  Peter
 
> On 07/06/2011 12:54 AM, Peter Hutterer wrote:
> > xinput map-to-crtc "device name" "VGA0" will map to the CRTC "VGA0" by
> > manipulating the transformation matrix accordingly. And because the NVIDIA
> > binary driver still exists, Xinerama is supported to. Maybe in another few
> > years, they'll catch up.
> > 
> > Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> > ---
> >  configure.ac    |    2 +-
> >  man/xinput.man  |   10 ++-
> >  src/Makefile.am |    2 +-
> >  src/transform.c |  263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  src/xinput.c    |    4 +
> >  src/xinput.h    |    1 +
> >  6 files changed, 279 insertions(+), 3 deletions(-)
> >  create mode 100644 src/transform.c
> > 
> > diff --git a/configure.ac b/configure.ac
> > index 9e18376..37f998f 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -38,7 +38,7 @@ XORG_MACROS_VERSION(1.8)
> >  XORG_DEFAULT_OPTIONS
> >  
> >  # Obtain compiler/linker options for dependencies
> > -PKG_CHECK_MODULES(XINPUT, x11 xext [xi >= 1.2] [inputproto >= 1.5])
> > +PKG_CHECK_MODULES(XINPUT, x11 xext [xi >= 1.2] [inputproto >= 1.5] xrandr xinerama)
> >  
> >  # XI2 support
> >  PKG_CHECK_MODULES(XI2, [xi >= 1.2.99.2] [inputproto >= 1.9.99.15],
> > diff --git a/man/xinput.man b/man/xinput.man
> > index 8a348d1..56cc654 100644
> > --- a/man/xinput.man
> > +++ b/man/xinput.man
> > @@ -139,6 +139,14 @@ Remove \fIslave\fP from its current master device.
> >  Set the ClientPointer for the client owning \fIwindow\fP to \fImaster\fP.
> >  \fImaster\fP must specify a master pointer.
> >  .PP
> > +.TP 8
> > +.B --map-to-crtc \fIdevice\fP \fIcrtc\fP
> > +Restricts the movements of the absolute \fIdevice\fP to the RandR
> > +\fIcrtc\fP. The CRTC name must match a currently connected CRTC (see
> > +\fIxrandr(__appmansuffix__)\fP). If the NVIDIA binary driver is
> > +detected or RandR 1.2 or later is not available, a Xinerama output may be
> > +specified as "HEAD-N", with N being the Xinerama screen number.
> > +.PP
> >  \fIdevice\fP can be the device name as a string or the XID of the
> >  device.
> >  .PP
> > @@ -151,7 +159,7 @@ device.
> >  \fIproperty\fP can be the property as a string or the Atom value.
> >  .PP
> >  .SH "SEE ALSO"
> > -X(__miscmansuffix__), xset(__appmansuffix__)
> > +X(__miscmansuffix__), xset(__appmansuffix__), xrandr(__appmansuffix__)
> >  .SH COPYRIGHT
> >  Copyright 1996,1997, Frederic Lepied.
> >  .PP
> > diff --git a/src/Makefile.am b/src/Makefile.am
> > index c33fae3..985207b 100644
> > --- a/src/Makefile.am
> > +++ b/src/Makefile.am
> > @@ -27,7 +27,7 @@ xinput_LDADD = $(XINPUT_LIBS)
> >  
> >  
> >  if HAVE_XI2
> > -xinput2_files = hierarchy.c setcp.c test_xi2.c
> > +xinput2_files = hierarchy.c setcp.c test_xi2.c transform.c
> >  endif
> >  
> >  xinput_SOURCES = \
> > diff --git a/src/transform.c b/src/transform.c
> > new file mode 100644
> > index 0000000..c1a065f
> > --- /dev/null
> > +++ b/src/transform.c
> > @@ -0,0 +1,263 @@
> > +/*
> > + * Copyright © 2011 Red Hat, Inc.
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > + * DEALINGS IN THE SOFTWARE.
> > + *
> > + */
> > +
> > +
> > +#include "xinput.h"
> > +#include <string.h>
> > +#include <X11/extensions/Xrandr.h>
> > +#include <X11/extensions/Xinerama.h>
> > +
> > +
> > +typedef struct Matrix {
> > +    float m[9];
> > +} Matrix;
> > +
> > +static void matrix_set(Matrix *m, int row, int col, float val)
> > +{
> > +    m->m[row * 3 + col] = val;
> > +}
> > +
> > +static void matrix_set_unity(Matrix *m)
> > +{
> > +    memset(m, 0, sizeof(m->m));
> > +    matrix_set(m, 0, 0, 1);
> > +    matrix_set(m, 1, 1, 1);
> > +    matrix_set(m, 2, 2, 1);
> > +}
> > +
> > +#if DEBUG
> > +static void matrix_print(const Matrix *m)
> > +{
> > +    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[0], m->m[1], m->m[2]);
> > +    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[3], m->m[4], m->m[5]);
> > +    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[6], m->m[7], m->m[8]);
> > +}
> > +#endif
> > +
> > +static int
> > +apply_matrix(Display *dpy, int deviceid, Matrix *m)
> > +{
> > +    Atom prop_float, prop_matrix;
> > +
> > +    union {
> > +        unsigned char *c;
> > +        float *f;
> > +    } data;
> > +    int format_return;
> > +    Atom type_return;
> > +    unsigned long nitems;
> > +    unsigned long bytes_after;
> > +
> > +    int rc;
> > +
> > +    prop_float = XInternAtom(dpy, "FLOAT", False);
> > +    prop_matrix = XInternAtom(dpy, "Coordinate Transformation Matrix", False);
> > +
> > +    if (!prop_float)
> > +    {
> > +        fprintf(stderr, "Float atom not found. This server is too old.\n");
> > +        return EXIT_FAILURE;
> > +    }
> > +    if (!prop_matrix)
> > +    {
> > +        fprintf(stderr, "Coordinate transformation matrix not found. This "
> > +                "server is too old\n");
> > +        return EXIT_FAILURE;
> > +    }
> > +
> > +    rc = XIGetProperty(dpy, deviceid, prop_matrix, 0, 9, False, prop_float,
> > +                       &type_return, &format_return, &nitems, &bytes_after,
> > +                       &data.c);
> > +    if (rc != Success || prop_float != type_return || format_return != 32 ||
> > +        nitems != 9 || bytes_after != 0)
> > +    {
> > +        fprintf(stderr, "Failed to retrieve current property values\n");
> > +        return EXIT_FAILURE;
> > +    }
> > +
> > +    memcpy(data.f, m->m, sizeof(m->m));
> > +
> > +    XIChangeProperty(dpy, deviceid, prop_matrix, prop_float,
> > +                     format_return, PropModeReplace, data.c, nitems);
> > +
> > +    XFree(data.c);
> > +
> > +    return EXIT_SUCCESS;
> > +}
> > +
> > +static void
> > +set_transformation_matrix(Display *dpy, Matrix *m, int offset_x, int offset_y,
> > +                                int screen_width, int screen_height)
> > +{
> > +    /* offset */
> > +    int width = DisplayWidth(dpy, DefaultScreen(dpy));
> > +    int height = DisplayHeight(dpy, DefaultScreen(dpy));
> > +
> > +    /* offset */
> > +    float x = 1.0 * offset_x/width;
> > +    float y = 1.0 * offset_y/height;
> > +
> > +    /* mapping */
> > +    float w = 1.0 * screen_width/width;
> > +    float h = 1.0 * screen_height/height;
> > +
> > +    matrix_set_unity(m);
> > +
> > +    matrix_set(m, 0, 2, x);
> > +    matrix_set(m, 1, 2, y);
> > +
> > +    matrix_set(m, 0, 0, w);
> > +    matrix_set(m, 1, 1, h);
> > +
> > +#if DEBUG
> > +    matrix_print(m);
> > +#endif
> > +}
> > +
> > +static int
> > +map_crtc_xrandr(Display *dpy, int deviceid, const char *output_name)
> > +{
> > +    int i, found = 0;
> > +    int rc = EXIT_FAILURE;
> > +    XRRScreenResources *res;
> > +    XRROutputInfo *output_info;
> > +    XRRCrtcInfo *crtc_info;
> > +
> > +    res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
> > +
> > +    for (i = 0; i < res->noutput && !found; i++)
> > +    {
> > +        output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]);
> > +        if (!output_info->crtc || output_info->connection != RR_Connected)
> > +            continue;
> > +
> > +        crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc);
> > +        if (strcmp(output_info->name, output_name) == 0)
> > +        {
> > +            found = 1;
> > +            break;
> > +        }
> > +    }
> > +
> > +    /* crtc holds our screen info, need to compare to actual screen size */
> > +    if (found)
> > +    {
> > +        Matrix m;
> > +        matrix_set_unity(&m);
> > +        set_transformation_matrix(dpy, &m, crtc_info->x, crtc_info->y,
> > +                                  crtc_info->width, crtc_info->height);
> > +        rc = apply_matrix(dpy, deviceid, &m);
> > +    } else
> > +        printf("Unable to find output '%s'. "
> > +                "Output may not be connected.\n", output_name);
> > +
> > +    XRRFreeScreenResources(res);
> > +
> > +    return rc;
> > +}
> > +
> > +static int
> > +map_crtc_xinerama(Display *dpy, int deviceid, const char *output_name)
> > +{
> > +    const char *prefix = "HEAD-";
> > +    XineramaScreenInfo *screens = NULL;
> > +    int rc = EXIT_FAILURE;
> > +    int event, error;
> > +    int nscreens;
> > +    int head;
> > +    Matrix m;
> > +
> > +    if (!XineramaQueryExtension(dpy, &event, &error))
> > +    {
> > +        fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n");
> > +        goto out;
> > +    }
> > +
> > +    if (strlen(output_name) < strlen(prefix) + 1 ||
> > +        strncmp(output_name, prefix, strlen(prefix)) != 0)
> > +    {
> > +        fprintf(stderr, "Please specify the output name as HEAD-X,"
> > +                "where X is the screen number\n");
> > +        goto out;
> > +    }
> > +
> > +    head = output_name[strlen(prefix)] - '0';
> > +
> > +    screens = XineramaQueryScreens(dpy, &nscreens);
> > +
> > +    if (nscreens == 0)
> > +    {
> > +        fprintf(stderr, "Xinerama failed to query screens.\n");
> > +        goto out;
> > +    } else if (nscreens <= head)
> > +    {
> > +        fprintf(stderr, "Found %d screens, but you requested %s.\n",
> > +                nscreens, output_name);
> > +        goto out;
> > +    }
> > +
> > +    matrix_set_unity(&m);
> > +    set_transformation_matrix(dpy, &m,
> > +                              screens[head].x_org, screens[head].y_org,
> > +                              screens[head].width, screens[head].height);
> > +    rc = apply_matrix(dpy, deviceid, &m);
> > +
> > +out:
> > +    XFree(screens);
> > +    return rc;
> > +}
> > +
> > +int
> > +map_to_crtc(Display *dpy, int argc, char *argv[], char *name, char *desc)
> > +{
> > +    int opcode, event, error;
> > +    int maj, min;
> > +    char *crtc_name;
> > +    XIDeviceInfo *info;
> > +
> > +    if (argc < 2)
> > +    {
> > +        fprintf(stderr, "Usage: xinput %s %s\n", name, desc);
> > +        return EXIT_FAILURE;
> > +    }
> > +
> > +    info = xi2_find_device_info(dpy, argv[0]);
> > +    if (!info)
> > +    {
> > +        fprintf(stderr, "unable to find device %s\n", argv[0]);
> > +        return EXIT_FAILURE;
> > +    }
> > +
> > +    crtc_name = argv[1];
> > +
> > +    /* Check for RandR 1.2. Server bug causes the NVIDIA driver to
> > +     * report with RandR 1.3 support but it doesn't expose RandR CRTCs.
> > +     * Force Xinerama if NV-CONTROL is present */
> > +    if (XQueryExtension(dpy, "NV-CONTROL", &opcode, &event, &error) ||
> > +        !XQueryExtension(dpy, "RANDR", &opcode, &event, &error) ||
> > +        !XRRQueryVersion(dpy, &maj, &min) || (maj * 1000 + min) < 1002)
> > +       return map_crtc_xinerama(dpy, info->deviceid, crtc_name);
> > +    else
> > +       return map_crtc_xrandr(dpy, info->deviceid, crtc_name);
> > +}
> > diff --git a/src/xinput.c b/src/xinput.c
> > index 64b4887..432cffc 100644
> > --- a/src/xinput.c
> > +++ b/src/xinput.c
> > @@ -104,6 +104,10 @@ static entry drivers[] =
> >        "<device>",
> >        test_xi2,
> >      },
> > +    { "map-to-crtc",
> > +      "<device> <crtc name>",
> > +      map_to_crtc,
> > +    },
> >  #endif
> >      { "list-props",
> >        "<device> [<device> ...]",
> > diff --git a/src/xinput.h b/src/xinput.h
> > index d44ce09..94b8f3c 100644
> > --- a/src/xinput.h
> > +++ b/src/xinput.h
> > @@ -77,5 +77,6 @@ int change_attachment( Display* display, int argc, char *argv[], char *prog_name
> >  int float_device( Display* display, int argc, char *argv[], char *prog_name, char *prog_desc);
> >  int set_clientpointer( Display* display, int argc, char *argv[], char *prog_name, char *prog_desc);
> >  int test_xi2( Display* display, int argc, char *argv[], char *prog_name, char *prog_desc);
> > +int map_to_crtc( Display* display, int argc, char *argv[], char *prog_name, char *prog_desc);
> >  
> >  /* end of xinput.h */
> 


More information about the xorg-devel mailing list