A generic touchscreen calibration program for X.Org
Revision | 081dbca29ffa089252e2bfbeaf1961f1c0002606 (tree) |
---|---|
Time | 2010-01-11 05:31:03 |
Author | Tias Guns <tias@ulys...> |
Commiter | Tias Guns |
New X11 based gui,
Looks and feels like the gtkmm based one, but written purely in the X
windowing system.
@@ -1,7 +1,11 @@ | ||
1 | -all: xinput_calibrator.old xinput_calibrator.gtkmm | |
1 | +all: xinput_calibrator.x11 xinput_calibrator.gtkmm | |
2 | 2 | |
3 | -xinput_calibrator.old: xinput_calibrator.cc | |
4 | - g++ -Wall xinput_calibrator.cc `pkg-config --cflags --libs gtkmm-2.4` -o xinput_calibrator.old | |
3 | +xinput_calibrator.x11: main_x11.cpp gui_x11.cpp | |
4 | + g++ -Wall main_x11.cpp -lX11 -lXi -o xinput_calibrator.x11 | |
5 | + cp xinput_calibrator.x11 xinput_calibrator | |
5 | 6 | |
6 | 7 | xinput_calibrator.gtkmm: main_gtkmm.cpp gui_gtkmm.cpp |
7 | 8 | g++ -Wall main_gtkmm.cpp `pkg-config --cflags --libs gtkmm-2.4` -o xinput_calibrator.gtkmm |
9 | + | |
10 | +clean: | |
11 | + rm -f xinput_calibrator xinput_calibrator.x11 xinput_calibrator.gtkmm |
@@ -0,0 +1,320 @@ | ||
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 <X11/X.h> | |
23 | +#include <X11/Xlib.h> | |
24 | +#include <X11/Xutil.h> | |
25 | +#include <X11/Xos.h> // strncpy, strlen | |
26 | + | |
27 | +#include <stdlib.h> | |
28 | +#include <stdio.h> | |
29 | +#include <signal.h> | |
30 | +#include <string.h> | |
31 | + | |
32 | +#include "calibrator.hh" | |
33 | + | |
34 | + | |
35 | +// Timeout parameters | |
36 | +const int time_step = 100; // in milliseconds | |
37 | +const int max_time = 15000; // 5000 = 5 sec | |
38 | + | |
39 | +// Clock appereance | |
40 | +const int cross_lines = 25; | |
41 | +const int cross_circle = 4; | |
42 | +const int clock_radius = 50; | |
43 | +const int clock_line_width = 10; | |
44 | + | |
45 | +// Text printed on screen | |
46 | +const int font_size = 16; | |
47 | +const int help_lines = 4; | |
48 | +const std::string help_text[help_lines] = { | |
49 | + "Touchscreen Calibration", | |
50 | + "Press the point, use a stylus to increase precision.", | |
51 | + "", | |
52 | + "(To abort, press any key or wait)" | |
53 | +}; | |
54 | + | |
55 | +// color management | |
56 | +enum { BLACK=0, WHITE=1, GRAY=2, DIMGRAY=3, RED=4 }; | |
57 | +const int nr_colors = 5; | |
58 | +const char* colors[nr_colors] = {"BLACK", "WHITE", "GRAY", "DIMGRAY", "RED"}; | |
59 | + | |
60 | + | |
61 | +void sigalarm_handler(int num); | |
62 | + | |
63 | + | |
64 | +/******************************************* | |
65 | + * X11 class for the the calibration GUI | |
66 | + *******************************************/ | |
67 | +class GuiCalibratorX11 | |
68 | +{ | |
69 | +public: | |
70 | + GuiCalibratorX11(Calibrator* w); | |
71 | + ~GuiCalibratorX11(); | |
72 | + static bool set_instance(GuiCalibratorX11* W); | |
73 | + static void give_timer_signal(); | |
74 | + | |
75 | +protected: | |
76 | + // Data | |
77 | + Calibrator* calibrator; | |
78 | + double X[4], Y[4]; | |
79 | + int display_width, display_height; | |
80 | + int time_elapsed; | |
81 | + | |
82 | + // X11 vars | |
83 | + Display* display; | |
84 | + Window win; | |
85 | + GC gc; | |
86 | + XFontStruct* font_info; | |
87 | + // color mngmt | |
88 | + unsigned long pixel[nr_colors]; | |
89 | + | |
90 | + | |
91 | + // Signal handlers | |
92 | + bool on_timer_signal(); | |
93 | + bool on_expose_event(); | |
94 | + bool on_button_press_event(XEvent event); | |
95 | + | |
96 | + // Helper functions | |
97 | + void redraw(); | |
98 | + | |
99 | +private: | |
100 | + static GuiCalibratorX11* instance; | |
101 | +}; | |
102 | +GuiCalibratorX11* GuiCalibratorX11::instance = NULL; | |
103 | + | |
104 | + | |
105 | +GuiCalibratorX11::GuiCalibratorX11(Calibrator* calibrator0) | |
106 | + : calibrator(calibrator0), time_elapsed(0) | |
107 | +{ | |
108 | + display = XOpenDisplay(NULL); | |
109 | + if (display == NULL) { | |
110 | + throw std::runtime_error("Unable to connect to X server"); | |
111 | + } | |
112 | + // Load font and get font information structure | |
113 | + font_info = XLoadQueryFont(display, "9x15"); | |
114 | + if (font_info == NULL) { | |
115 | + XCloseDisplay(display); | |
116 | + throw std::runtime_error("Unable to open 9x15 font"); | |
117 | + } | |
118 | + | |
119 | + // Compute absolute circle centers | |
120 | + int screen_num = DefaultScreen(display); | |
121 | + display_width = DisplayWidth(display, screen_num); | |
122 | + display_height = DisplayHeight(display, screen_num); | |
123 | + | |
124 | + const int delta_x = display_width/num_blocks; | |
125 | + const int delta_y = display_height/num_blocks; | |
126 | + X[UL] = delta_x; Y[UL] = delta_y; | |
127 | + X[UR] = display_width - delta_x - 1; Y[UR] = delta_y; | |
128 | + X[LL] = delta_x; Y[LL] = display_height - delta_y - 1; | |
129 | + X[LR] = display_width - delta_x - 1; Y[LR] = display_height - delta_y - 1; | |
130 | + | |
131 | + // Register events on the window | |
132 | + XSetWindowAttributes attributes; | |
133 | + attributes.override_redirect = True; | |
134 | + attributes.event_mask = ExposureMask | KeyPressMask | ButtonPressMask; | |
135 | + | |
136 | + win = XCreateWindow(display, RootWindow(display, screen_num), | |
137 | + 0, 0, display_width, display_height, 0, | |
138 | + CopyFromParent, InputOutput, CopyFromParent, | |
139 | + CWOverrideRedirect | CWEventMask, | |
140 | + &attributes); | |
141 | + XMapWindow(display, win); | |
142 | + | |
143 | + // Listen to events | |
144 | + XGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, | |
145 | + CurrentTime); | |
146 | + XGrabPointer(display, win, False, ButtonPressMask, GrabModeAsync, | |
147 | + GrabModeAsync, None, None, CurrentTime); | |
148 | + | |
149 | + Colormap colormap = DefaultColormap(display, screen_num); | |
150 | + XColor color; | |
151 | + for (int i = 0; i != nr_colors; i++) { | |
152 | + XParseColor(display, colormap, colors[i], &color); | |
153 | + XAllocColor(display, colormap, &color); | |
154 | + pixel[i] = color.pixel; | |
155 | + } | |
156 | + XSetWindowBackground(display, win, pixel[GRAY]); | |
157 | + XClearWindow(display, win); | |
158 | + | |
159 | + gc = XCreateGC(display, win, 0, NULL); | |
160 | + XSetFont(display, gc, font_info->fid); | |
161 | + | |
162 | + // Setup timer for animation | |
163 | + signal(SIGALRM, sigalarm_handler); | |
164 | + struct itimerval timer; | |
165 | + timer.it_value.tv_sec = time_step/1000; | |
166 | + timer.it_value.tv_usec = (time_step % 1000) * 1000; | |
167 | + timer.it_interval = timer.it_value; | |
168 | + setitimer(ITIMER_REAL, &timer, NULL); | |
169 | +} | |
170 | + | |
171 | +GuiCalibratorX11::~GuiCalibratorX11() | |
172 | +{ | |
173 | + XUngrabPointer(display, CurrentTime); | |
174 | + XUngrabKeyboard(display, CurrentTime); | |
175 | + XFreeGC(display, gc); | |
176 | + XCloseDisplay(display); | |
177 | +} | |
178 | + | |
179 | +void GuiCalibratorX11::redraw() | |
180 | +{ | |
181 | + // Print the text | |
182 | + int text_height = font_info->ascent + font_info->descent; | |
183 | + int text_width = -1; | |
184 | + for (int i = 0; i != help_lines; i++) { | |
185 | + text_width = std::max(text_width, XTextWidth(font_info, | |
186 | + help_text[i].c_str(), help_text[i].length())); | |
187 | + } | |
188 | + | |
189 | + int x = (display_width - text_width) / 2; | |
190 | + int y = (display_height - text_height) / 2 - 60; | |
191 | + XSetForeground(display, gc, pixel[BLACK]); | |
192 | + XSetLineAttributes(display, gc, 2, LineSolid, CapRound, JoinRound); | |
193 | + XDrawRectangle(display, win, gc, x - 10, y - (help_lines*text_height) - 10, | |
194 | + text_width + 20, (help_lines*text_height) + 20); | |
195 | + | |
196 | + // Print help lines | |
197 | + y -= 3; | |
198 | + for (int i = help_lines-1; i != -1; i--) { | |
199 | + int w = XTextWidth(font_info, help_text[i].c_str(), help_text[i].length()); | |
200 | + XDrawString(display, win, gc, x + (text_width-w)/2, y, | |
201 | + help_text[i].c_str(), help_text[i].length()); | |
202 | + y -= text_height; | |
203 | + } | |
204 | + | |
205 | + // Draw the points | |
206 | + for (int i = 0; i <= calibrator->get_numclicks(); i++) { | |
207 | + // set color: already clicked or not | |
208 | + if (i < calibrator->get_numclicks()) | |
209 | + XSetForeground(display, gc, pixel[WHITE]); | |
210 | + else | |
211 | + XSetForeground(display, gc, pixel[RED]); | |
212 | + XSetLineAttributes(display, gc, 1, LineSolid, CapRound, JoinRound); | |
213 | + | |
214 | + XDrawLine(display, win, gc, X[i] - cross_lines, Y[i], | |
215 | + X[i] + cross_lines, Y[i]); | |
216 | + XDrawLine(display, win, gc, X[i], Y[i] - cross_lines, | |
217 | + X[i], Y[i] + cross_lines); | |
218 | + XDrawArc(display, win, gc, X[i] - cross_circle, Y[i] - cross_circle, | |
219 | + (2 * cross_circle), (2 * cross_circle), 0, 360 * 64); | |
220 | + } | |
221 | + | |
222 | + // Draw the clock background | |
223 | + XSetForeground(display, gc, pixel[DIMGRAY]); | |
224 | + XSetLineAttributes(display, gc, 0, LineSolid, CapRound, JoinRound); | |
225 | + XFillArc(display, win, gc, (display_width-clock_radius)/2, (display_height - clock_radius)/2, | |
226 | + clock_radius, clock_radius, 0, 360 * 64); | |
227 | +} | |
228 | + | |
229 | +bool GuiCalibratorX11::on_expose_event() | |
230 | +{ | |
231 | + redraw(); | |
232 | + | |
233 | + return true; | |
234 | +} | |
235 | + | |
236 | +bool GuiCalibratorX11::on_timer_signal() | |
237 | +{ | |
238 | + time_elapsed += time_step; | |
239 | + if (time_elapsed > max_time) { | |
240 | + exit(0); | |
241 | + } | |
242 | + | |
243 | + // Update clock | |
244 | + XSetForeground(display, gc, pixel[BLACK]); | |
245 | + XSetLineAttributes(display, gc, clock_line_width, | |
246 | + LineSolid, CapButt, JoinMiter); | |
247 | + XDrawArc(display, win, gc, (display_width-clock_radius+clock_line_width)/2, | |
248 | + (display_height-clock_radius+clock_line_width)/2, | |
249 | + clock_radius-clock_line_width, clock_radius-clock_line_width, | |
250 | + 90*64, ((double)time_elapsed/(double)max_time) * -360 * 64); | |
251 | + | |
252 | + return true; | |
253 | +} | |
254 | + | |
255 | +bool GuiCalibratorX11::on_button_press_event(XEvent event) | |
256 | +{ | |
257 | + // Handle click | |
258 | + time_elapsed = 0; | |
259 | + calibrator->add_click(event.xbutton.x, event.xbutton.y); | |
260 | + | |
261 | + // Are we done yet? | |
262 | + if (calibrator->get_numclicks() >= 4) { | |
263 | + // Recalibrate | |
264 | + calibrator->finish(display_width, display_height); | |
265 | + | |
266 | + exit(0); | |
267 | + } | |
268 | + | |
269 | + // Force a redraw | |
270 | + redraw(); | |
271 | + | |
272 | + return true; | |
273 | +} | |
274 | + | |
275 | +void GuiCalibratorX11::give_timer_signal() | |
276 | +{ | |
277 | + if (instance != NULL) { | |
278 | + // timer signal | |
279 | + //check timeout | |
280 | + instance->on_timer_signal(); | |
281 | + | |
282 | + // process events | |
283 | + XEvent event; | |
284 | + while (XCheckWindowEvent(instance->display, instance->win, -1, &event) == True) { | |
285 | + switch (event.type) { | |
286 | + case Expose: | |
287 | + // only draw the last contiguous expose | |
288 | + if (event.xexpose.count != 0) | |
289 | + break; | |
290 | + instance->on_expose_event(); | |
291 | + break; | |
292 | + | |
293 | + case ButtonPress: | |
294 | + instance->on_button_press_event(event); | |
295 | + break; | |
296 | + | |
297 | + case KeyPress: | |
298 | + exit(0); | |
299 | + break; | |
300 | + } | |
301 | + } | |
302 | + } | |
303 | +} | |
304 | + | |
305 | +bool GuiCalibratorX11::set_instance(GuiCalibratorX11* W) | |
306 | +{ | |
307 | + bool wasSet = (instance != NULL); | |
308 | + instance = W; | |
309 | + | |
310 | + return wasSet; | |
311 | +} | |
312 | + | |
313 | + | |
314 | +// handle SIGALRM signal, pass to singleton | |
315 | +void sigalarm_handler(int num) | |
316 | +{ | |
317 | + if (num == SIGALRM) { | |
318 | + GuiCalibratorX11::give_timer_signal(); | |
319 | + } | |
320 | +} |
@@ -0,0 +1,39 @@ | ||
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 "main_common.hpp" | |
24 | +#include "gui_x11.cpp" | |
25 | + | |
26 | +int main(int argc, char** argv) | |
27 | +{ | |
28 | + Calibrator* calibrator = main_common(argc, argv); | |
29 | + | |
30 | + GuiCalibratorX11 gui(calibrator); | |
31 | + GuiCalibratorX11::set_instance(&gui); | |
32 | + | |
33 | + // wait for timer signal, processes events | |
34 | + while(1) | |
35 | + pause(); | |
36 | + | |
37 | + delete calibrator; | |
38 | + return 0; | |
39 | +} |