Tizen 2.0 Release
[framework/uifw/xorg/util/x11-xserver-utils.git] / 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 /* Caller must free return value */
139 static XRROutputInfo*
140 find_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
171 static int
172 map_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
202 static int
203 map_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
248 out:
249     XFree(screens);
250     return rc;
251 }
252
253 int
254 map_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 }