]> diplodocus.org Git - xorg-xinput/blob - src/transform.c
3ea7ff85e98cc213d87134213aeabeae15a2e2b7
[xorg-xinput] / src / transform.c
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
32 typedef struct Matrix {
33 float m[9];
34 } Matrix;
35
36 static void matrix_set(Matrix *m, int row, int col, float val)
37 {
38 m->m[row * 3 + col] = val;
39 }
40
41 static 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
50 static 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
58 static int
59 apply_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
109 static void
110 set_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 static int
139 map_output_xrandr(Display *dpy, int deviceid, const char *output_name)
140 {
141 int i, found = 0;
142 int rc = EXIT_FAILURE;
143 XRRScreenResources *res;
144 XRROutputInfo *output_info;
145
146 res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
147
148 for (i = 0; i < res->noutput && !found; i++)
149 {
150 output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]);
151
152 if (output_info->crtc && output_info->connection == RR_Connected &&
153 strcmp(output_info->name, output_name) == 0)
154 {
155 found = 1;
156 break;
157 }
158
159 XRRFreeOutputInfo(output_info);
160 }
161
162 /* crtc holds our screen info, need to compare to actual screen size */
163 if (found)
164 {
165 XRRCrtcInfo *crtc_info;
166 Matrix m;
167 matrix_set_unity(&m);
168 crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc);
169 set_transformation_matrix(dpy, &m, crtc_info->x, crtc_info->y,
170 crtc_info->width, crtc_info->height);
171 rc = apply_matrix(dpy, deviceid, &m);
172 XRRFreeCrtcInfo(crtc_info);
173 XRRFreeOutputInfo(output_info);
174 } else
175 printf("Unable to find output '%s'. "
176 "Output may not be connected.\n", output_name);
177
178 XRRFreeScreenResources(res);
179
180 return rc;
181 }
182
183 static int
184 map_output_xinerama(Display *dpy, int deviceid, const char *output_name)
185 {
186 const char *prefix = "HEAD-";
187 XineramaScreenInfo *screens = NULL;
188 int rc = EXIT_FAILURE;
189 int event, error;
190 int nscreens;
191 int head;
192 Matrix m;
193
194 if (!XineramaQueryExtension(dpy, &event, &error))
195 {
196 fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n");
197 goto out;
198 }
199
200 if (strlen(output_name) < strlen(prefix) + 1 ||
201 strncmp(output_name, prefix, strlen(prefix)) != 0)
202 {
203 fprintf(stderr, "Please specify the output name as HEAD-X,"
204 "where X is the screen number\n");
205 goto out;
206 }
207
208 head = output_name[strlen(prefix)] - '0';
209
210 screens = XineramaQueryScreens(dpy, &nscreens);
211
212 if (nscreens == 0)
213 {
214 fprintf(stderr, "Xinerama failed to query screens.\n");
215 goto out;
216 } else if (nscreens <= head)
217 {
218 fprintf(stderr, "Found %d screens, but you requested %s.\n",
219 nscreens, output_name);
220 goto out;
221 }
222
223 matrix_set_unity(&m);
224 set_transformation_matrix(dpy, &m,
225 screens[head].x_org, screens[head].y_org,
226 screens[head].width, screens[head].height);
227 rc = apply_matrix(dpy, deviceid, &m);
228
229 out:
230 XFree(screens);
231 return rc;
232 }
233
234 int
235 map_to_output(Display *dpy, int argc, char *argv[], char *name, char *desc)
236 {
237 int opcode, event, error;
238 int maj, min;
239 char *output_name;
240 XIDeviceInfo *info;
241
242 if (argc < 2)
243 {
244 fprintf(stderr, "Usage: xinput %s %s\n", name, desc);
245 return EXIT_FAILURE;
246 }
247
248 info = xi2_find_device_info(dpy, argv[0]);
249 if (!info)
250 {
251 fprintf(stderr, "unable to find device '%s'\n", argv[0]);
252 return EXIT_FAILURE;
253 }
254
255 output_name = argv[1];
256
257 /* Check for RandR 1.2. Server bug causes the NVIDIA driver to
258 * report with RandR 1.3 support but it doesn't expose RandR outputs.
259 * Force Xinerama if NV-CONTROL is present */
260 if (XQueryExtension(dpy, "NV-CONTROL", &opcode, &event, &error) ||
261 !XQueryExtension(dpy, "RANDR", &opcode, &event, &error) ||
262 !XRRQueryVersion(dpy, &maj, &min) || (maj * 1000 + min) < 1002)
263 return map_output_xinerama(dpy, info->deviceid, output_name);
264 else
265 return map_output_xrandr(dpy, info->deviceid, output_name);
266 }