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 110matrix_s4(Matrix *m, float x02, float x12, float d1, float d2, int main_diag) 111{ 112 matrix_set(m, 0, 2, x02); 113 matrix_set(m, 1, 2, x12); 114 115 if (main_diag) { 116 matrix_set(m, 0, 0, d1); 117 matrix_set(m, 1, 1, d2); 118 } else { 119 matrix_set(m, 0, 0, 0); 120 matrix_set(m, 1, 1, 0); 121 matrix_set(m, 0, 1, d1); 122 matrix_set(m, 1, 0, d2); 123 } 124} 125 126#define RR_Reflect_All (RR_Reflect_X|RR_Reflect_Y) 127 128static void 129set_transformation_matrix(Display *dpy, Matrix *m, int offset_x, int offset_y, 130 int screen_width, int screen_height, 131 int rotation) 132{ 133 /* total display size */ 134 int width = DisplayWidth(dpy, DefaultScreen(dpy)); 135 int height = DisplayHeight(dpy, DefaultScreen(dpy)); 136 137 /* offset */ 138 float x = 1.0 * offset_x/width; 139 float y = 1.0 * offset_y/height; 140 141 /* mapping */ 142 float w = 1.0 * screen_width/width; 143 float h = 1.0 * screen_height/height; 144 145 matrix_set_unity(m); 146 147 /* 148 * There are 16 cases: 149 * Rotation X Reflection 150 * Rotation: 0 | 90 | 180 | 270 151 * Reflection: None | X | Y | XY 152 * 153 * They are spelled out instead of doing matrix multiplication to avoid 154 * any floating point errors. 155 */ 156 switch (rotation) { 157 case RR_Rotate_0: 158 case RR_Rotate_180 | RR_Reflect_All: 159 matrix_s4(m, x, y, w, h, 1); 160 break; 161 case RR_Reflect_X|RR_Rotate_0: 162 case RR_Reflect_Y|RR_Rotate_180: 163 matrix_s4(m, x + w, y, -w, h, 1); 164 break; 165 case RR_Reflect_Y|RR_Rotate_0: 166 case RR_Reflect_X|RR_Rotate_180: 167 matrix_s4(m, x, y + h, w, -h, 1); 168 break; 169 case RR_Rotate_90: 170 case RR_Rotate_270 | RR_Reflect_All: /* left limited - correct in working zone. */ 171 matrix_s4(m, x + w, y, -w, h, 0); 172 break; 173 case RR_Rotate_270: 174 case RR_Rotate_90 | RR_Reflect_All: /* left limited - correct in working zone. */ 175 matrix_s4(m, x, y + h, w, -h, 0); 176 break; 177 case RR_Rotate_90 | RR_Reflect_X: /* left limited - correct in working zone. */ 178 case RR_Rotate_270 | RR_Reflect_Y: /* left limited - correct in working zone. */ 179 matrix_s4(m, x, y, w, h, 0); 180 break; 181 case RR_Rotate_90 | RR_Reflect_Y: /* right limited - correct in working zone. */ 182 case RR_Rotate_270 | RR_Reflect_X: /* right limited - correct in working zone. */ 183 matrix_s4(m, x + w, y + h, -w, -h, 0); 184 break; 185 case RR_Rotate_180: 186 case RR_Reflect_All|RR_Rotate_0: 187 matrix_s4(m, x + w, y + h, -w, -h, 1); 188 break; 189 } 190 191#if DEBUG 192 matrix_print(m); 193#endif 194} 195 196/* Caller must free return value */ 197static XRROutputInfo* 198find_output_xrandr(Display *dpy, const char *output_name) 199{ 200 XRRScreenResources *res; 201 XRROutputInfo *output_info = NULL; 202 int i; 203 int found = 0; 204 205 res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy)); 206 207 for (i = 0; i < res->noutput && !found; i++) 208 { 209 output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]); 210 211 if (output_info->crtc && output_info->connection == RR_Connected && 212 strcmp(output_info->name, output_name) == 0) 213 { 214 found = 1; 215 break; 216 } 217 218 XRRFreeOutputInfo(output_info); 219 } 220 221 XRRFreeScreenResources(res); 222 223 if (!found) 224 output_info = NULL; 225 226 return output_info; 227} 228 229static int 230map_output_xrandr(Display *dpy, int deviceid, const char *output_name) 231{ 232 int rc = EXIT_FAILURE; 233 XRRScreenResources *res; 234 XRROutputInfo *output_info; 235 236 res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy)); 237 output_info = find_output_xrandr(dpy, output_name); 238 239 /* crtc holds our screen info, need to compare to actual screen size */ 240 if (output_info) 241 { 242 XRRCrtcInfo *crtc_info; 243 Matrix m; 244 matrix_set_unity(&m); 245 crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc); 246 set_transformation_matrix(dpy, &m, crtc_info->x, crtc_info->y, 247 crtc_info->width, crtc_info->height, 248 crtc_info->rotation); 249 rc = apply_matrix(dpy, deviceid, &m); 250 XRRFreeCrtcInfo(crtc_info); 251 XRRFreeOutputInfo(output_info); 252 } else 253 printf("Unable to find output '%s'. " 254 "Output may not be connected.\n", output_name); 255 256 XRRFreeScreenResources(res); 257 258 return rc; 259} 260 261static int 262map_output_xinerama(Display *dpy, int deviceid, const char *output_name) 263{ 264 const char *prefix = "HEAD-"; 265 XineramaScreenInfo *screens = NULL; 266 int rc = EXIT_FAILURE; 267 int event, error; 268 int nscreens; 269 int head; 270 Matrix m; 271 272 if (!XineramaQueryExtension(dpy, &event, &error)) 273 { 274 fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n"); 275 goto out; 276 } 277 278 if (strlen(output_name) < strlen(prefix) + 1 || 279 strncmp(output_name, prefix, strlen(prefix)) != 0) 280 { 281 fprintf(stderr, "Please specify the output name as HEAD-X," 282 "where X is the screen number\n"); 283 goto out; 284 } 285 286 head = output_name[strlen(prefix)] - '0'; 287 288 screens = XineramaQueryScreens(dpy, &nscreens); 289 290 if (nscreens == 0) 291 { 292 fprintf(stderr, "Xinerama failed to query screens.\n"); 293 goto out; 294 } else if (nscreens <= head) 295 { 296 fprintf(stderr, "Found %d screens, but you requested %s.\n", 297 nscreens, output_name); 298 goto out; 299 } 300 301 matrix_set_unity(&m); 302 set_transformation_matrix(dpy, &m, 303 screens[head].x_org, screens[head].y_org, 304 screens[head].width, screens[head].height, 305 RR_Rotate_0); 306 rc = apply_matrix(dpy, deviceid, &m); 307 308out: 309 XFree(screens); 310 return rc; 311} 312 313int 314map_to_output(Display *dpy, int argc, char *argv[], char *name, char *desc) 315{ 316 char *output_name; 317 XIDeviceInfo *info; 318 XRROutputInfo *output_info; 319 320 if (argc < 2) 321 { 322 fprintf(stderr, "Usage: xinput %s %s\n", name, desc); 323 return EXIT_FAILURE; 324 } 325 326 info = xi2_find_device_info(dpy, argv[0]); 327 if (!info) 328 { 329 fprintf(stderr, "unable to find device '%s'\n", argv[0]); 330 return EXIT_FAILURE; 331 } 332 333 output_name = argv[1]; 334 335 if (!strcmp("all", output_name)) 336 { 337 Matrix m; 338 matrix_set_unity(&m); 339 return apply_matrix(dpy, info->deviceid, &m); 340 } 341 342 output_info = find_output_xrandr(dpy, output_name); 343 if (!output_info) 344 { 345 /* Output doesn't exist. Is this a (partial) non-RandR setup? */ 346 output_info = find_output_xrandr(dpy, "default"); 347 if (output_info) 348 { 349 XRRFreeOutputInfo(output_info); 350 if (strncmp("HEAD-", output_name, strlen("HEAD-")) == 0) 351 return map_output_xinerama(dpy, info->deviceid, output_name); 352 } 353 } else 354 XRRFreeOutputInfo(output_info); 355 356 return map_output_xrandr(dpy, info->deviceid, output_name); 357} 358