transform.c revision 0309d3b3
10309d3b3Smrg/*
20309d3b3Smrg * Copyright © 2011 Red Hat, Inc.
30309d3b3Smrg *
40309d3b3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
50309d3b3Smrg * copy of this software and associated documentation files (the "Software"),
60309d3b3Smrg * to deal in the Software without restriction, including without limitation
70309d3b3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
80309d3b3Smrg * and/or sell copies of the Software, and to permit persons to whom the
90309d3b3Smrg * Software is furnished to do so, subject to the following conditions:
100309d3b3Smrg *
110309d3b3Smrg * The above copyright notice and this permission notice (including the next
120309d3b3Smrg * paragraph) shall be included in all copies or substantial portions of the
130309d3b3Smrg * Software.
140309d3b3Smrg *
150309d3b3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
160309d3b3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
170309d3b3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
180309d3b3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
190309d3b3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
200309d3b3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
210309d3b3Smrg * DEALINGS IN THE SOFTWARE.
220309d3b3Smrg *
230309d3b3Smrg */
240309d3b3Smrg
250309d3b3Smrg
260309d3b3Smrg#include "xinput.h"
270309d3b3Smrg#include <string.h>
280309d3b3Smrg#include <X11/extensions/Xrandr.h>
290309d3b3Smrg#include <X11/extensions/Xinerama.h>
300309d3b3Smrg
310309d3b3Smrg
320309d3b3Smrgtypedef struct Matrix {
330309d3b3Smrg    float m[9];
340309d3b3Smrg} Matrix;
350309d3b3Smrg
360309d3b3Smrgstatic void matrix_set(Matrix *m, int row, int col, float val)
370309d3b3Smrg{
380309d3b3Smrg    m->m[row * 3 + col] = val;
390309d3b3Smrg}
400309d3b3Smrg
410309d3b3Smrgstatic void matrix_set_unity(Matrix *m)
420309d3b3Smrg{
430309d3b3Smrg    memset(m, 0, sizeof(m->m));
440309d3b3Smrg    matrix_set(m, 0, 0, 1);
450309d3b3Smrg    matrix_set(m, 1, 1, 1);
460309d3b3Smrg    matrix_set(m, 2, 2, 1);
470309d3b3Smrg}
480309d3b3Smrg
490309d3b3Smrg#if DEBUG
500309d3b3Smrgstatic void matrix_print(const Matrix *m)
510309d3b3Smrg{
520309d3b3Smrg    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[0], m->m[1], m->m[2]);
530309d3b3Smrg    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[3], m->m[4], m->m[5]);
540309d3b3Smrg    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[6], m->m[7], m->m[8]);
550309d3b3Smrg}
560309d3b3Smrg#endif
570309d3b3Smrg
580309d3b3Smrgstatic int
590309d3b3Smrgapply_matrix(Display *dpy, int deviceid, Matrix *m)
600309d3b3Smrg{
610309d3b3Smrg    Atom prop_float, prop_matrix;
620309d3b3Smrg
630309d3b3Smrg    union {
640309d3b3Smrg        unsigned char *c;
650309d3b3Smrg        float *f;
660309d3b3Smrg    } data;
670309d3b3Smrg    int format_return;
680309d3b3Smrg    Atom type_return;
690309d3b3Smrg    unsigned long nitems;
700309d3b3Smrg    unsigned long bytes_after;
710309d3b3Smrg
720309d3b3Smrg    int rc;
730309d3b3Smrg
740309d3b3Smrg    prop_float = XInternAtom(dpy, "FLOAT", False);
750309d3b3Smrg    prop_matrix = XInternAtom(dpy, "Coordinate Transformation Matrix", False);
760309d3b3Smrg
770309d3b3Smrg    if (!prop_float)
780309d3b3Smrg    {
790309d3b3Smrg        fprintf(stderr, "Float atom not found. This server is too old.\n");
800309d3b3Smrg        return EXIT_FAILURE;
810309d3b3Smrg    }
820309d3b3Smrg    if (!prop_matrix)
830309d3b3Smrg    {
840309d3b3Smrg        fprintf(stderr, "Coordinate transformation matrix not found. This "
850309d3b3Smrg                "server is too old\n");
860309d3b3Smrg        return EXIT_FAILURE;
870309d3b3Smrg    }
880309d3b3Smrg
890309d3b3Smrg    rc = XIGetProperty(dpy, deviceid, prop_matrix, 0, 9, False, prop_float,
900309d3b3Smrg                       &type_return, &format_return, &nitems, &bytes_after,
910309d3b3Smrg                       &data.c);
920309d3b3Smrg    if (rc != Success || prop_float != type_return || format_return != 32 ||
930309d3b3Smrg        nitems != 9 || bytes_after != 0)
940309d3b3Smrg    {
950309d3b3Smrg        fprintf(stderr, "Failed to retrieve current property values\n");
960309d3b3Smrg        return EXIT_FAILURE;
970309d3b3Smrg    }
980309d3b3Smrg
990309d3b3Smrg    memcpy(data.f, m->m, sizeof(m->m));
1000309d3b3Smrg
1010309d3b3Smrg    XIChangeProperty(dpy, deviceid, prop_matrix, prop_float,
1020309d3b3Smrg                     format_return, PropModeReplace, data.c, nitems);
1030309d3b3Smrg
1040309d3b3Smrg    XFree(data.c);
1050309d3b3Smrg
1060309d3b3Smrg    return EXIT_SUCCESS;
1070309d3b3Smrg}
1080309d3b3Smrg
1090309d3b3Smrgstatic void
1100309d3b3Smrgset_transformation_matrix(Display *dpy, Matrix *m, int offset_x, int offset_y,
1110309d3b3Smrg                                int screen_width, int screen_height)
1120309d3b3Smrg{
1130309d3b3Smrg    /* offset */
1140309d3b3Smrg    int width = DisplayWidth(dpy, DefaultScreen(dpy));
1150309d3b3Smrg    int height = DisplayHeight(dpy, DefaultScreen(dpy));
1160309d3b3Smrg
1170309d3b3Smrg    /* offset */
1180309d3b3Smrg    float x = 1.0 * offset_x/width;
1190309d3b3Smrg    float y = 1.0 * offset_y/height;
1200309d3b3Smrg
1210309d3b3Smrg    /* mapping */
1220309d3b3Smrg    float w = 1.0 * screen_width/width;
1230309d3b3Smrg    float h = 1.0 * screen_height/height;
1240309d3b3Smrg
1250309d3b3Smrg    matrix_set_unity(m);
1260309d3b3Smrg
1270309d3b3Smrg    matrix_set(m, 0, 2, x);
1280309d3b3Smrg    matrix_set(m, 1, 2, y);
1290309d3b3Smrg
1300309d3b3Smrg    matrix_set(m, 0, 0, w);
1310309d3b3Smrg    matrix_set(m, 1, 1, h);
1320309d3b3Smrg
1330309d3b3Smrg#if DEBUG
1340309d3b3Smrg    matrix_print(m);
1350309d3b3Smrg#endif
1360309d3b3Smrg}
1370309d3b3Smrg
1380309d3b3Smrg/* Caller must free return value */
1390309d3b3Smrgstatic XRROutputInfo*
1400309d3b3Smrgfind_output_xrandr(Display *dpy, const char *output_name)
1410309d3b3Smrg{
1420309d3b3Smrg    XRRScreenResources *res;
1430309d3b3Smrg    XRROutputInfo *output_info = NULL;
1440309d3b3Smrg    int i;
1450309d3b3Smrg    int found = 0;
1460309d3b3Smrg
1470309d3b3Smrg    res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
1480309d3b3Smrg
1490309d3b3Smrg    for (i = 0; i < res->noutput && !found; i++)
1500309d3b3Smrg    {
1510309d3b3Smrg        output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]);
1520309d3b3Smrg
1530309d3b3Smrg        if (output_info->crtc && output_info->connection == RR_Connected &&
1540309d3b3Smrg            strcmp(output_info->name, output_name) == 0)
1550309d3b3Smrg        {
1560309d3b3Smrg            found = 1;
1570309d3b3Smrg            break;
1580309d3b3Smrg        }
1590309d3b3Smrg
1600309d3b3Smrg        XRRFreeOutputInfo(output_info);
1610309d3b3Smrg    }
1620309d3b3Smrg
1630309d3b3Smrg    XRRFreeScreenResources(res);
1640309d3b3Smrg
1650309d3b3Smrg    if (!found)
1660309d3b3Smrg        output_info = NULL;
1670309d3b3Smrg
1680309d3b3Smrg    return output_info;
1690309d3b3Smrg}
1700309d3b3Smrg
1710309d3b3Smrgstatic int
1720309d3b3Smrgmap_output_xrandr(Display *dpy, int deviceid, const char *output_name)
1730309d3b3Smrg{
1740309d3b3Smrg    int rc = EXIT_FAILURE;
1750309d3b3Smrg    XRRScreenResources *res;
1760309d3b3Smrg    XRROutputInfo *output_info;
1770309d3b3Smrg
1780309d3b3Smrg    res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
1790309d3b3Smrg    output_info = find_output_xrandr(dpy, output_name);
1800309d3b3Smrg
1810309d3b3Smrg    /* crtc holds our screen info, need to compare to actual screen size */
1820309d3b3Smrg    if (output_info)
1830309d3b3Smrg    {
1840309d3b3Smrg        XRRCrtcInfo *crtc_info;
1850309d3b3Smrg        Matrix m;
1860309d3b3Smrg        matrix_set_unity(&m);
1870309d3b3Smrg        crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc);
1880309d3b3Smrg        set_transformation_matrix(dpy, &m, crtc_info->x, crtc_info->y,
1890309d3b3Smrg                                  crtc_info->width, crtc_info->height);
1900309d3b3Smrg        rc = apply_matrix(dpy, deviceid, &m);
1910309d3b3Smrg        XRRFreeCrtcInfo(crtc_info);
1920309d3b3Smrg        XRRFreeOutputInfo(output_info);
1930309d3b3Smrg    } else
1940309d3b3Smrg        printf("Unable to find output '%s'. "
1950309d3b3Smrg                "Output may not be connected.\n", output_name);
1960309d3b3Smrg
1970309d3b3Smrg    XRRFreeScreenResources(res);
1980309d3b3Smrg
1990309d3b3Smrg    return rc;
2000309d3b3Smrg}
2010309d3b3Smrg
2020309d3b3Smrgstatic int
2030309d3b3Smrgmap_output_xinerama(Display *dpy, int deviceid, const char *output_name)
2040309d3b3Smrg{
2050309d3b3Smrg    const char *prefix = "HEAD-";
2060309d3b3Smrg    XineramaScreenInfo *screens = NULL;
2070309d3b3Smrg    int rc = EXIT_FAILURE;
2080309d3b3Smrg    int event, error;
2090309d3b3Smrg    int nscreens;
2100309d3b3Smrg    int head;
2110309d3b3Smrg    Matrix m;
2120309d3b3Smrg
2130309d3b3Smrg    if (!XineramaQueryExtension(dpy, &event, &error))
2140309d3b3Smrg    {
2150309d3b3Smrg        fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n");
2160309d3b3Smrg        goto out;
2170309d3b3Smrg    }
2180309d3b3Smrg
2190309d3b3Smrg    if (strlen(output_name) < strlen(prefix) + 1 ||
2200309d3b3Smrg        strncmp(output_name, prefix, strlen(prefix)) != 0)
2210309d3b3Smrg    {
2220309d3b3Smrg        fprintf(stderr, "Please specify the output name as HEAD-X,"
2230309d3b3Smrg                "where X is the screen number\n");
2240309d3b3Smrg        goto out;
2250309d3b3Smrg    }
2260309d3b3Smrg
2270309d3b3Smrg    head = output_name[strlen(prefix)] - '0';
2280309d3b3Smrg
2290309d3b3Smrg    screens = XineramaQueryScreens(dpy, &nscreens);
2300309d3b3Smrg
2310309d3b3Smrg    if (nscreens == 0)
2320309d3b3Smrg    {
2330309d3b3Smrg        fprintf(stderr, "Xinerama failed to query screens.\n");
2340309d3b3Smrg        goto out;
2350309d3b3Smrg    } else if (nscreens <= head)
2360309d3b3Smrg    {
2370309d3b3Smrg        fprintf(stderr, "Found %d screens, but you requested %s.\n",
2380309d3b3Smrg                nscreens, output_name);
2390309d3b3Smrg        goto out;
2400309d3b3Smrg    }
2410309d3b3Smrg
2420309d3b3Smrg    matrix_set_unity(&m);
2430309d3b3Smrg    set_transformation_matrix(dpy, &m,
2440309d3b3Smrg                              screens[head].x_org, screens[head].y_org,
2450309d3b3Smrg                              screens[head].width, screens[head].height);
2460309d3b3Smrg    rc = apply_matrix(dpy, deviceid, &m);
2470309d3b3Smrg
2480309d3b3Smrgout:
2490309d3b3Smrg    XFree(screens);
2500309d3b3Smrg    return rc;
2510309d3b3Smrg}
2520309d3b3Smrg
2530309d3b3Smrgint
2540309d3b3Smrgmap_to_output(Display *dpy, int argc, char *argv[], char *name, char *desc)
2550309d3b3Smrg{
2560309d3b3Smrg    char *output_name;
2570309d3b3Smrg    XIDeviceInfo *info;
2580309d3b3Smrg    XRROutputInfo *output_info;
2590309d3b3Smrg
2600309d3b3Smrg    if (argc < 2)
2610309d3b3Smrg    {
2620309d3b3Smrg        fprintf(stderr, "Usage: xinput %s %s\n", name, desc);
2630309d3b3Smrg        return EXIT_FAILURE;
2640309d3b3Smrg    }
2650309d3b3Smrg
2660309d3b3Smrg    info = xi2_find_device_info(dpy, argv[0]);
2670309d3b3Smrg    if (!info)
2680309d3b3Smrg    {
2690309d3b3Smrg        fprintf(stderr, "unable to find device '%s'\n", argv[0]);
2700309d3b3Smrg        return EXIT_FAILURE;
2710309d3b3Smrg    }
2720309d3b3Smrg
2730309d3b3Smrg    output_name = argv[1];
2740309d3b3Smrg    output_info = find_output_xrandr(dpy, output_name);
2750309d3b3Smrg    if (!output_info)
2760309d3b3Smrg    {
2770309d3b3Smrg        /* Output doesn't exist. Is this a (partial) non-RandR setup?  */
2780309d3b3Smrg        output_info = find_output_xrandr(dpy, "default");
2790309d3b3Smrg        if (output_info)
2800309d3b3Smrg        {
2810309d3b3Smrg            XRRFreeOutputInfo(output_info);
2820309d3b3Smrg            if (strncmp("HEAD-", output_name, strlen("HEAD-")) == 0)
2830309d3b3Smrg                return map_output_xinerama(dpy, info->deviceid, output_name);
2840309d3b3Smrg        }
2850309d3b3Smrg    } else
2860309d3b3Smrg        XRRFreeOutputInfo(output_info);
2870309d3b3Smrg
2880309d3b3Smrg    return map_output_xrandr(dpy, info->deviceid, output_name);
2890309d3b3Smrg}
290