A generic touchscreen calibration program for X.Org
修订版 | 084adde93435e7f74e0dfdded5a5bb0ba4ae90a0 (tree) |
---|---|
时间 | 2012-06-19 06:36:45 |
作者 | Tias Guns <tias@ulys...> |
Commiter | Tias Guns |
Merge branch 'tester'
A tester object to faithfully test the calibration routines.
This will be reused for testing the evdev calibration (which
is non-standard) as well.
@@ -30,7 +30,7 @@ SUBDIRS = \ | ||
30 | 30 | |
31 | 31 | AM_CXXFLAGS = -Wall -ansi -pedantic |
32 | 32 | |
33 | -bin_PROGRAMS = xinput_calibrator | |
33 | +bin_PROGRAMS = xinput_calibrator tester | |
34 | 34 | |
35 | 35 | COMMON_SRCS=calibrator.cpp calibrator/XorgPrint.cpp calibrator/Evdev.cpp calibrator/Usbtouchscreen.cpp main_common.cpp |
36 | 36 |
@@ -51,6 +51,10 @@ xinput_calibrator_CXXFLAGS = $(XINPUT_CFLAGS) $(GTKMM_CFLAGS) $(AM_CXXFLAGS) | ||
51 | 51 | xinput_calibrator_LDFLAGS = -Wl,--as-needed |
52 | 52 | endif |
53 | 53 | |
54 | +tester_SOURCES = tester.cpp calibrator.cpp calibrator/Tester.cpp | |
55 | +tester_LDADD = $(XINPUT_LIBS) $(XRANDR_LIBS) $(X11_LIBS) | |
56 | +tester_CXXFLAGS = $(XINPUT_CFLAGS) $(X11_CFLAGS) $(XRANDR_CFLAGS) $(AM_CXXFLAGS) | |
57 | + | |
54 | 58 | EXTRA_DIST = \ |
55 | 59 | calibrator.cpp \ |
56 | 60 | calibrator.hh \ |
@@ -251,3 +251,71 @@ bool Calibrator::has_xorgconfd_support(Display* dpy) { | ||
251 | 251 | |
252 | 252 | return has_support; |
253 | 253 | } |
254 | + | |
255 | +/* | |
256 | + * FROM xf86Xinput.c | |
257 | + * | |
258 | + * Cx - raw data from touch screen | |
259 | + * to_max - scaled highest dimension | |
260 | + * (remember, this is of rows - 1 because of 0 origin) | |
261 | + * to_min - scaled lowest dimension | |
262 | + * from_max - highest raw value from touch screen calibration | |
263 | + * from_min - lowest raw value from touch screen calibration | |
264 | + * | |
265 | + * This function is the same for X or Y coordinates. | |
266 | + * You may have to reverse the high and low values to compensate for | |
267 | + * different orgins on the touch screen vs X. | |
268 | + * | |
269 | + * e.g. to scale from device coordinates into screen coordinates, call | |
270 | + * xf86ScaleAxis(x, 0, screen_width, dev_min, dev_max); | |
271 | + */ | |
272 | +int | |
273 | +xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min) | |
274 | +{ | |
275 | + int X; | |
276 | + int64_t to_width = to_max - to_min; | |
277 | + int64_t from_width = from_max - from_min; | |
278 | + | |
279 | + if (from_width) { | |
280 | + X = (int) (((to_width * (Cx - from_min)) / from_width) + to_min); | |
281 | + } | |
282 | + else { | |
283 | + X = 0; | |
284 | + printf("Divide by Zero in xf86ScaleAxis\n"); | |
285 | + exit(1); | |
286 | + } | |
287 | + | |
288 | + if (X > to_max) | |
289 | + X = to_max; | |
290 | + if (X < to_min) | |
291 | + X = to_min; | |
292 | + | |
293 | + return X; | |
294 | +} | |
295 | + | |
296 | +// same but without rounding to min/max | |
297 | +int | |
298 | +scaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min) | |
299 | +{ | |
300 | + int X; | |
301 | + int64_t to_width = to_max - to_min; | |
302 | + int64_t from_width = from_max - from_min; | |
303 | + | |
304 | + if (from_width) { | |
305 | + X = (int) (((to_width * (Cx - from_min)) / from_width) + to_min); | |
306 | + } | |
307 | + else { | |
308 | + X = 0; | |
309 | + printf("Divide by Zero in scaleAxis\n"); | |
310 | + exit(1); | |
311 | + } | |
312 | + | |
313 | + /* no rounding to max/min | |
314 | + if (X > to_max) | |
315 | + X = to_max; | |
316 | + if (X < to_min) | |
317 | + X = to_min; | |
318 | + */ | |
319 | + | |
320 | + return X; | |
321 | +} |
@@ -27,8 +27,12 @@ | ||
27 | 27 | |
28 | 28 | #include <stdexcept> |
29 | 29 | #include <X11/Xlib.h> |
30 | +#include <stdio.h> | |
30 | 31 | #include <vector> |
31 | 32 | |
33 | +int xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min); | |
34 | +int scaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min); | |
35 | + | |
32 | 36 | /* |
33 | 37 | * Number of blocks. We partition the screen into 'num_blocks' x 'num_blocks' |
34 | 38 | * rectangles of equal size. We then ask the user to press points that are |
@@ -91,6 +95,18 @@ struct XYinfo { | ||
91 | 95 | void do_swap_xy() { |
92 | 96 | swap_xy = !swap_xy; |
93 | 97 | } |
98 | + | |
99 | + void do_xf86ScaleAxis(XYinfo& to, XYinfo& from) { | |
100 | + x.min = xf86ScaleAxis(x.min, to.x.max, to.x.min, from.x.max, from.x.min); | |
101 | + x.max = xf86ScaleAxis(x.max, to.x.max, to.x.min, from.x.max, from.x.min); | |
102 | + y.min = xf86ScaleAxis(y.min, to.y.max, to.y.min, from.y.max, from.y.min); | |
103 | + y.max = xf86ScaleAxis(y.max, to.y.max, to.y.min, from.y.max, from.y.min); | |
104 | + } | |
105 | + | |
106 | + void print(const char* xtra="\n") { | |
107 | + printf("XYinfo: x.min=%i, x.max=%i, y.min=%i, y.max=%i, swap_xy=%i, invert_x=%i, invert_y=%i%s", | |
108 | + x.min, x.max, y.min, y.max, swap_xy, x.invert, y.invert, xtra); | |
109 | + } | |
94 | 110 | }; |
95 | 111 | |
96 | 112 | /// Names of the points |
@@ -1,4 +1,5 @@ | ||
1 | 1 | EXTRA_DIST = \ |
2 | 2 | Evdev.cpp \ |
3 | 3 | Usbtouchscreen.cpp \ |
4 | - XorgPrint.cpp | |
4 | + XorgPrint.cpp \ | |
5 | + Tester.cpp |
@@ -0,0 +1,72 @@ | ||
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 | +#include "calibrator/Tester.hpp" | |
24 | + | |
25 | +#include <cstdio> | |
26 | + | |
27 | +CalibratorTester::CalibratorTester(const char* const device_name0, const XYinfo& axys0, const int thr_misclick, const int thr_doubleclick, const OutputType output_type, const char* geometry) | |
28 | + : Calibrator(device_name0, axys0, thr_misclick, thr_doubleclick, output_type, geometry) | |
29 | +{ | |
30 | + //printf("Starting test driver\n"); | |
31 | +} | |
32 | + | |
33 | +bool CalibratorTester::finish_data(const XYinfo axis) | |
34 | +{ | |
35 | + new_axis = axis; | |
36 | + | |
37 | + return true; | |
38 | +} | |
39 | + | |
40 | +XYinfo CalibratorTester::emulate_driver(XYinfo& raw, bool useNewAxis, XYinfo screen, XYinfo device) { | |
41 | + XYinfo calibAxis; | |
42 | + if (useNewAxis) | |
43 | + calibAxis = new_axis; | |
44 | + else | |
45 | + calibAxis = old_axys; | |
46 | + | |
47 | + /** | |
48 | + * The most simple and intuitive calibration implementation | |
49 | + * if only all drivers sticked to this... | |
50 | + * Note that axis inversion is automatically supported | |
51 | + * by the ScaleAxis implementation (swapping max/min on | |
52 | + * one axis will result in the inversion being calculated) | |
53 | + */ | |
54 | + | |
55 | + // placeholder for the new coordinates | |
56 | + XYinfo result(raw); | |
57 | + | |
58 | + // swap coordinates if asked | |
59 | + if (calibAxis.swap_xy) { | |
60 | + result.x.min = raw.y.min; | |
61 | + result.x.max = raw.y.max; | |
62 | + result.y.min = raw.x.min; | |
63 | + result.y.max = raw.x.max; | |
64 | + } | |
65 | + | |
66 | + result.do_xf86ScaleAxis(device, calibAxis); | |
67 | + | |
68 | + // the last step is usually done by the X server, | |
69 | + // or transparently somewhere on the way | |
70 | + result.do_xf86ScaleAxis(screen, device); | |
71 | + return result; | |
72 | +} |
@@ -0,0 +1,53 @@ | ||
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 | +#ifndef CALIBRATOR_TESTER_HPP | |
24 | +#define CALIBRATOR_TESTER_HPP | |
25 | + | |
26 | +#include "calibrator.hh" | |
27 | + | |
28 | +/*************************************** | |
29 | + * Class for generic Xorg driver, | |
30 | + * outputs new Xorg.conf and FDI policy, on stdout | |
31 | + ***************************************/ | |
32 | +class CalibratorTester: public Calibrator | |
33 | +{ | |
34 | +protected: | |
35 | + // store the new axis for use in driver emulation | |
36 | + XYinfo new_axis; | |
37 | + | |
38 | +public: | |
39 | + CalibratorTester(const char* const device_name, const XYinfo& axys, | |
40 | + const int thr_misclick=0, const int thr_doubleclick=0, | |
41 | + const OutputType output_type=OUTYPE_AUTO, const char* geometry=0); | |
42 | + | |
43 | + virtual bool finish_data(const XYinfo new_axis); | |
44 | + | |
45 | + // emulate the driver processing the coordinates in 'raw' | |
46 | + XYinfo emulate_driver(XYinfo& raw, bool useNewAxis, XYinfo screen, XYinfo device); | |
47 | + | |
48 | + void new_axis_print() { | |
49 | + new_axis.print(); | |
50 | + } | |
51 | +}; | |
52 | + | |
53 | +#endif |
@@ -0,0 +1,105 @@ | ||
1 | +#include <algorithm> | |
2 | +#include <stdio.h> | |
3 | +#include <vector> | |
4 | + | |
5 | +#include "calibrator.hh" | |
6 | +#include "calibrator/Tester.hpp" | |
7 | + | |
8 | +int main() { | |
9 | + // screen dimensions | |
10 | + int width = 800; | |
11 | + int height = 600; | |
12 | + XYinfo screen_res(0, width, 0, height); | |
13 | + | |
14 | + int delta_x = width/(float)num_blocks; | |
15 | + int delta_y = height/(float)num_blocks; | |
16 | + XYinfo target(delta_x, width-delta_x, delta_y, height-delta_y); | |
17 | + | |
18 | + int slack = 2; // amount of pixels result can be off target | |
19 | + | |
20 | + XYinfo dev_res(0, 1000, 0, 1000); | |
21 | + | |
22 | + std::vector<XYinfo> old_axes; | |
23 | + old_axes.push_back( XYinfo(0, 1000, 0, 1000) ); | |
24 | + old_axes.push_back( XYinfo(1000, 0, 0, 1000) ); | |
25 | + old_axes.push_back( XYinfo(0, 1000, 1000, 0) ); | |
26 | + old_axes.push_back( XYinfo(1000, 0, 0, 1000) ); | |
27 | + old_axes.push_back( XYinfo(0, 1000, 0, 1000, 1, 0, 0) ); | |
28 | + old_axes.push_back( XYinfo(0, 1000, 0, 1000, 1, 0, 1) ); | |
29 | + old_axes.push_back( XYinfo(0, 1000, 0, 1000, 1, 1, 0) ); | |
30 | + old_axes.push_back( XYinfo(0, 1000, 0, 1000, 1, 1, 1) ); | |
31 | + old_axes.push_back( XYinfo(1000, 0, 0, 1000, 1, 0, 0) ); | |
32 | + old_axes.push_back( XYinfo(1000, 0, 0, 1000, 1, 0, 1) ); | |
33 | + old_axes.push_back( XYinfo(1000, 0, 0, 1000, 1, 1, 0) ); | |
34 | + old_axes.push_back( XYinfo(1000, 0, 0, 1000, 1, 1, 1) ); | |
35 | + // non device-resolution calibs | |
36 | + old_axes.push_back( XYinfo(42, 929, 20, 888) ); | |
37 | + // xf86ScaleAxis rounds to min/max, this can lead to inaccurate | |
38 | + // results! Can we fix that? | |
39 | + old_axes.push_back( XYinfo(42, 929, 20, 888) ); | |
40 | + //old_axes.push_back( XYinfo(-9, 895, 124, 990) ); // this is the true axis | |
41 | + // rounding error when raw_coords are swapped??? | |
42 | + //old_axes.push_back( XYinfo(75, 750, 20, 888) ); // rounding error on X axis | |
43 | + //old_axes.push_back( XYinfo(42, 929, 120, 888) ); // rounding error on Y axis | |
44 | + | |
45 | + // raw device coordinates to emulate | |
46 | + std::vector<XYinfo> raw_coords; | |
47 | + // normal | |
48 | + raw_coords.push_back( XYinfo(105, 783, 233, 883) ); | |
49 | + // invert x, y, x+y | |
50 | + raw_coords.push_back( XYinfo(783, 105, 233, 883) ); | |
51 | + raw_coords.push_back( XYinfo(105, 783, 883, 233) ); | |
52 | + raw_coords.push_back( XYinfo(783, 105, 883, 233) ); | |
53 | + // swap | |
54 | + raw_coords.push_back( XYinfo(233, 883, 105, 783) ); | |
55 | + // swap and inverts | |
56 | + raw_coords.push_back( XYinfo(233, 883, 783, 105) ); | |
57 | + raw_coords.push_back( XYinfo(883, 233, 105, 783) ); | |
58 | + raw_coords.push_back( XYinfo(883, 233, 783, 105) ); | |
59 | + | |
60 | + for (unsigned a=0; a != old_axes.size(); a++) { | |
61 | + XYinfo old_axis(old_axes[a]); | |
62 | + printf("Old axis: "); old_axis.print(); | |
63 | + | |
64 | + for (unsigned c=0; c != raw_coords.size(); c++) { | |
65 | + XYinfo raw(raw_coords[c]); | |
66 | + //printf("Raw: "); raw.print(); | |
67 | + | |
68 | + CalibratorTester calib("Tester", old_axis); | |
69 | + | |
70 | + // clicked from raw | |
71 | + XYinfo clicked = calib.emulate_driver(raw, false, screen_res, dev_res);// false=old_axis | |
72 | + //printf("\tClicked: "); clicked.print(); | |
73 | + | |
74 | + // emulate screen clicks | |
75 | + calib.add_click(clicked.x.min, clicked.y.min); | |
76 | + calib.add_click(clicked.x.max, clicked.y.min); | |
77 | + calib.add_click(clicked.x.min, clicked.y.max); | |
78 | + calib.add_click(clicked.x.max, clicked.y.max); | |
79 | + calib.finish(width, height); | |
80 | + | |
81 | + // test result | |
82 | + XYinfo result = calib.emulate_driver(raw, true, screen_res, dev_res); // true=new_axis | |
83 | + | |
84 | + int maxdiff = std::max(abs(target.x.min - result.x.min), | |
85 | + std::max(abs(target.x.max - result.x.max), | |
86 | + std::max(abs(target.y.min - result.y.min), | |
87 | + abs(target.y.max - result.y.max)))); // no n-ary max in c++?? | |
88 | + if (maxdiff > slack) { | |
89 | + printf("-\n"); | |
90 | + printf("Old axis: "); old_axis.print(); | |
91 | + printf("Raw: "); raw.print(); | |
92 | + printf("Clicked: "); clicked.print(); | |
93 | + printf("New axis: "); calib.new_axis_print(); | |
94 | + printf("Error: difference between target and result: %i > %i:\n", maxdiff, slack); | |
95 | + printf("\tTarget: "); target.print(); | |
96 | + printf("\tResult: "); result.print(); | |
97 | + exit(1); | |
98 | + } | |
99 | + | |
100 | + printf("%i", maxdiff); | |
101 | + } // loop over raw_coords | |
102 | + | |
103 | + printf(". OK\n"); | |
104 | + } // loop over old_axes | |
105 | +} |