A generic touchscreen calibration program for X.Org
Revision | cc54f8ec926ac223c685ff4d8adfd87f10a0cafa (tree) |
---|---|
Time | 2010-01-08 08:07:46 |
Author | Tias Guns <tias@ulys...> |
Commiter | Tias Guns |
Rewrite: split the one-huge-file into clean components
@@ -1,4 +1,7 @@ | ||
1 | -all: xinput_calibrator | |
1 | +all: xinput_calibrator.old xinput_calibrator.gtkmm | |
2 | 2 | |
3 | -xinput_calibrator: xinput_calibrator.cc | |
4 | - g++ -Wall xinput_calibrator.cc `pkg-config --cflags --libs gtkmm-2.4` -o xinput_calibrator | |
3 | +xinput_calibrator.old: xinput_calibrator.cc | |
4 | + g++ -Wall xinput_calibrator.cc `pkg-config --cflags --libs gtkmm-2.4` -o xinput_calibrator.old | |
5 | + | |
6 | +xinput_calibrator.gtkmm: main_gtkmm.cpp gui_gtkmm.cpp | |
7 | + g++ -Wall main_gtkmm.cpp `pkg-config --cflags --libs gtkmm-2.4` -o xinput_calibrator.gtkmm |
@@ -0,0 +1,88 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Tias Guns | |
3 | + * Copyright (c) 2009 Soren Hauberg | |
4 | + * | |
5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | + * of this software and associated documentation files (the "Software"), to deal | |
7 | + * in the Software without restriction, including without limitation the rights | |
8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | + * copies of the Software, and to permit persons to whom the Software is | |
10 | + * furnished to do so, subject to the following conditions: | |
11 | + * | |
12 | + * The above copyright notice and this permission notice shall be included in | |
13 | + * all copies or substantial portions of the 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 THE | |
18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | + * THE SOFTWARE. | |
22 | + */ | |
23 | +#include <algorithm> | |
24 | +#include "calibrator.hh" | |
25 | + | |
26 | +Calibrator::Calibrator(const char* const drivername0, const XYinfo& axys0) | |
27 | + : drivername(drivername0), old_axys(axys0), num_clicks(0) | |
28 | +{ | |
29 | +} | |
30 | + | |
31 | +int Calibrator::get_numclicks() | |
32 | +{ | |
33 | + return num_clicks; | |
34 | +} | |
35 | + | |
36 | +bool Calibrator::add_click(double x, double y) | |
37 | +{ | |
38 | + // Check that we don't click the same point twice | |
39 | + if (num_clicks > 0 && click_threshold > 0 | |
40 | + && abs (x - clicked_x[num_clicks-1]) < click_threshold | |
41 | + && abs (y - clicked_y[num_clicks-1]) < click_threshold) | |
42 | + return false; | |
43 | + | |
44 | + clicked_x[num_clicks] = x; | |
45 | + clicked_y[num_clicks] = y; | |
46 | + num_clicks ++; | |
47 | + | |
48 | + return true; | |
49 | +} | |
50 | + | |
51 | +void Calibrator::finish(int width, int height) | |
52 | +{ | |
53 | + // Should x and y be swapped? | |
54 | + const bool swap_xy = (abs (clicked_x [UL] - clicked_x [UR]) < abs (clicked_y [UL] - clicked_y [UR])); | |
55 | + if (swap_xy) { | |
56 | + std::swap(clicked_x[LL], clicked_x[UR]); | |
57 | + std::swap(clicked_y[LL], clicked_y[UR]); | |
58 | + } | |
59 | + | |
60 | + // Compute min/max coordinates. | |
61 | + XYinfo axys; | |
62 | + // These are scaled using the values of old_axys | |
63 | + const float scale_x = (old_axys.x_max - old_axys.x_min)/(float)width; | |
64 | + axys.x_min = ((clicked_x[UL] + clicked_x[LL]) * scale_x/2) + old_axys.x_min; | |
65 | + axys.x_max = ((clicked_x[UR] + clicked_x[LR]) * scale_x/2) + old_axys.x_min; | |
66 | + const float scale_y = (old_axys.y_max - old_axys.y_min)/(float)height; | |
67 | + axys.y_min = ((clicked_y[UL] + clicked_y[UR]) * scale_y/2) + old_axys.y_min; | |
68 | + axys.y_max = ((clicked_y[LL] + clicked_y[LR]) * scale_y/2) + old_axys.y_min; | |
69 | + | |
70 | + // Add/subtract the offset that comes from not having the points in the | |
71 | + // corners (using the same coordinate system they are currently in) | |
72 | + const int delta_x = (axys.x_max - axys.x_min) / (float)(num_blocks - 2); | |
73 | + axys.x_min -= delta_x; | |
74 | + axys.x_max += delta_x; | |
75 | + const int delta_y = (axys.y_max - axys.y_min) / (float)(num_blocks - 2); | |
76 | + axys.y_min -= delta_y; | |
77 | + axys.y_max += delta_y; | |
78 | + | |
79 | + | |
80 | + // If x and y has to be swapped we also have to swap the parameters | |
81 | + if (swap_xy) { | |
82 | + std::swap(axys.x_min, axys.y_max); | |
83 | + std::swap(axys.y_min, axys.x_max); | |
84 | + } | |
85 | + | |
86 | + // finish the data, driver specific | |
87 | + finish_data(axys, swap_xy); | |
88 | +} |
@@ -0,0 +1,47 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Tias Guns | |
3 | + * Copyright (c) 2009 Soren Hauberg | |
4 | + * | |
5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | + * of this software and associated documentation files (the "Software"), to deal | |
7 | + * in the Software without restriction, including without limitation the rights | |
8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | + * copies of the Software, and to permit persons to whom the Software is | |
10 | + * furnished to do so, subject to the following conditions: | |
11 | + * | |
12 | + * The above copyright notice and this permission notice shall be included in | |
13 | + * all copies or substantial portions of the 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 THE | |
18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | + * THE SOFTWARE. | |
22 | + */ | |
23 | + | |
24 | +#ifndef _calibrator_hh | |
25 | +#define _calibrator_hh | |
26 | + | |
27 | +// Abstract base class for calculating new calibration parameters | |
28 | +class Calibrator | |
29 | +{ | |
30 | +public: | |
31 | + Calibrator(const char* const drivername, const XYinfo& axys); | |
32 | + ~Calibrator() {} | |
33 | + | |
34 | + int get_numclicks(); | |
35 | + bool add_click(double x, double y); | |
36 | + void finish(int width, int height); | |
37 | + | |
38 | +protected: | |
39 | + virtual void finish_data(const XYinfo new_axys, int swap_xy) =0; | |
40 | + | |
41 | + const char* const drivername; | |
42 | + XYinfo old_axys; | |
43 | + int num_clicks; | |
44 | + double clicked_x[4], clicked_y[4]; | |
45 | +}; | |
46 | + | |
47 | +#endif |
@@ -0,0 +1,399 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Tias Guns | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | + * of this software and associated documentation files (the "Software"), to deal | |
6 | + * in the Software without restriction, including without limitation the rights | |
7 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | + * copies of the Software, and to permit persons to whom the Software is | |
9 | + * furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | + * THE SOFTWARE. | |
21 | + */ | |
22 | +#include <ctype.h> | |
23 | + | |
24 | +#include <X11/Xlib.h> | |
25 | +#include <X11/extensions/XInput.h> | |
26 | +#include <X11/Xatom.h> | |
27 | +//#include <X11/Xutil.h> | |
28 | + | |
29 | +#ifndef EXIT_SUCCESS | |
30 | +#define EXIT_SUCCESS 1 | |
31 | +#endif | |
32 | +#ifndef EXIT_FAILURE | |
33 | +#define EXIT_FAILURE 0 | |
34 | +#endif | |
35 | + | |
36 | +/*************************************** | |
37 | + * Class for dynamic evdev calibration | |
38 | + * uses xinput "Evdev Axis Calibration" | |
39 | + ***************************************/ | |
40 | +class CalibratorEvdev: public Calibrator | |
41 | +{ | |
42 | +private: | |
43 | + Display *display; | |
44 | + XDeviceInfo *info; | |
45 | + XDevice *dev; | |
46 | +public: | |
47 | + CalibratorEvdev(const char* const drivername, const XYinfo& axys); | |
48 | + ~CalibratorEvdev(); | |
49 | + | |
50 | + virtual void finish_data(const XYinfo new_axys, int swap_xy); | |
51 | + | |
52 | + static Bool check_driver(const char* const name); | |
53 | + | |
54 | + // xinput functions (from the xinput source) | |
55 | + static Atom parse_atom(Display *display, const char* name); | |
56 | + static XDeviceInfo* find_device_info(Display *display, const char* name, Bool only_extended); | |
57 | + int do_set_prop(Display *display, Atom type, int format, int argc, char* argv[]); | |
58 | +}; | |
59 | + | |
60 | +CalibratorEvdev::CalibratorEvdev(const char* const drivername0, const XYinfo& axys0) | |
61 | + : Calibrator(drivername0, axys0) | |
62 | +{ | |
63 | + printf("Calibrating EVDEV driver for \"%s\"\n", drivername); | |
64 | + | |
65 | + // init | |
66 | + display = XOpenDisplay(NULL); | |
67 | + if (display == NULL) { | |
68 | + fprintf(stderr, "Unable to connect to X server\n"); | |
69 | + return; | |
70 | + } | |
71 | + | |
72 | + info = find_device_info(display, drivername, False); | |
73 | + if (!info) { | |
74 | + fprintf(stderr, "unable to find device %s\n", drivername); | |
75 | + return; | |
76 | + } | |
77 | + | |
78 | + dev = XOpenDevice(display, info->id); | |
79 | + if (!dev) { | |
80 | + fprintf(stderr, "unable to open device '%s'\n", info->name); | |
81 | + return; | |
82 | + } | |
83 | + | |
84 | + // get "Evdev Axis Calibration" property | |
85 | + Atom property = parse_atom(display, "Evdev Axis Calibration"); | |
86 | + Atom act_type; | |
87 | + int act_format; | |
88 | + unsigned long nitems, bytes_after; | |
89 | + unsigned char *data, *ptr; | |
90 | + if (XGetDeviceProperty(display, dev, property, 0, 1000, False, | |
91 | + AnyPropertyType, &act_type, &act_format, | |
92 | + &nitems, &bytes_after, &data) == Success) | |
93 | + { | |
94 | + if (act_format != 32 || act_type != XA_INTEGER) { | |
95 | + fprintf(stderr, "Error: unexpected format or type from \"Evdev Axis Calibration\" property.\n"); | |
96 | + } | |
97 | + | |
98 | + if (nitems != 0) { | |
99 | + ptr = data; | |
100 | + | |
101 | + old_axys.x_min = *((long*)ptr); | |
102 | + ptr += sizeof(long); | |
103 | + old_axys.x_max = *((long*)ptr); | |
104 | + ptr += sizeof(long); | |
105 | + old_axys.y_min = *((long*)ptr); | |
106 | + ptr += sizeof(long); | |
107 | + old_axys.y_max = *((long*)ptr); | |
108 | + ptr += sizeof(long); | |
109 | + | |
110 | + printf("Read current calibration data from XInput: min_x=%d, max_x=%d and min_y=%d, max_y=%d\n", | |
111 | + old_axys.x_min, old_axys.x_max, old_axys.y_min, old_axys.y_max); | |
112 | + } | |
113 | + | |
114 | + XFree(data); | |
115 | + } else | |
116 | + printf("\tFetch failure for 'Evdev Axis Calibration', continuing nonetheless\n"); | |
117 | +} | |
118 | + | |
119 | +CalibratorEvdev::~CalibratorEvdev () { | |
120 | + XCloseDevice(display, dev); | |
121 | + XCloseDisplay(display); | |
122 | +} | |
123 | + | |
124 | +void CalibratorEvdev::finish_data(const XYinfo new_axys, int swap_xy) | |
125 | +{ | |
126 | + printf("\nTo make the settings permanent, create add a startup script for your window manager with the following command(s):\n"); | |
127 | + if (swap_xy) | |
128 | + printf(" xinput set-int-prop \"%s\" \"Evdev Axes Swap\" 8 %d\n", drivername, swap_xy); | |
129 | + printf(" xinput set-int-prop \"%s\" \"Evdev Axis Calibration\" 32 %d %d %d %d\n", drivername, new_axys.x_min, new_axys.x_max, new_axys.y_min, new_axys.y_max); | |
130 | + | |
131 | + printf("\nDoing dynamic recalibration:\n"); | |
132 | + // Evdev Axes Swap | |
133 | + if (swap_xy) { | |
134 | + printf("\tSwapping X and Y axis...\n"); | |
135 | + // xinput set-int-prop "divername" "Evdev Axes Swap" 8 0 | |
136 | + char* arr_cmd[3]; | |
137 | + //arr_cmd[0] = ""; | |
138 | + char str_prop[50]; | |
139 | + sprintf(str_prop, "Evdev Axes Swap"); | |
140 | + arr_cmd[1] = str_prop; | |
141 | + char str_swap_xy[20]; | |
142 | + sprintf(str_swap_xy, "%d", swap_xy); | |
143 | + arr_cmd[2] = str_swap_xy; | |
144 | + | |
145 | + do_set_prop(display, XA_INTEGER, 8, 3, arr_cmd); | |
146 | + } | |
147 | + | |
148 | + // Evdev Axis Calibration | |
149 | + printf("\tSetting new calibration data: %d, %d, %d, %d\n", new_axys.x_min, new_axys.x_max, new_axys.y_min, new_axys.y_max); | |
150 | + // xinput set-int-prop 4 223 32 5 500 8 300 | |
151 | + char* arr_cmd[6]; | |
152 | + //arr_cmd[0] = ""; | |
153 | + char str_prop[50]; | |
154 | + sprintf(str_prop, "Evdev Axis Calibration"); | |
155 | + arr_cmd[1] = str_prop; | |
156 | + char str_min_x[20]; | |
157 | + sprintf(str_min_x, "%d", new_axys.x_min); | |
158 | + arr_cmd[2] = str_min_x; | |
159 | + char str_max_x[20]; | |
160 | + sprintf(str_max_x, "%d", new_axys.x_max); | |
161 | + arr_cmd[3] = str_max_x; | |
162 | + char str_min_y[20]; | |
163 | + sprintf(str_min_y, "%d", new_axys.y_min); | |
164 | + arr_cmd[4] = str_min_y; | |
165 | + char str_max_y[20]; | |
166 | + sprintf(str_max_y, "%d", new_axys.y_max); | |
167 | + arr_cmd[5] = str_max_y; | |
168 | + | |
169 | + do_set_prop(display, XA_INTEGER, 32, 6, arr_cmd); | |
170 | + | |
171 | + // close | |
172 | + XSync(display, False); | |
173 | +} | |
174 | + | |
175 | +Bool CalibratorEvdev::check_driver(const char *name) { | |
176 | + Display *display; | |
177 | + XDeviceInfo *info; | |
178 | + XDevice *dev; | |
179 | + int nprops; | |
180 | + Atom *props; | |
181 | + Bool found = False; | |
182 | + | |
183 | + display = XOpenDisplay(NULL); | |
184 | + if (display == NULL) { | |
185 | + //fprintf(stderr, "Unable to connect to X server"); | |
186 | + return false; | |
187 | + } | |
188 | + | |
189 | + info = find_device_info(display, name, False); | |
190 | + if (!info) | |
191 | + { | |
192 | + XCloseDisplay(display); | |
193 | + //fprintf(stderr, "unable to find device %s\n", name); | |
194 | + return false; | |
195 | + } | |
196 | + | |
197 | + dev = XOpenDevice(display, info->id); | |
198 | + if (!dev) | |
199 | + { | |
200 | + XCloseDisplay(display); | |
201 | + //fprintf(stderr, "unable to open device '%s'\n", info->name); | |
202 | + return false; | |
203 | + } | |
204 | + | |
205 | + props = XListDeviceProperties(display, dev, &nprops); | |
206 | + if (!nprops) | |
207 | + { | |
208 | + XCloseDevice(display, dev); | |
209 | + XCloseDisplay(display); | |
210 | + //printf("Device '%s' does not report any properties.\n", info->name); | |
211 | + return false; | |
212 | + } | |
213 | + | |
214 | + // check if "Evdev Axis Calibration" exists, then its an evdev driver | |
215 | + while(nprops--) | |
216 | + { | |
217 | + if (strcmp(XGetAtomName(display, props[nprops]), | |
218 | + "Evdev Axis Calibration") == 0) { | |
219 | + found = True; | |
220 | + break; | |
221 | + } | |
222 | + } | |
223 | + | |
224 | + XFree(props); | |
225 | + XCloseDevice(display, dev); | |
226 | + XCloseDisplay(display); | |
227 | + | |
228 | + return found; | |
229 | +} | |
230 | + | |
231 | +Atom CalibratorEvdev::parse_atom(Display *display, const char *name) { | |
232 | + Bool is_atom = True; | |
233 | + int i; | |
234 | + | |
235 | + for (i = 0; name[i] != '\0'; i++) { | |
236 | + if (!isdigit(name[i])) { | |
237 | + is_atom = False; | |
238 | + break; | |
239 | + } | |
240 | + } | |
241 | + | |
242 | + if (is_atom) | |
243 | + return atoi(name); | |
244 | + else | |
245 | + return XInternAtom(display, name, False); | |
246 | +} | |
247 | + | |
248 | +XDeviceInfo* CalibratorEvdev::find_device_info( | |
249 | +Display *display, const char *name, Bool only_extended) | |
250 | +{ | |
251 | + XDeviceInfo *devices; | |
252 | + XDeviceInfo *found = NULL; | |
253 | + int loop; | |
254 | + int num_devices; | |
255 | + int len = strlen(name); | |
256 | + Bool is_id = True; | |
257 | + XID id = (XID)-1; | |
258 | + | |
259 | + for (loop=0; loop<len; loop++) { | |
260 | + if (!isdigit(name[loop])) { | |
261 | + is_id = False; | |
262 | + break; | |
263 | + } | |
264 | + } | |
265 | + | |
266 | + if (is_id) { | |
267 | + id = atoi(name); | |
268 | + } | |
269 | + | |
270 | + devices = XListInputDevices(display, &num_devices); | |
271 | + | |
272 | + for (loop=0; loop<num_devices; loop++) { | |
273 | + if ((!only_extended || (devices[loop].use >= IsXExtensionDevice)) && | |
274 | + ((!is_id && strcmp(devices[loop].name, name) == 0) || | |
275 | + (is_id && devices[loop].id == id))) { | |
276 | + if (found) { | |
277 | + fprintf(stderr, | |
278 | + "Warning: There are multiple devices named \"%s\".\n" | |
279 | + "To ensure the correct one is selected, please use " | |
280 | + "the device ID instead.\n\n", name); | |
281 | + return NULL; | |
282 | + } else { | |
283 | + found = &devices[loop]; | |
284 | + } | |
285 | + } | |
286 | + } | |
287 | + | |
288 | + return found; | |
289 | +} | |
290 | + | |
291 | +int CalibratorEvdev::do_set_prop( | |
292 | +Display *display, Atom type, int format, int argc, char **argv) | |
293 | +{ | |
294 | + Atom prop; | |
295 | + Atom old_type; | |
296 | + char *name; | |
297 | + int i; | |
298 | + Atom float_atom; | |
299 | + int old_format, nelements = 0; | |
300 | + unsigned long act_nitems, bytes_after; | |
301 | + char *endptr; | |
302 | + union { | |
303 | + unsigned char *c; | |
304 | + short *s; | |
305 | + long *l; | |
306 | + Atom *a; | |
307 | + } data; | |
308 | + | |
309 | + if (argc < 3) | |
310 | + { | |
311 | + fprintf(stderr, "Wrong usage of do_set_prop, need at least 3 arguments\n"); | |
312 | + return EXIT_FAILURE; | |
313 | + } | |
314 | + | |
315 | + name = argv[1]; | |
316 | + | |
317 | + prop = parse_atom(display, name); | |
318 | + | |
319 | + if (prop == None) { | |
320 | + fprintf(stderr, "invalid property %s\n", name); | |
321 | + return EXIT_FAILURE; | |
322 | + } | |
323 | + | |
324 | + float_atom = XInternAtom(display, "FLOAT", False); | |
325 | + | |
326 | + nelements = argc - 2; | |
327 | + if (type == None || format == 0) { | |
328 | + if (XGetDeviceProperty(display, dev, prop, 0, 0, False, AnyPropertyType, | |
329 | + &old_type, &old_format, &act_nitems, | |
330 | + &bytes_after, &data.c) != Success) { | |
331 | + fprintf(stderr, "failed to get property type and format for %s\n", | |
332 | + name); | |
333 | + return EXIT_FAILURE; | |
334 | + } else { | |
335 | + if (type == None) | |
336 | + type = old_type; | |
337 | + if (format == 0) | |
338 | + format = old_format; | |
339 | + } | |
340 | + | |
341 | + XFree(data.c); | |
342 | + } | |
343 | + | |
344 | + if (type == None) { | |
345 | + fprintf(stderr, "property %s doesn't exist, you need to specify " | |
346 | + "its type and format\n", name); | |
347 | + return EXIT_FAILURE; | |
348 | + } | |
349 | + | |
350 | + data.c = (unsigned char*)calloc(nelements, sizeof(long)); | |
351 | + | |
352 | + for (i = 0; i < nelements; i++) | |
353 | + { | |
354 | + if (type == XA_INTEGER) { | |
355 | + switch (format) | |
356 | + { | |
357 | + case 8: | |
358 | + data.c[i] = atoi(argv[2 + i]); | |
359 | + break; | |
360 | + case 16: | |
361 | + data.s[i] = atoi(argv[2 + i]); | |
362 | + break; | |
363 | + case 32: | |
364 | + data.l[i] = atoi(argv[2 + i]); | |
365 | + break; | |
366 | + default: | |
367 | + fprintf(stderr, "unexpected size for property %s", name); | |
368 | + return EXIT_FAILURE; | |
369 | + } | |
370 | + } else if (type == float_atom) { | |
371 | + if (format != 32) { | |
372 | + fprintf(stderr, "unexpected format %d for property %s\n", | |
373 | + format, name); | |
374 | + return EXIT_FAILURE; | |
375 | + } | |
376 | + *(float *)(data.l + i) = strtod(argv[2 + i], &endptr); | |
377 | + if (endptr == argv[2 + i]) { | |
378 | + fprintf(stderr, "argument %s could not be parsed\n", argv[2 + i]); | |
379 | + return EXIT_FAILURE; | |
380 | + } | |
381 | + } else if (type == XA_ATOM) { | |
382 | + if (format != 32) { | |
383 | + fprintf(stderr, "unexpected format %d for property %s\n", | |
384 | + format, name); | |
385 | + return EXIT_FAILURE; | |
386 | + } | |
387 | + data.a[i] = parse_atom(display, argv[2 + i]); | |
388 | + } else { | |
389 | + fprintf(stderr, "unexpected type for property %s\n", name); | |
390 | + return EXIT_FAILURE; | |
391 | + } | |
392 | + } | |
393 | + | |
394 | + XChangeDeviceProperty(display, dev, prop, type, format, PropModeReplace, | |
395 | + data.c, nelements); | |
396 | + free(data.c); | |
397 | + XCloseDevice(display, dev); | |
398 | + return EXIT_SUCCESS; | |
399 | +} |
@@ -0,0 +1,218 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Soren Hauberg | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | + * of this software and associated documentation files (the "Software"), to deal | |
6 | + * in the Software without restriction, including without limitation the rights | |
7 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | + * copies of the Software, and to permit persons to whom the Software is | |
9 | + * furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | + * THE SOFTWARE. | |
21 | + */ | |
22 | +#include <string> | |
23 | + | |
24 | +/************************* | |
25 | + * Variables for usbtouchscreen specifically | |
26 | + *************************/ | |
27 | +// The file to which the calibration parameters are saved. | |
28 | +// (XXX: is this distribution dependend?) | |
29 | +const char *modprobe_conf_local = "/etc/modprobe.conf.local"; | |
30 | + | |
31 | +// Prefix to the kernel path where we can set the parameters | |
32 | +const char *module_prefix = "/sys/module/usbtouchscreen/parameters"; | |
33 | + | |
34 | +// Names of kernel parameters | |
35 | +const char *p_range_x = "range_x"; | |
36 | +const char *p_range_y = "range_y"; | |
37 | +const char *p_min_x = "min_x"; | |
38 | +const char *p_min_y = "min_y"; | |
39 | +const char *p_max_x = "max_x"; | |
40 | +const char *p_max_y = "max_y"; | |
41 | +const char *p_transform_xy = "transform_xy"; | |
42 | +const char *p_flip_x = "flip_x"; | |
43 | +const char *p_flip_y = "flip_y"; | |
44 | +const char *p_swap_xy = "swap_xy"; | |
45 | + | |
46 | + | |
47 | +/********************************** | |
48 | + * Class for usbtouchscreen driver, | |
49 | + * writes output parameters to running kernel and to modprobe.conf | |
50 | + **********************************/ | |
51 | +class CalibratorUsbtouchscreen: public Calibrator | |
52 | +{ | |
53 | +public: | |
54 | + CalibratorUsbtouchscreen(const char* const drivername, const XYinfo& axys); | |
55 | + ~CalibratorUsbtouchscreen(); | |
56 | + | |
57 | + virtual void finish_data(const XYinfo new_axys, int swap_xy); | |
58 | + | |
59 | +protected: | |
60 | + // Globals for kernel parameters from startup. | |
61 | + // We revert to these if the program aborts | |
62 | + bool val_transform_xy, val_flip_x, val_flip_y, val_swap_xy; | |
63 | + | |
64 | + // Helper functions | |
65 | + char yesno(const bool value) | |
66 | + { | |
67 | + if (value) | |
68 | + return 'Y'; | |
69 | + else | |
70 | + return 'N'; | |
71 | + } | |
72 | + | |
73 | + void read_int_parameter(const char *param, int &value) | |
74 | + { | |
75 | + char filename[100]; | |
76 | + sprintf(filename, "%s/%s", module_prefix, param); | |
77 | + FILE *fid = fopen(filename, "r"); | |
78 | + if (fid == NULL) { | |
79 | + fprintf(stderr, "Could not read parameter '%s'\n", param); | |
80 | + return; | |
81 | + } | |
82 | + | |
83 | + fscanf(fid, "%d", &value); | |
84 | + fclose(fid); | |
85 | + } | |
86 | + | |
87 | + void read_bool_parameter(const char *param, bool &value) | |
88 | + { | |
89 | + char filename[100]; | |
90 | + sprintf(filename, "%s/%s", module_prefix, param); | |
91 | + FILE *fid = fopen(filename, "r"); | |
92 | + if (fid == NULL) { | |
93 | + fprintf(stderr, "Could not read parameter '%s'\n", param); | |
94 | + return; | |
95 | + } | |
96 | + | |
97 | + char val[3]; | |
98 | + fgets(val, 2, fid); | |
99 | + fclose(fid); | |
100 | + | |
101 | + value = (val[0] == yesno(true)); | |
102 | + } | |
103 | + | |
104 | + void write_int_parameter(const char *param, const int value) | |
105 | + { | |
106 | + char filename[100]; | |
107 | + sprintf(filename, "%s/%s", module_prefix, param); | |
108 | + FILE *fid = fopen(filename, "w"); | |
109 | + if (fid == NULL) { | |
110 | + fprintf(stderr, "Could not save parameter '%s'\n", param); | |
111 | + return; | |
112 | + } | |
113 | + | |
114 | + fprintf(fid, "%d", value); | |
115 | + fclose(fid); | |
116 | + } | |
117 | + | |
118 | + void write_bool_parameter(const char *param, const bool value) | |
119 | + { | |
120 | + char filename[100]; | |
121 | + sprintf(filename, "%s/%s", module_prefix, param); | |
122 | + FILE *fid = fopen(filename, "w"); | |
123 | + if (fid == NULL) { | |
124 | + fprintf(stderr, "Could not save parameter '%s'\n", param); | |
125 | + return; | |
126 | + } | |
127 | + | |
128 | + fprintf(fid, "%c", yesno (value)); | |
129 | + fclose(fid); | |
130 | + } | |
131 | +}; | |
132 | + | |
133 | +CalibratorUsbtouchscreen::CalibratorUsbtouchscreen(const char* const drivername0, const XYinfo& axys0) | |
134 | + : Calibrator(drivername0, axys0) | |
135 | +{ | |
136 | + // Reset the currently running kernel | |
137 | + read_bool_parameter(p_transform_xy, val_transform_xy); | |
138 | + read_bool_parameter(p_flip_x, val_flip_x); | |
139 | + read_bool_parameter(p_flip_y, val_flip_y); | |
140 | + read_bool_parameter(p_swap_xy, val_swap_xy); | |
141 | + | |
142 | + write_bool_parameter(p_transform_xy, false); | |
143 | + write_bool_parameter(p_flip_x, false); | |
144 | + write_bool_parameter(p_flip_y, false); | |
145 | + write_bool_parameter(p_swap_xy, false); | |
146 | +} | |
147 | + | |
148 | +CalibratorUsbtouchscreen::~CalibratorUsbtouchscreen() | |
149 | +{ | |
150 | + // Dirty exit, so we restore the parameters of the running kernel | |
151 | + write_bool_parameter (p_transform_xy, val_transform_xy); | |
152 | + write_bool_parameter (p_flip_x, val_flip_x); | |
153 | + write_bool_parameter (p_flip_y, val_flip_y); | |
154 | + write_bool_parameter (p_swap_xy, val_swap_xy); | |
155 | +} | |
156 | + | |
157 | +void CalibratorUsbtouchscreen::finish_data(const XYinfo new_axys, int swap_xy) | |
158 | +{ | |
159 | + // New ranges | |
160 | + const int range_x = (new_axys.x_max - new_axys.x_min); | |
161 | + const int range_y = (new_axys.y_max - new_axys.y_min); | |
162 | + // Should x and y be flipped ? | |
163 | + const bool flip_x = (new_axys.x_min > new_axys.x_max); | |
164 | + const bool flip_y = (new_axys.y_min > new_axys.y_max); | |
165 | + | |
166 | + // Send the estimated parameters to the currently running kernel | |
167 | + write_int_parameter(p_range_x, range_x); | |
168 | + write_int_parameter(p_range_y, range_y); | |
169 | + write_int_parameter(p_min_x, new_axys.x_min); | |
170 | + write_int_parameter(p_max_x, new_axys.x_max); | |
171 | + write_int_parameter(p_min_y, new_axys.y_min); | |
172 | + write_int_parameter(p_max_y, new_axys.y_max); | |
173 | + write_bool_parameter(p_transform_xy, true); | |
174 | + write_bool_parameter(p_flip_x, flip_x); | |
175 | + write_bool_parameter(p_flip_y, flip_y); | |
176 | + write_bool_parameter(p_swap_xy, swap_xy); | |
177 | + | |
178 | + // Read, then write calibration parameters to modprobe_conf_local, | |
179 | + // to keep the for the next boot | |
180 | + FILE *fid = fopen(modprobe_conf_local, "r"); | |
181 | + if (fid == NULL) { | |
182 | + fprintf(stderr, "Error: Can't open '%s' for reading. Make sure you have the necesary rights\n", modprobe_conf_local); | |
183 | + fprintf(stderr, "New calibration data NOT saved\n"); | |
184 | + return; | |
185 | + } | |
186 | + | |
187 | + std::string new_contents; | |
188 | + const int len = 1024; // XXX: we currently don't handle lines that are longer than this | |
189 | + char line[len]; | |
190 | + const char *opt = "options usbtouchscreen"; | |
191 | + const int opt_len = strlen(opt); | |
192 | + while (fgets(line, len, fid)) { | |
193 | + if (strncmp(line, opt, opt_len) == 0) { | |
194 | + // This is the line we want to remove | |
195 | + continue; | |
196 | + } | |
197 | + new_contents += line; | |
198 | + } | |
199 | + fclose(fid); | |
200 | + | |
201 | + char new_opt[opt_len]; | |
202 | + sprintf(new_opt, "%s %s=%d %s=%d %s=%d %s=%d %s=%d %s=%d %s=%c %s=%c %s=%c %s=%c\n", | |
203 | + opt, p_range_x, range_x, p_range_y, range_y, | |
204 | + p_min_x, new_axys.x_min, p_min_y, new_axys.y_min, | |
205 | + p_max_x, new_axys.x_max, p_max_y, new_axys.y_max, | |
206 | + p_transform_xy, yesno(true), p_flip_x, yesno(flip_x), | |
207 | + p_flip_y, yesno(flip_y), p_swap_xy, yesno(swap_xy)); | |
208 | + new_contents += new_opt; | |
209 | + | |
210 | + fid = fopen(modprobe_conf_local, "w"); | |
211 | + if (fid == NULL) { | |
212 | + fprintf(stderr, "Error: Can't open '%s' for writing. Make sure you have the necesary rights\n", modprobe_conf_local); | |
213 | + fprintf(stderr, "New calibration data NOT saved\n"); | |
214 | + return; | |
215 | + } | |
216 | + fprintf(fid, "%s", new_contents.c_str ()); | |
217 | + fclose(fid); | |
218 | +} |
@@ -0,0 +1,70 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Tias Guns | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | + * of this software and associated documentation files (the "Software"), to deal | |
6 | + * in the Software without restriction, including without limitation the rights | |
7 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | + * copies of the Software, and to permit persons to whom the Software is | |
9 | + * furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | + * THE SOFTWARE. | |
21 | + */ | |
22 | + | |
23 | +/*************************************** | |
24 | + * Class for generic Xorg driver, | |
25 | + * outputs new Xorg.conf and FDI policy, on stdout | |
26 | + ***************************************/ | |
27 | +class CalibratorXorgPrint: public Calibrator | |
28 | +{ | |
29 | +public: | |
30 | + CalibratorXorgPrint(const char* const drivername, const XYinfo& axys); | |
31 | + | |
32 | + virtual void finish_data(const XYinfo new_axys, int swap_xy); | |
33 | +}; | |
34 | + | |
35 | +CalibratorXorgPrint::CalibratorXorgPrint(const char* const drivername0, const XYinfo& axys0) | |
36 | + : Calibrator(drivername0, axys0) | |
37 | +{ | |
38 | + printf ("Calibrating Xorg driver \"%s\" (currently having min_x=%d, max_x=%d and min_y=%d, max_y=%d)\n", | |
39 | + drivername, old_axys.x_min, old_axys.x_max, | |
40 | + old_axys.y_min, old_axys.y_max); | |
41 | + printf("\tIf the current calibration data is estimated wrong then either supply it manually with --precalib <minx> <maxx> <miny> <maxy> or run the 'get_precalib.sh' script to automatically get it from your current Xorg configuration (through hal).\n"); | |
42 | +} | |
43 | + | |
44 | +void CalibratorXorgPrint::finish_data(const XYinfo new_axys, int swap_xy) | |
45 | +{ | |
46 | + // FDI policy output | |
47 | + printf("\nNew method for making the calibration permanent: create an FDI policy file like /etc/hal/fdi/policy/touchscreen.fdi with:\n\ | |
48 | +<match key=\"info.product\" contains=\"%%Name_Of_TouchScreen%%\">\n\ | |
49 | + <merge key=\"input.x11_options.minx\" type=\"string\">%d</merge>\n\ | |
50 | + <merge key=\"input.x11_options.miny\" type=\"string\">%d</merge>\n\ | |
51 | + <merge key=\"input.x11_options.maxx\" type=\"string\">%d</merge>\n\ | |
52 | + <merge key=\"input.x11_options.maxy\" type=\"string\">%d</merge>\n" | |
53 | + , new_axys.x_min, new_axys.x_max, new_axys.y_min, new_axys.y_max); | |
54 | + if (swap_xy != 0) | |
55 | + printf(" <merge key=\"input.x11_options.swapxy\" type=\"string\">%d</merge>\n", swap_xy); | |
56 | + printf("</match>\n"); | |
57 | + | |
58 | + // Xorg.conf output | |
59 | + printf("\nOld method, edit /etc/X11/xorg.conf and add in the 'Section \"Device\"' of your touchscreen device:\n"); | |
60 | + printf("\tOption\t\"MinX\"\t\t\"%d\"\t# was \"%d\"\n", | |
61 | + new_axys.x_min, old_axys.x_min); | |
62 | + printf("\tOption\t\"MaxX\"\t\t\"%d\"\t# was \"%d\"\n", | |
63 | + new_axys.x_max, old_axys.x_max); | |
64 | + printf("\tOption\t\"MinY\"\t\t\"%d\"\t# was \"%d\"\n", | |
65 | + new_axys.y_min, old_axys.y_min); | |
66 | + printf("\tOption\t\"MaxY\"\t\t\"%d\"\t# was \"%d\"\n", | |
67 | + new_axys.y_max, old_axys.y_max); | |
68 | + if (swap_xy != 0) | |
69 | + printf("\tOption\t\"SwapXY\"\t\"%d\"\n", swap_xy); | |
70 | +} |
@@ -0,0 +1,192 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Tias Guns | |
3 | + * Copyright (c) 2009 Soren Hauberg | |
4 | + * | |
5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | + * of this software and associated documentation files (the "Software"), to deal | |
7 | + * in the Software without restriction, including without limitation the rights | |
8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | + * copies of the Software, and to permit persons to whom the Software is | |
10 | + * furnished to do so, subject to the following conditions: | |
11 | + * | |
12 | + * The above copyright notice and this permission notice shall be included in | |
13 | + * all copies or substantial portions of the 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 THE | |
18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | + * THE SOFTWARE. | |
22 | + */ | |
23 | +#include <gtkmm/main.h> | |
24 | +#include <gtkmm/window.h> | |
25 | +#include <gtkmm/drawingarea.h> | |
26 | +#include <cairomm/context.h> | |
27 | + | |
28 | +#include "calibrator.hh" | |
29 | + | |
30 | + | |
31 | +// Timeout parameters | |
32 | +const int time_step = 100; // in milliseconds | |
33 | +const int max_time = 15000; // 5000 = 5 sec | |
34 | + | |
35 | +// Clock appereance | |
36 | +const int clock_radius = 50; | |
37 | +const int clock_line_width = 10; | |
38 | + | |
39 | +// Text printed on screen | |
40 | +const int font_size = 20; | |
41 | +const std::string help_text = "Press the point with the stylus"; | |
42 | + | |
43 | + | |
44 | +/******************************************* | |
45 | + * GTK-mm class for the the calibration GUI | |
46 | + *******************************************/ | |
47 | +class CalibrationArea : public Gtk::DrawingArea | |
48 | +{ | |
49 | +public: | |
50 | + CalibrationArea(Calibrator* w); | |
51 | + | |
52 | +protected: | |
53 | + // Data | |
54 | + Calibrator* calibrator; | |
55 | + double X[4], Y[4]; | |
56 | + int display_width, display_height; | |
57 | + int time_elapsed; | |
58 | + | |
59 | + | |
60 | + // Signal handlers | |
61 | + bool on_timer_signal(); | |
62 | + bool on_expose_event(GdkEventExpose *event); | |
63 | + bool on_button_press_event(GdkEventButton *event); | |
64 | + | |
65 | + // Helper functions | |
66 | + void redraw(); | |
67 | +}; | |
68 | + | |
69 | +CalibrationArea::CalibrationArea(Calibrator* calibrator0) | |
70 | + : calibrator(calibrator0), time_elapsed(0) | |
71 | +{ | |
72 | + // Listen for mouse events | |
73 | + add_events(Gdk::BUTTON_PRESS_MASK); | |
74 | + | |
75 | + // Compute absolute circle centers | |
76 | + const Glib::RefPtr<Gdk::Screen> S = get_screen(); | |
77 | + display_width = S->get_width(); | |
78 | + display_height = S->get_height(); | |
79 | + | |
80 | + const int delta_x = display_width/num_blocks; | |
81 | + const int delta_y = display_height/num_blocks; | |
82 | + X[UL] = delta_x; Y[UL] = delta_y; | |
83 | + X[UR] = display_width - delta_x - 1; Y[UR] = delta_y; | |
84 | + X[LL] = delta_x; Y[LL] = display_height - delta_y - 1; | |
85 | + X[LR] = display_width - delta_x - 1; Y[LR] = display_height - delta_y - 1; | |
86 | + | |
87 | + // Setup timer for animation | |
88 | + sigc::slot<bool> slot = sigc::mem_fun(*this, &CalibrationArea::on_timer_signal); | |
89 | + Glib::signal_timeout().connect(slot, time_step); | |
90 | +} | |
91 | + | |
92 | + | |
93 | +bool CalibrationArea::on_expose_event(GdkEventExpose *event) | |
94 | +{ | |
95 | + const double radius = 4; | |
96 | + Glib::RefPtr<Gdk::Window> window = get_window(); | |
97 | + if (window) { | |
98 | + Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context(); | |
99 | + cr->save(); | |
100 | + | |
101 | + cr->rectangle(event->area.x, event->area.y, event->area.width, event->area.height); | |
102 | + cr->clip(); | |
103 | + | |
104 | + // Print the text | |
105 | + Cairo::TextExtents extent; | |
106 | + cr->set_font_size(font_size); | |
107 | + cr->get_text_extents(help_text, extent); | |
108 | + cr->move_to((display_width-extent.width)/2, 0.3*display_height); | |
109 | + cr->show_text(help_text); | |
110 | + cr->stroke(); | |
111 | + | |
112 | + // Draw the points | |
113 | + int i = 0; | |
114 | + for (; i < calibrator->get_numclicks(); i++) { | |
115 | + cr->arc(X[i], Y[i], radius, 0.0, 2.0 * M_PI); | |
116 | + cr->set_source_rgb(1.0, 1.0, 1.0); | |
117 | + cr->fill_preserve(); | |
118 | + cr->stroke(); | |
119 | + } | |
120 | + | |
121 | + cr->set_line_width(2); | |
122 | + cr->arc(X[i], Y[i], radius, 0.0, 2.0 * M_PI); | |
123 | + cr->set_source_rgb(0.8, 0.0, 0.0); | |
124 | + //cr->fill_preserve(); | |
125 | + cr->stroke(); | |
126 | + | |
127 | + // Draw the clock background | |
128 | + cr->arc(display_width/2, display_height/2, clock_radius/2, 0.0, 2.0 * M_PI); | |
129 | + cr->set_source_rgb(0.5, 0.5, 0.5); | |
130 | + cr->fill_preserve(); | |
131 | + cr->stroke(); | |
132 | + | |
133 | + cr->set_line_width(clock_line_width); | |
134 | + cr->arc(display_width/2, display_height/2, (clock_radius - clock_line_width)/2, | |
135 | + 0.0, 2.0 * M_PI * (double)time_elapsed/(double)max_time); | |
136 | + cr->set_source_rgb(0.0, 0.0, 0.0); | |
137 | + cr->stroke(); | |
138 | + | |
139 | + cr->restore(); | |
140 | + } | |
141 | + | |
142 | + return true; | |
143 | +} | |
144 | + | |
145 | +void CalibrationArea::redraw() | |
146 | +{ | |
147 | + Glib::RefPtr<Gdk::Window> win = get_window(); | |
148 | + if (win) { | |
149 | + const Gdk::Rectangle rect(0, 0, display_width, display_height); | |
150 | + win->invalidate_rect(rect, false); | |
151 | + } | |
152 | +} | |
153 | + | |
154 | +bool CalibrationArea::on_timer_signal() | |
155 | +{ | |
156 | + time_elapsed += time_step; | |
157 | + if (time_elapsed > max_time) { | |
158 | + exit(0); | |
159 | + } | |
160 | + | |
161 | + // Update clock | |
162 | + Glib::RefPtr<Gdk::Window> win = get_window(); | |
163 | + if (win) { | |
164 | + const Gdk::Rectangle rect(display_width/2 - clock_radius - clock_line_width, | |
165 | + display_height/2 - clock_radius - clock_line_width, | |
166 | + 2 * clock_radius + 1 + 2 * clock_line_width, | |
167 | + 2 * clock_radius + 1 + 2 * clock_line_width); | |
168 | + win->invalidate_rect(rect, false); | |
169 | + } | |
170 | + | |
171 | + return true; | |
172 | +} | |
173 | + | |
174 | +bool CalibrationArea::on_button_press_event(GdkEventButton *event) | |
175 | +{ | |
176 | + // Handle click | |
177 | + time_elapsed = 0; | |
178 | + calibrator->add_click(event->x_root, event->y_root); | |
179 | + | |
180 | + // Are we done yet? | |
181 | + if (calibrator->get_numclicks() >= 4) { | |
182 | + // Recalibrate | |
183 | + calibrator->finish(display_width, display_height); | |
184 | + | |
185 | + exit(0); | |
186 | + } | |
187 | + | |
188 | + // Force a redraw | |
189 | + redraw(); | |
190 | + | |
191 | + return true; | |
192 | +} |
@@ -0,0 +1,243 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Tias Guns | |
3 | + * Copyright (c) 2009 Soren Hauberg | |
4 | + * | |
5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | + * of this software and associated documentation files (the "Software"), to deal | |
7 | + * in the Software without restriction, including without limitation the rights | |
8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | + * copies of the Software, and to permit persons to whom the Software is | |
10 | + * furnished to do so, subject to the following conditions: | |
11 | + * | |
12 | + * The above copyright notice and this permission notice shall be included in | |
13 | + * all copies or substantial portions of the 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 THE | |
18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | + * THE SOFTWARE. | |
22 | + */ | |
23 | +#include <cstring> | |
24 | +#include <stdio.h> | |
25 | +#include <stdlib.h> | |
26 | + | |
27 | +#include <X11/Xlib.h> | |
28 | +#include <X11/extensions/XInput.h> | |
29 | + | |
30 | +/* | |
31 | + * Number of blocks. We partition the screen into 'num_blocks' x 'num_blocks' | |
32 | + * rectangles of equal size. We then ask the user to press points that are | |
33 | + * located at the corner closes to the center of the four blocks in the corners | |
34 | + * of the screen. The following ascii art illustrates the situation. We partition | |
35 | + * the screen into 8 blocks in each direction. We then let the user press the | |
36 | + * points marked with 'O'. | |
37 | + * | |
38 | + * +--+--+--+--+--+--+--+--+ | |
39 | + * | | | | | | | | | | |
40 | + * +--O--+--+--+--+--+--O--+ | |
41 | + * | | | | | | | | | | |
42 | + * +--+--+--+--+--+--+--+--+ | |
43 | + * | | | | | | | | | | |
44 | + * +--+--+--+--+--+--+--+--+ | |
45 | + * | | | | | | | | | | |
46 | + * +--+--+--+--+--+--+--+--+ | |
47 | + * | | | | | | | | | | |
48 | + * +--+--+--+--+--+--+--+--+ | |
49 | + * | | | | | | | | | | |
50 | + * +--+--+--+--+--+--+--+--+ | |
51 | + * | | | | | | | | | | |
52 | + * +--O--+--+--+--+--+--O--+ | |
53 | + * | | | | | | | | | | |
54 | + * +--+--+--+--+--+--+--+--+ | |
55 | + */ | |
56 | +const int num_blocks = 8; | |
57 | + | |
58 | +// Threshold to keep the same point from being clicked twice. | |
59 | +// Set to zero if you don't want this check | |
60 | +const int click_threshold = 7; | |
61 | + | |
62 | +// Names of the points | |
63 | +enum { | |
64 | + UL = 0, // Upper-left | |
65 | + UR = 1, // Upper-right | |
66 | + LL = 2, // Lower-left | |
67 | + LR = 3 // Lower-right | |
68 | +}; | |
69 | + | |
70 | +// struct to hold min/max info of the X and Y axis | |
71 | +struct XYinfo { | |
72 | + int x_min; | |
73 | + int x_max; | |
74 | + int y_min; | |
75 | + int y_max; | |
76 | + XYinfo() : x_min(-1), x_max(-1), y_min(-1), y_max(-1) {} | |
77 | + XYinfo(int xmi, int xma, int ymi, int yma) : | |
78 | + x_min(xmi), x_max(xma), y_min(ymi), y_max(yma) {} | |
79 | +}; | |
80 | + | |
81 | +// all need struct XYinfo, and some the consts too | |
82 | +#include "calibrator.cpp" | |
83 | +#include "calibrators/calibratorXorgPrint.cpp" | |
84 | +#include "calibrators/calibratorEvdev.cpp" | |
85 | +#include "calibrators/calibratorUsbtouchscreen.cpp" | |
86 | + | |
87 | + | |
88 | + | |
89 | +// find a calibratable device (using Xinput) | |
90 | +// retuns number of devices found, | |
91 | +// data of last driver is returned in the function parameters | |
92 | +int find_driver(const char* drivername, XYinfo& axys) | |
93 | +{ | |
94 | + int found = 0; | |
95 | + | |
96 | + Display* display = XOpenDisplay(NULL); | |
97 | + if (display == NULL) { | |
98 | + fprintf(stderr, "Unable to connect to X server\n"); | |
99 | + exit(1); | |
100 | + } | |
101 | + | |
102 | + int xi_opcode, event, error; | |
103 | + if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) { | |
104 | + printf("X Input extension not available.\n"); | |
105 | + exit(1); | |
106 | + } | |
107 | + | |
108 | + int ndevices; | |
109 | + XDeviceInfoPtr list, slist; | |
110 | + slist=list=(XDeviceInfoPtr) XListInputDevices (display, &ndevices); | |
111 | + for (int i=0; i<ndevices; i++, list++) | |
112 | + { | |
113 | + if (list->use == IsXKeyboard || list->use == IsXPointer) | |
114 | + continue; | |
115 | + | |
116 | + XAnyClassPtr any = (XAnyClassPtr) (list->inputclassinfo); | |
117 | + for (int j=0; j<list->num_classes; j++) | |
118 | + { | |
119 | + | |
120 | + if (any->c_class == ValuatorClass) | |
121 | + { | |
122 | + XValuatorInfoPtr V = (XValuatorInfoPtr) any; | |
123 | + XAxisInfoPtr ax = (XAxisInfoPtr) V->axes; | |
124 | + | |
125 | + if (V->num_axes >= 2 && | |
126 | + !(ax[0].min_value == -1 && ax[0].max_value == -1) && | |
127 | + !(ax[1].min_value == -1 && ax[1].max_value == -1)) { | |
128 | + /* a calibratable device (no mouse etc) */ | |
129 | + found++; | |
130 | + drivername = strdup(list->name); | |
131 | + axys.x_min = ax[0].min_value; | |
132 | + axys.x_max = ax[0].max_value; | |
133 | + axys.y_min = ax[1].min_value; | |
134 | + axys.y_max = ax[1].max_value; | |
135 | + } | |
136 | + | |
137 | + } | |
138 | + | |
139 | + /* | |
140 | + * Increment 'any' to point to the next item in the linked | |
141 | + * list. The length is in bytes, so 'any' must be cast to | |
142 | + * a character pointer before being incremented. | |
143 | + */ | |
144 | + any = (XAnyClassPtr) ((char *) any + any->length); | |
145 | + } | |
146 | + | |
147 | + } | |
148 | + XFreeDeviceList(slist); | |
149 | + XCloseDisplay(display); | |
150 | + | |
151 | + return found; | |
152 | +} | |
153 | + | |
154 | +static void usage(char* cmd) | |
155 | +{ | |
156 | + fprintf(stderr, "Usage: %s [-h|--help] [--precalib <minx> <maxx> <miny> <maxy>]\n", cmd); | |
157 | + fprintf(stderr, "\t--precalib: manually provide the current calibration setting (eg the values in xorg.conf)\n"); | |
158 | +} | |
159 | + | |
160 | +Calibrator* main_common(int argc, char** argv) | |
161 | +{ | |
162 | + bool fake = false; | |
163 | + bool precalib = false; | |
164 | + XYinfo pre_axys; | |
165 | + | |
166 | + // parse input | |
167 | + if (argc > 1) { | |
168 | + for (int i=1; i!=argc; i++) { | |
169 | + // Display help ? | |
170 | + if (strcmp("-h", argv[i]) == 0 || | |
171 | + strcmp("--help", argv[i]) == 0) { | |
172 | + usage(argv[0]); | |
173 | + exit(0); | |
174 | + } | |
175 | + | |
176 | + // Get pre-calibration ? | |
177 | + if (strcmp("--precalib", argv[i]) == 0) { | |
178 | + precalib = true; | |
179 | + if (argc > i+1) | |
180 | + pre_axys.x_min = atoi(argv[i+1]); | |
181 | + if (argc > i+2) | |
182 | + pre_axys.x_max = atoi(argv[i+2]); | |
183 | + if (argc > i+3) | |
184 | + pre_axys.y_min = atoi(argv[i+3]); | |
185 | + if (argc > i+4) | |
186 | + pre_axys.y_max = atoi(argv[i+4]); | |
187 | + } | |
188 | + | |
189 | + // Fake calibratable device ? | |
190 | + if (strcmp("--fake", argv[i]) == 0) { | |
191 | + fake = true; | |
192 | + } | |
193 | + } | |
194 | + } | |
195 | + | |
196 | + // find driver(s) | |
197 | + const char* drivername = NULL; | |
198 | + XYinfo axys; | |
199 | + int nr_found = find_driver(drivername, axys); | |
200 | + if (nr_found == 0) { | |
201 | + if (fake) { | |
202 | + // Fake a calibratable device | |
203 | + drivername = "Fake_device"; | |
204 | + axys = XYinfo(0,0,0,0); | |
205 | + } else { | |
206 | + fprintf (stderr, "Error: No calibratable devices found.\n"); | |
207 | + exit(1); | |
208 | + } | |
209 | + } | |
210 | + if (nr_found > 1) { | |
211 | + printf ("Warning: multiple calibratable devices found, calibrating last one (%s)\n", drivername); | |
212 | + } | |
213 | + | |
214 | + // override min/max XY from command line ? | |
215 | + if (precalib) { | |
216 | + if (pre_axys.x_min != -1) | |
217 | + axys.x_min = pre_axys.x_min; | |
218 | + if (pre_axys.x_max != -1) | |
219 | + axys.x_max = pre_axys.x_max; | |
220 | + if (pre_axys.y_min != -1) | |
221 | + axys.y_min = pre_axys.y_min; | |
222 | + if (pre_axys.y_max != -1) | |
223 | + axys.y_max = pre_axys.y_max; | |
224 | + } | |
225 | + | |
226 | + Calibrator* calibrator; | |
227 | + // Different driver, different backend behaviour | |
228 | + if (strcmp(drivername, "Usbtouchscreen") == 0) | |
229 | + // Usbtouchscreen driver | |
230 | + calibrator = new CalibratorUsbtouchscreen(drivername, axys); | |
231 | + else { | |
232 | + // unable to know device driver from its name alone | |
233 | + // either its EVDEV or a normal Xorg touchscreen driver | |
234 | + if (CalibratorEvdev::check_driver(drivername)) { | |
235 | + // Evdev driver | |
236 | + calibrator = new CalibratorEvdev(drivername, axys); | |
237 | + } else { | |
238 | + // EVTouch is handled as a normal Xorg driver | |
239 | + calibrator = new CalibratorXorgPrint(drivername, axys); | |
240 | + } | |
241 | + } | |
242 | + return calibrator; | |
243 | +} |
@@ -0,0 +1,53 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2009 Tias Guns | |
3 | + * Copyright (c) 2009 Soren Hauberg | |
4 | + * | |
5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | + * of this software and associated documentation files (the "Software"), to deal | |
7 | + * in the Software without restriction, including without limitation the rights | |
8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | + * copies of the Software, and to permit persons to whom the Software is | |
10 | + * furnished to do so, subject to the following conditions: | |
11 | + * | |
12 | + * The above copyright notice and this permission notice shall be included in | |
13 | + * all copies or substantial portions of the 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 THE | |
18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | + * THE SOFTWARE. | |
22 | + */ | |
23 | +#include <stdexcept> | |
24 | + | |
25 | +// Must be before Xlib stuff | |
26 | +#include <gtkmm/main.h> | |
27 | +#include <gtkmm/window.h> | |
28 | +#include <gtkmm/drawingarea.h> | |
29 | +#include <cairomm/context.h> | |
30 | + | |
31 | +#include "main_common.hpp" | |
32 | +#include "gui_gtkmm.cpp" | |
33 | + | |
34 | +int main(int argc, char** argv) | |
35 | +{ | |
36 | + Calibrator* calibrator = main_common(argc, argv); | |
37 | + | |
38 | + // GTK-mm setup | |
39 | + Gtk::Main kit(argc, argv); | |
40 | + | |
41 | + Gtk::Window win; | |
42 | + win.fullscreen(); | |
43 | + | |
44 | + CalibrationArea area(calibrator); | |
45 | + win.add(area); | |
46 | + area.show(); | |
47 | + | |
48 | + Gtk::Main::run(win); | |
49 | + | |
50 | + Gtk::Main::quit(); | |
51 | + delete calibrator; | |
52 | + return 0; | |
53 | +} |