10309d3b3Smrg/* 20309d3b3Smrg * Copyright © 2011 Red Hat, Inc. 30309d3b3Smrg * 40309d3b3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 50309d3b3Smrg * copy of this software and associated documentation files (the "Software"), 60309d3b3Smrg * to deal in the Software without restriction, including without limitation 70309d3b3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 80309d3b3Smrg * and/or sell copies of the Software, and to permit persons to whom the 90309d3b3Smrg * Software is furnished to do so, subject to the following conditions: 100309d3b3Smrg * 110309d3b3Smrg * The above copyright notice and this permission notice (including the next 120309d3b3Smrg * paragraph) shall be included in all copies or substantial portions of the 130309d3b3Smrg * Software. 140309d3b3Smrg * 150309d3b3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 160309d3b3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 170309d3b3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 180309d3b3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 190309d3b3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 200309d3b3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 210309d3b3Smrg * DEALINGS IN THE SOFTWARE. 220309d3b3Smrg * 230309d3b3Smrg */ 240309d3b3Smrg 250309d3b3Smrg 260309d3b3Smrg#include "xinput.h" 270309d3b3Smrg#include <string.h> 280309d3b3Smrg#include <X11/extensions/Xrandr.h> 290309d3b3Smrg#include <X11/extensions/Xinerama.h> 300309d3b3Smrg 310309d3b3Smrg 320309d3b3Smrgtypedef struct Matrix { 330309d3b3Smrg float m[9]; 340309d3b3Smrg} Matrix; 350309d3b3Smrg 360309d3b3Smrgstatic void matrix_set(Matrix *m, int row, int col, float val) 370309d3b3Smrg{ 380309d3b3Smrg m->m[row * 3 + col] = val; 390309d3b3Smrg} 400309d3b3Smrg 410309d3b3Smrgstatic void matrix_set_unity(Matrix *m) 420309d3b3Smrg{ 430309d3b3Smrg memset(m, 0, sizeof(m->m)); 440309d3b3Smrg matrix_set(m, 0, 0, 1); 450309d3b3Smrg matrix_set(m, 1, 1, 1); 460309d3b3Smrg matrix_set(m, 2, 2, 1); 470309d3b3Smrg} 480309d3b3Smrg 490309d3b3Smrg#if DEBUG 500309d3b3Smrgstatic void matrix_print(const Matrix *m) 510309d3b3Smrg{ 520309d3b3Smrg printf("[ %3.3f %3.3f %3.3f ]\n", m->m[0], m->m[1], m->m[2]); 530309d3b3Smrg printf("[ %3.3f %3.3f %3.3f ]\n", m->m[3], m->m[4], m->m[5]); 540309d3b3Smrg printf("[ %3.3f %3.3f %3.3f ]\n", m->m[6], m->m[7], m->m[8]); 550309d3b3Smrg} 560309d3b3Smrg#endif 570309d3b3Smrg 580309d3b3Smrgstatic int 590309d3b3Smrgapply_matrix(Display *dpy, int deviceid, Matrix *m) 600309d3b3Smrg{ 610309d3b3Smrg Atom prop_float, prop_matrix; 620309d3b3Smrg 630309d3b3Smrg union { 640309d3b3Smrg unsigned char *c; 650309d3b3Smrg float *f; 660309d3b3Smrg } data; 670309d3b3Smrg int format_return; 680309d3b3Smrg Atom type_return; 690309d3b3Smrg unsigned long nitems; 700309d3b3Smrg unsigned long bytes_after; 710309d3b3Smrg 720309d3b3Smrg int rc; 730309d3b3Smrg 740309d3b3Smrg prop_float = XInternAtom(dpy, "FLOAT", False); 750309d3b3Smrg prop_matrix = XInternAtom(dpy, "Coordinate Transformation Matrix", False); 760309d3b3Smrg 770309d3b3Smrg if (!prop_float) 780309d3b3Smrg { 790309d3b3Smrg fprintf(stderr, "Float atom not found. This server is too old.\n"); 800309d3b3Smrg return EXIT_FAILURE; 810309d3b3Smrg } 820309d3b3Smrg if (!prop_matrix) 830309d3b3Smrg { 840309d3b3Smrg fprintf(stderr, "Coordinate transformation matrix not found. This " 850309d3b3Smrg "server is too old\n"); 860309d3b3Smrg return EXIT_FAILURE; 870309d3b3Smrg } 880309d3b3Smrg 890309d3b3Smrg rc = XIGetProperty(dpy, deviceid, prop_matrix, 0, 9, False, prop_float, 900309d3b3Smrg &type_return, &format_return, &nitems, &bytes_after, 910309d3b3Smrg &data.c); 920309d3b3Smrg if (rc != Success || prop_float != type_return || format_return != 32 || 930309d3b3Smrg nitems != 9 || bytes_after != 0) 940309d3b3Smrg { 950309d3b3Smrg fprintf(stderr, "Failed to retrieve current property values\n"); 960309d3b3Smrg return EXIT_FAILURE; 970309d3b3Smrg } 980309d3b3Smrg 990309d3b3Smrg memcpy(data.f, m->m, sizeof(m->m)); 1000309d3b3Smrg 1010309d3b3Smrg XIChangeProperty(dpy, deviceid, prop_matrix, prop_float, 1020309d3b3Smrg format_return, PropModeReplace, data.c, nitems); 1030309d3b3Smrg 1040309d3b3Smrg XFree(data.c); 1050309d3b3Smrg 1060309d3b3Smrg return EXIT_SUCCESS; 1070309d3b3Smrg} 1080309d3b3Smrg 10933734831Smrgstatic void 11033734831Smrgmatrix_s4(Matrix *m, float x02, float x12, float d1, float d2, int main_diag) 11133734831Smrg{ 11233734831Smrg matrix_set(m, 0, 2, x02); 11333734831Smrg matrix_set(m, 1, 2, x12); 11433734831Smrg 11533734831Smrg if (main_diag) { 11633734831Smrg matrix_set(m, 0, 0, d1); 11733734831Smrg matrix_set(m, 1, 1, d2); 11833734831Smrg } else { 11933734831Smrg matrix_set(m, 0, 0, 0); 12033734831Smrg matrix_set(m, 1, 1, 0); 12133734831Smrg matrix_set(m, 0, 1, d1); 12233734831Smrg matrix_set(m, 1, 0, d2); 12333734831Smrg } 12433734831Smrg} 12533734831Smrg 12633734831Smrg#define RR_Reflect_All (RR_Reflect_X|RR_Reflect_Y) 12733734831Smrg 1280309d3b3Smrgstatic void 1290309d3b3Smrgset_transformation_matrix(Display *dpy, Matrix *m, int offset_x, int offset_y, 13033734831Smrg int screen_width, int screen_height, 13133734831Smrg int rotation) 1320309d3b3Smrg{ 13333734831Smrg /* total display size */ 1340309d3b3Smrg int width = DisplayWidth(dpy, DefaultScreen(dpy)); 1350309d3b3Smrg int height = DisplayHeight(dpy, DefaultScreen(dpy)); 1360309d3b3Smrg 1370309d3b3Smrg /* offset */ 1380309d3b3Smrg float x = 1.0 * offset_x/width; 1390309d3b3Smrg float y = 1.0 * offset_y/height; 1400309d3b3Smrg 1410309d3b3Smrg /* mapping */ 1420309d3b3Smrg float w = 1.0 * screen_width/width; 1430309d3b3Smrg float h = 1.0 * screen_height/height; 1440309d3b3Smrg 1450309d3b3Smrg matrix_set_unity(m); 1460309d3b3Smrg 14733734831Smrg /* 14833734831Smrg * There are 16 cases: 14933734831Smrg * Rotation X Reflection 15033734831Smrg * Rotation: 0 | 90 | 180 | 270 15133734831Smrg * Reflection: None | X | Y | XY 15233734831Smrg * 15333734831Smrg * They are spelled out instead of doing matrix multiplication to avoid 15433734831Smrg * any floating point errors. 15533734831Smrg */ 15633734831Smrg switch (rotation) { 15733734831Smrg case RR_Rotate_0: 15833734831Smrg case RR_Rotate_180 | RR_Reflect_All: 15933734831Smrg matrix_s4(m, x, y, w, h, 1); 16033734831Smrg break; 16133734831Smrg case RR_Reflect_X|RR_Rotate_0: 16233734831Smrg case RR_Reflect_Y|RR_Rotate_180: 16333734831Smrg matrix_s4(m, x + w, y, -w, h, 1); 16433734831Smrg break; 16533734831Smrg case RR_Reflect_Y|RR_Rotate_0: 16633734831Smrg case RR_Reflect_X|RR_Rotate_180: 16733734831Smrg matrix_s4(m, x, y + h, w, -h, 1); 16833734831Smrg break; 16933734831Smrg case RR_Rotate_90: 17033734831Smrg case RR_Rotate_270 | RR_Reflect_All: /* left limited - correct in working zone. */ 17133734831Smrg matrix_s4(m, x + w, y, -w, h, 0); 17233734831Smrg break; 17333734831Smrg case RR_Rotate_270: 17433734831Smrg case RR_Rotate_90 | RR_Reflect_All: /* left limited - correct in working zone. */ 17533734831Smrg matrix_s4(m, x, y + h, w, -h, 0); 17633734831Smrg break; 17733734831Smrg case RR_Rotate_90 | RR_Reflect_X: /* left limited - correct in working zone. */ 17833734831Smrg case RR_Rotate_270 | RR_Reflect_Y: /* left limited - correct in working zone. */ 17933734831Smrg matrix_s4(m, x, y, w, h, 0); 18033734831Smrg break; 18133734831Smrg case RR_Rotate_90 | RR_Reflect_Y: /* right limited - correct in working zone. */ 18233734831Smrg case RR_Rotate_270 | RR_Reflect_X: /* right limited - correct in working zone. */ 18333734831Smrg matrix_s4(m, x + w, y + h, -w, -h, 0); 18433734831Smrg break; 18533734831Smrg case RR_Rotate_180: 18633734831Smrg case RR_Reflect_All|RR_Rotate_0: 18733734831Smrg matrix_s4(m, x + w, y + h, -w, -h, 1); 18833734831Smrg break; 18933734831Smrg } 1900309d3b3Smrg 1910309d3b3Smrg#if DEBUG 1920309d3b3Smrg matrix_print(m); 1930309d3b3Smrg#endif 1940309d3b3Smrg} 1950309d3b3Smrg 1960309d3b3Smrg/* Caller must free return value */ 1970309d3b3Smrgstatic XRROutputInfo* 1980309d3b3Smrgfind_output_xrandr(Display *dpy, const char *output_name) 1990309d3b3Smrg{ 2000309d3b3Smrg XRRScreenResources *res; 2010309d3b3Smrg XRROutputInfo *output_info = NULL; 2020309d3b3Smrg int i; 2030309d3b3Smrg int found = 0; 2040309d3b3Smrg 2050309d3b3Smrg res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy)); 2060309d3b3Smrg 2070309d3b3Smrg for (i = 0; i < res->noutput && !found; i++) 2080309d3b3Smrg { 2090309d3b3Smrg output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]); 2100309d3b3Smrg 2110309d3b3Smrg if (output_info->crtc && output_info->connection == RR_Connected && 2120309d3b3Smrg strcmp(output_info->name, output_name) == 0) 2130309d3b3Smrg { 2140309d3b3Smrg found = 1; 2150309d3b3Smrg break; 2160309d3b3Smrg } 2170309d3b3Smrg 2180309d3b3Smrg XRRFreeOutputInfo(output_info); 2190309d3b3Smrg } 2200309d3b3Smrg 2210309d3b3Smrg XRRFreeScreenResources(res); 2220309d3b3Smrg 2230309d3b3Smrg if (!found) 2240309d3b3Smrg output_info = NULL; 2250309d3b3Smrg 2260309d3b3Smrg return output_info; 2270309d3b3Smrg} 2280309d3b3Smrg 2290309d3b3Smrgstatic int 2300309d3b3Smrgmap_output_xrandr(Display *dpy, int deviceid, const char *output_name) 2310309d3b3Smrg{ 2320309d3b3Smrg int rc = EXIT_FAILURE; 2330309d3b3Smrg XRRScreenResources *res; 2340309d3b3Smrg XRROutputInfo *output_info; 2350309d3b3Smrg 2360309d3b3Smrg res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy)); 2370309d3b3Smrg output_info = find_output_xrandr(dpy, output_name); 2380309d3b3Smrg 2390309d3b3Smrg /* crtc holds our screen info, need to compare to actual screen size */ 2400309d3b3Smrg if (output_info) 2410309d3b3Smrg { 2420309d3b3Smrg XRRCrtcInfo *crtc_info; 2430309d3b3Smrg Matrix m; 2440309d3b3Smrg matrix_set_unity(&m); 2450309d3b3Smrg crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc); 2460309d3b3Smrg set_transformation_matrix(dpy, &m, crtc_info->x, crtc_info->y, 24733734831Smrg crtc_info->width, crtc_info->height, 24833734831Smrg crtc_info->rotation); 2490309d3b3Smrg rc = apply_matrix(dpy, deviceid, &m); 2500309d3b3Smrg XRRFreeCrtcInfo(crtc_info); 2510309d3b3Smrg XRRFreeOutputInfo(output_info); 2520309d3b3Smrg } else 2530309d3b3Smrg printf("Unable to find output '%s'. " 2540309d3b3Smrg "Output may not be connected.\n", output_name); 2550309d3b3Smrg 2560309d3b3Smrg XRRFreeScreenResources(res); 2570309d3b3Smrg 2580309d3b3Smrg return rc; 2590309d3b3Smrg} 2600309d3b3Smrg 2610309d3b3Smrgstatic int 2620309d3b3Smrgmap_output_xinerama(Display *dpy, int deviceid, const char *output_name) 2630309d3b3Smrg{ 2640309d3b3Smrg const char *prefix = "HEAD-"; 2650309d3b3Smrg XineramaScreenInfo *screens = NULL; 2660309d3b3Smrg int rc = EXIT_FAILURE; 2670309d3b3Smrg int event, error; 2680309d3b3Smrg int nscreens; 2690309d3b3Smrg int head; 2700309d3b3Smrg Matrix m; 2710309d3b3Smrg 2720309d3b3Smrg if (!XineramaQueryExtension(dpy, &event, &error)) 2730309d3b3Smrg { 2740309d3b3Smrg fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n"); 2750309d3b3Smrg goto out; 2760309d3b3Smrg } 2770309d3b3Smrg 2780309d3b3Smrg if (strlen(output_name) < strlen(prefix) + 1 || 2790309d3b3Smrg strncmp(output_name, prefix, strlen(prefix)) != 0) 2800309d3b3Smrg { 2810309d3b3Smrg fprintf(stderr, "Please specify the output name as HEAD-X," 2820309d3b3Smrg "where X is the screen number\n"); 2830309d3b3Smrg goto out; 2840309d3b3Smrg } 2850309d3b3Smrg 2860309d3b3Smrg head = output_name[strlen(prefix)] - '0'; 2870309d3b3Smrg 2880309d3b3Smrg screens = XineramaQueryScreens(dpy, &nscreens); 2890309d3b3Smrg 2900309d3b3Smrg if (nscreens == 0) 2910309d3b3Smrg { 2920309d3b3Smrg fprintf(stderr, "Xinerama failed to query screens.\n"); 2930309d3b3Smrg goto out; 2940309d3b3Smrg } else if (nscreens <= head) 2950309d3b3Smrg { 2960309d3b3Smrg fprintf(stderr, "Found %d screens, but you requested %s.\n", 2970309d3b3Smrg nscreens, output_name); 2980309d3b3Smrg goto out; 2990309d3b3Smrg } 3000309d3b3Smrg 3010309d3b3Smrg matrix_set_unity(&m); 3020309d3b3Smrg set_transformation_matrix(dpy, &m, 3030309d3b3Smrg screens[head].x_org, screens[head].y_org, 30433734831Smrg screens[head].width, screens[head].height, 30533734831Smrg RR_Rotate_0); 3060309d3b3Smrg rc = apply_matrix(dpy, deviceid, &m); 3070309d3b3Smrg 3080309d3b3Smrgout: 3090309d3b3Smrg XFree(screens); 3100309d3b3Smrg return rc; 3110309d3b3Smrg} 3120309d3b3Smrg 3130309d3b3Smrgint 3140309d3b3Smrgmap_to_output(Display *dpy, int argc, char *argv[], char *name, char *desc) 3150309d3b3Smrg{ 3160309d3b3Smrg char *output_name; 3170309d3b3Smrg XIDeviceInfo *info; 3180309d3b3Smrg XRROutputInfo *output_info; 3190309d3b3Smrg 3200309d3b3Smrg if (argc < 2) 3210309d3b3Smrg { 3220309d3b3Smrg fprintf(stderr, "Usage: xinput %s %s\n", name, desc); 3230309d3b3Smrg return EXIT_FAILURE; 3240309d3b3Smrg } 3250309d3b3Smrg 3260309d3b3Smrg info = xi2_find_device_info(dpy, argv[0]); 3270309d3b3Smrg if (!info) 3280309d3b3Smrg { 3290309d3b3Smrg fprintf(stderr, "unable to find device '%s'\n", argv[0]); 3300309d3b3Smrg return EXIT_FAILURE; 3310309d3b3Smrg } 3320309d3b3Smrg 3330309d3b3Smrg output_name = argv[1]; 33420f5670eSmrg 33520f5670eSmrg if (!strcmp("all", output_name)) 33620f5670eSmrg { 33720f5670eSmrg Matrix m; 33820f5670eSmrg matrix_set_unity(&m); 33920f5670eSmrg return apply_matrix(dpy, info->deviceid, &m); 34020f5670eSmrg } 34120f5670eSmrg 3420309d3b3Smrg output_info = find_output_xrandr(dpy, output_name); 3430309d3b3Smrg if (!output_info) 3440309d3b3Smrg { 3450309d3b3Smrg /* Output doesn't exist. Is this a (partial) non-RandR setup? */ 3460309d3b3Smrg output_info = find_output_xrandr(dpy, "default"); 3470309d3b3Smrg if (output_info) 3480309d3b3Smrg { 3490309d3b3Smrg XRRFreeOutputInfo(output_info); 3500309d3b3Smrg if (strncmp("HEAD-", output_name, strlen("HEAD-")) == 0) 3510309d3b3Smrg return map_output_xinerama(dpy, info->deviceid, output_name); 3520309d3b3Smrg } 3530309d3b3Smrg } else 3540309d3b3Smrg XRRFreeOutputInfo(output_info); 3550309d3b3Smrg 3560309d3b3Smrg return map_output_xrandr(dpy, info->deviceid, output_name); 3570309d3b3Smrg} 358