/* * Copyright © 2011 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include "xinput.h" #include #include #include typedef struct Matrix { float m[9]; } Matrix; static void matrix_set(Matrix *m, int row, int col, float val) { m->m[row * 3 + col] = val; } static void matrix_set_unity(Matrix *m) { memset(m, 0, sizeof(m->m)); matrix_set(m, 0, 0, 1); matrix_set(m, 1, 1, 1); matrix_set(m, 2, 2, 1); } #if DEBUG static void matrix_print(const Matrix *m) { printf("[ %3.3f %3.3f %3.3f ]\n", m->m[0], m->m[1], m->m[2]); printf("[ %3.3f %3.3f %3.3f ]\n", m->m[3], m->m[4], m->m[5]); printf("[ %3.3f %3.3f %3.3f ]\n", m->m[6], m->m[7], m->m[8]); } #endif static int apply_matrix(Display *dpy, int deviceid, Matrix *m) { Atom prop_float, prop_matrix; union { unsigned char *c; float *f; } data; int format_return; Atom type_return; unsigned long nitems; unsigned long bytes_after; int rc; prop_float = XInternAtom(dpy, "FLOAT", False); prop_matrix = XInternAtom(dpy, "Coordinate Transformation Matrix", False); if (!prop_float) { fprintf(stderr, "Float atom not found. This server is too old.\n"); return EXIT_FAILURE; } if (!prop_matrix) { fprintf(stderr, "Coordinate transformation matrix not found. This " "server is too old\n"); return EXIT_FAILURE; } rc = XIGetProperty(dpy, deviceid, prop_matrix, 0, 9, False, prop_float, &type_return, &format_return, &nitems, &bytes_after, &data.c); if (rc != Success || prop_float != type_return || format_return != 32 || nitems != 9 || bytes_after != 0) { fprintf(stderr, "Failed to retrieve current property values\n"); return EXIT_FAILURE; } memcpy(data.f, m->m, sizeof(m->m)); XIChangeProperty(dpy, deviceid, prop_matrix, prop_float, format_return, PropModeReplace, data.c, nitems); XFree(data.c); return EXIT_SUCCESS; } static void matrix_s4(Matrix *m, float x02, float x12, float d1, float d2, int main_diag) { matrix_set(m, 0, 2, x02); matrix_set(m, 1, 2, x12); if (main_diag) { matrix_set(m, 0, 0, d1); matrix_set(m, 1, 1, d2); } else { matrix_set(m, 0, 0, 0); matrix_set(m, 1, 1, 0); matrix_set(m, 0, 1, d1); matrix_set(m, 1, 0, d2); } } #define RR_Reflect_All (RR_Reflect_X|RR_Reflect_Y) static void set_transformation_matrix(Display *dpy, Matrix *m, int offset_x, int offset_y, int screen_width, int screen_height, int rotation) { /* total display size */ int width = DisplayWidth(dpy, DefaultScreen(dpy)); int height = DisplayHeight(dpy, DefaultScreen(dpy)); /* offset */ float x = 1.0 * offset_x/width; float y = 1.0 * offset_y/height; /* mapping */ float w = 1.0 * screen_width/width; float h = 1.0 * screen_height/height; matrix_set_unity(m); /* * There are 16 cases: * Rotation X Reflection * Rotation: 0 | 90 | 180 | 270 * Reflection: None | X | Y | XY * * They are spelled out instead of doing matrix multiplication to avoid * any floating point errors. */ switch (rotation) { case RR_Rotate_0: case RR_Rotate_180 | RR_Reflect_All: matrix_s4(m, x, y, w, h, 1); break; case RR_Reflect_X|RR_Rotate_0: case RR_Reflect_Y|RR_Rotate_180: matrix_s4(m, x + w, y, -w, h, 1); break; case RR_Reflect_Y|RR_Rotate_0: case RR_Reflect_X|RR_Rotate_180: matrix_s4(m, x, y + h, w, -h, 1); break; case RR_Rotate_90: case RR_Rotate_270 | RR_Reflect_All: /* left limited - correct in working zone. */ matrix_s4(m, x + w, y, -w, h, 0); break; case RR_Rotate_270: case RR_Rotate_90 | RR_Reflect_All: /* left limited - correct in working zone. */ matrix_s4(m, x, y + h, w, -h, 0); break; case RR_Rotate_90 | RR_Reflect_X: /* left limited - correct in working zone. */ case RR_Rotate_270 | RR_Reflect_Y: /* left limited - correct in working zone. */ matrix_s4(m, x, y, w, h, 0); break; case RR_Rotate_90 | RR_Reflect_Y: /* right limited - correct in working zone. */ case RR_Rotate_270 | RR_Reflect_X: /* right limited - correct in working zone. */ matrix_s4(m, x + w, y + h, -w, -h, 0); break; case RR_Rotate_180: case RR_Reflect_All|RR_Rotate_0: matrix_s4(m, x + w, y + h, -w, -h, 1); break; } #if DEBUG matrix_print(m); #endif } /* Caller must free return value */ static XRROutputInfo* find_output_xrandr(Display *dpy, const char *output_name) { XRRScreenResources *res; XRROutputInfo *output_info = NULL; int i; int found = 0; res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy)); for (i = 0; i < res->noutput && !found; i++) { output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]); if (output_info->crtc && output_info->connection == RR_Connected && strcmp(output_info->name, output_name) == 0) { found = 1; break; } XRRFreeOutputInfo(output_info); } XRRFreeScreenResources(res); if (!found) output_info = NULL; return output_info; } static int map_output_xrandr(Display *dpy, int deviceid, const char *output_name) { int rc = EXIT_FAILURE; XRRScreenResources *res; XRROutputInfo *output_info; res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy)); output_info = find_output_xrandr(dpy, output_name); /* crtc holds our screen info, need to compare to actual screen size */ if (output_info) { XRRCrtcInfo *crtc_info; Matrix m; matrix_set_unity(&m); crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc); set_transformation_matrix(dpy, &m, crtc_info->x, crtc_info->y, crtc_info->width, crtc_info->height, crtc_info->rotation); rc = apply_matrix(dpy, deviceid, &m); XRRFreeCrtcInfo(crtc_info); XRRFreeOutputInfo(output_info); } else printf("Unable to find output '%s'. " "Output may not be connected.\n", output_name); XRRFreeScreenResources(res); return rc; } static int map_output_xinerama(Display *dpy, int deviceid, const char *output_name) { const char *prefix = "HEAD-"; XineramaScreenInfo *screens = NULL; int rc = EXIT_FAILURE; int event, error; int nscreens; int head; Matrix m; if (!XineramaQueryExtension(dpy, &event, &error)) { fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n"); goto out; } if (strlen(output_name) < strlen(prefix) + 1 || strncmp(output_name, prefix, strlen(prefix)) != 0) { fprintf(stderr, "Please specify the output name as HEAD-X," "where X is the screen number\n"); goto out; } head = output_name[strlen(prefix)] - '0'; screens = XineramaQueryScreens(dpy, &nscreens); if (nscreens == 0) { fprintf(stderr, "Xinerama failed to query screens.\n"); goto out; } else if (nscreens <= head) { fprintf(stderr, "Found %d screens, but you requested %s.\n", nscreens, output_name); goto out; } matrix_set_unity(&m); set_transformation_matrix(dpy, &m, screens[head].x_org, screens[head].y_org, screens[head].width, screens[head].height, RR_Rotate_0); rc = apply_matrix(dpy, deviceid, &m); out: XFree(screens); return rc; } int map_to_output(Display *dpy, int argc, char *argv[], char *name, char *desc) { char *output_name; XIDeviceInfo *info; XRROutputInfo *output_info; if (argc < 2) { fprintf(stderr, "Usage: xinput %s %s\n", name, desc); return EXIT_FAILURE; } info = xi2_find_device_info(dpy, argv[0]); if (!info) { fprintf(stderr, "unable to find device '%s'\n", argv[0]); return EXIT_FAILURE; } output_name = argv[1]; output_info = find_output_xrandr(dpy, output_name); if (!output_info) { /* Output doesn't exist. Is this a (partial) non-RandR setup? */ output_info = find_output_xrandr(dpy, "default"); if (output_info) { XRRFreeOutputInfo(output_info); if (strncmp("HEAD-", output_name, strlen("HEAD-")) == 0) return map_output_xinerama(dpy, info->deviceid, output_name); } } else XRRFreeOutputInfo(output_info); return map_output_xrandr(dpy, info->deviceid, output_name); }