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