transform.c revision 0309d3b3
1/*
2 * Copyright © 2011 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25
26#include "xinput.h"
27#include <string.h>
28#include <X11/extensions/Xrandr.h>
29#include <X11/extensions/Xinerama.h>
30
31
32typedef struct Matrix {
33    float m[9];
34} Matrix;
35
36static void matrix_set(Matrix *m, int row, int col, float val)
37{
38    m->m[row * 3 + col] = val;
39}
40
41static void matrix_set_unity(Matrix *m)
42{
43    memset(m, 0, sizeof(m->m));
44    matrix_set(m, 0, 0, 1);
45    matrix_set(m, 1, 1, 1);
46    matrix_set(m, 2, 2, 1);
47}
48
49#if DEBUG
50static void matrix_print(const Matrix *m)
51{
52    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[0], m->m[1], m->m[2]);
53    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[3], m->m[4], m->m[5]);
54    printf("[ %3.3f %3.3f %3.3f ]\n", m->m[6], m->m[7], m->m[8]);
55}
56#endif
57
58static int
59apply_matrix(Display *dpy, int deviceid, Matrix *m)
60{
61    Atom prop_float, prop_matrix;
62
63    union {
64        unsigned char *c;
65        float *f;
66    } data;
67    int format_return;
68    Atom type_return;
69    unsigned long nitems;
70    unsigned long bytes_after;
71
72    int rc;
73
74    prop_float = XInternAtom(dpy, "FLOAT", False);
75    prop_matrix = XInternAtom(dpy, "Coordinate Transformation Matrix", False);
76
77    if (!prop_float)
78    {
79        fprintf(stderr, "Float atom not found. This server is too old.\n");
80        return EXIT_FAILURE;
81    }
82    if (!prop_matrix)
83    {
84        fprintf(stderr, "Coordinate transformation matrix not found. This "
85                "server is too old\n");
86        return EXIT_FAILURE;
87    }
88
89    rc = XIGetProperty(dpy, deviceid, prop_matrix, 0, 9, False, prop_float,
90                       &type_return, &format_return, &nitems, &bytes_after,
91                       &data.c);
92    if (rc != Success || prop_float != type_return || format_return != 32 ||
93        nitems != 9 || bytes_after != 0)
94    {
95        fprintf(stderr, "Failed to retrieve current property values\n");
96        return EXIT_FAILURE;
97    }
98
99    memcpy(data.f, m->m, sizeof(m->m));
100
101    XIChangeProperty(dpy, deviceid, prop_matrix, prop_float,
102                     format_return, PropModeReplace, data.c, nitems);
103
104    XFree(data.c);
105
106    return EXIT_SUCCESS;
107}
108
109static void
110set_transformation_matrix(Display *dpy, Matrix *m, int offset_x, int offset_y,
111                                int screen_width, int screen_height)
112{
113    /* offset */
114    int width = DisplayWidth(dpy, DefaultScreen(dpy));
115    int height = DisplayHeight(dpy, DefaultScreen(dpy));
116
117    /* offset */
118    float x = 1.0 * offset_x/width;
119    float y = 1.0 * offset_y/height;
120
121    /* mapping */
122    float w = 1.0 * screen_width/width;
123    float h = 1.0 * screen_height/height;
124
125    matrix_set_unity(m);
126
127    matrix_set(m, 0, 2, x);
128    matrix_set(m, 1, 2, y);
129
130    matrix_set(m, 0, 0, w);
131    matrix_set(m, 1, 1, h);
132
133#if DEBUG
134    matrix_print(m);
135#endif
136}
137
138/* Caller must free return value */
139static XRROutputInfo*
140find_output_xrandr(Display *dpy, const char *output_name)
141{
142    XRRScreenResources *res;
143    XRROutputInfo *output_info = NULL;
144    int i;
145    int found = 0;
146
147    res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
148
149    for (i = 0; i < res->noutput && !found; i++)
150    {
151        output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]);
152
153        if (output_info->crtc && output_info->connection == RR_Connected &&
154            strcmp(output_info->name, output_name) == 0)
155        {
156            found = 1;
157            break;
158        }
159
160        XRRFreeOutputInfo(output_info);
161    }
162
163    XRRFreeScreenResources(res);
164
165    if (!found)
166        output_info = NULL;
167
168    return output_info;
169}
170
171static int
172map_output_xrandr(Display *dpy, int deviceid, const char *output_name)
173{
174    int rc = EXIT_FAILURE;
175    XRRScreenResources *res;
176    XRROutputInfo *output_info;
177
178    res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
179    output_info = find_output_xrandr(dpy, output_name);
180
181    /* crtc holds our screen info, need to compare to actual screen size */
182    if (output_info)
183    {
184        XRRCrtcInfo *crtc_info;
185        Matrix m;
186        matrix_set_unity(&m);
187        crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc);
188        set_transformation_matrix(dpy, &m, crtc_info->x, crtc_info->y,
189                                  crtc_info->width, crtc_info->height);
190        rc = apply_matrix(dpy, deviceid, &m);
191        XRRFreeCrtcInfo(crtc_info);
192        XRRFreeOutputInfo(output_info);
193    } else
194        printf("Unable to find output '%s'. "
195                "Output may not be connected.\n", output_name);
196
197    XRRFreeScreenResources(res);
198
199    return rc;
200}
201
202static int
203map_output_xinerama(Display *dpy, int deviceid, const char *output_name)
204{
205    const char *prefix = "HEAD-";
206    XineramaScreenInfo *screens = NULL;
207    int rc = EXIT_FAILURE;
208    int event, error;
209    int nscreens;
210    int head;
211    Matrix m;
212
213    if (!XineramaQueryExtension(dpy, &event, &error))
214    {
215        fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n");
216        goto out;
217    }
218
219    if (strlen(output_name) < strlen(prefix) + 1 ||
220        strncmp(output_name, prefix, strlen(prefix)) != 0)
221    {
222        fprintf(stderr, "Please specify the output name as HEAD-X,"
223                "where X is the screen number\n");
224        goto out;
225    }
226
227    head = output_name[strlen(prefix)] - '0';
228
229    screens = XineramaQueryScreens(dpy, &nscreens);
230
231    if (nscreens == 0)
232    {
233        fprintf(stderr, "Xinerama failed to query screens.\n");
234        goto out;
235    } else if (nscreens <= head)
236    {
237        fprintf(stderr, "Found %d screens, but you requested %s.\n",
238                nscreens, output_name);
239        goto out;
240    }
241
242    matrix_set_unity(&m);
243    set_transformation_matrix(dpy, &m,
244                              screens[head].x_org, screens[head].y_org,
245                              screens[head].width, screens[head].height);
246    rc = apply_matrix(dpy, deviceid, &m);
247
248out:
249    XFree(screens);
250    return rc;
251}
252
253int
254map_to_output(Display *dpy, int argc, char *argv[], char *name, char *desc)
255{
256    char *output_name;
257    XIDeviceInfo *info;
258    XRROutputInfo *output_info;
259
260    if (argc < 2)
261    {
262        fprintf(stderr, "Usage: xinput %s %s\n", name, desc);
263        return EXIT_FAILURE;
264    }
265
266    info = xi2_find_device_info(dpy, argv[0]);
267    if (!info)
268    {
269        fprintf(stderr, "unable to find device '%s'\n", argv[0]);
270        return EXIT_FAILURE;
271    }
272
273    output_name = argv[1];
274    output_info = find_output_xrandr(dpy, output_name);
275    if (!output_info)
276    {
277        /* Output doesn't exist. Is this a (partial) non-RandR setup?  */
278        output_info = find_output_xrandr(dpy, "default");
279        if (output_info)
280        {
281            XRRFreeOutputInfo(output_info);
282            if (strncmp("HEAD-", output_name, strlen("HEAD-")) == 0)
283                return map_output_xinerama(dpy, info->deviceid, output_name);
284        }
285    } else
286        XRRFreeOutputInfo(output_info);
287
288    return map_output_xrandr(dpy, info->deviceid, output_name);
289}
290