11/*
22 * Copyright (c) 2009 Tias Guns
33 * Copyright (c) 2009 Soren Hauberg
4+ * Copyright (c) 2011 Antoine Hue
45 *
56 * Permission is hereby granted, free of charge, to any person obtaining a copy
67 * of this software and associated documentation files (the "Software"), to deal
2627#include < iostream>
2728#include < fstream>
2829#include < cstring>
30+ #include < stdarg.h>
2931
3032#include " calibrator.hh"
3133
32- Calibrator::Calibrator (const char * const device_name0, const XYinfo& axys0,
33- const bool verbose0, const int thr_misclick, const int thr_doubleclick, const OutputType output_type0, const char * geometry0)
34- : device_name(device_name0), old_axys(axys0), verbose(verbose0), num_clicks(0 ), threshold_doubleclick(thr_doubleclick), threshold_misclick(thr_misclick), output_type(output_type0), geometry(geometry0)
35- {
36- }
34+ // static instance
35+ bool Calibrator::verbose = false ;
3736
38- void Calibrator::set_threshold_doubleclick (int t)
39- {
40- threshold_doubleclick = t;
41- }
42-
43- void Calibrator::set_threshold_misclick (int t)
44- {
45- threshold_misclick = t;
46- }
47-
48- int Calibrator::get_numclicks ()
37+ Calibrator::Calibrator (const char * const device_name0, const XYinfo& axys0,
38+ const int thr_misclick, const int thr_doubleclick, const OutputType output_type0, const char * geometry0)
39+ : device_name(device_name0),
40+ threshold_doubleclick(thr_doubleclick), threshold_misclick(thr_misclick),
41+ output_type(output_type0), geometry(geometry0)
4942{
50- return num_clicks;
51- }
43+ old_axys = axys0;
5244
53- const char * Calibrator::get_geometry ()
54- {
55- return geometry ;
45+ clicked. num = 0 ;
46+ // clicked.x(NUM_POINTS);
47+ // clicked.y(NUM_POINTS) ;
5648}
5749
5850bool Calibrator::add_click (int x, int y)
5951{
6052 // Double-click detection
61- if (threshold_doubleclick > 0 && num_clicks > 0 ) {
62- int i = num_clicks- 1 ;
53+ if (threshold_doubleclick > 0 && clicked. num > 0 ) {
54+ int i = clicked. num - 1 ;
6355 while (i >= 0 ) {
64- if (abs (x - clicked_x [i]) <= threshold_doubleclick
65- && abs (y - clicked_y [i]) <= threshold_doubleclick) {
56+ if (abs (x - clicked. x [i]) <= threshold_doubleclick
57+ && abs (y - clicked. y [i]) <= threshold_doubleclick) {
6658 if (verbose) {
67- printf ( " DEBUG: Not adding click %i (X=%i, Y=%i): within %i pixels of previous click\n " ,
68- num_clicks , x, y, threshold_doubleclick);
59+ trace ( " Not adding click %i (X=%i, Y=%i): within %i pixels of previous click\n " ,
60+ clicked. num , x, y, threshold_doubleclick);
6961 }
7062 return false ;
7163 }
@@ -74,52 +66,61 @@ bool Calibrator::add_click(int x, int y)
7466 }
7567
7668 // Mis-click detection
77- if (threshold_misclick > 0 && num_clicks > 0 ) {
69+ if (threshold_misclick > 0 && clicked. num > 0 ) {
7870 bool misclick = true ;
7971
80- if (num_clicks == 1 ) {
81- // check that along one axis of first point
82- if (along_axis (x,clicked_x[0 ],clicked_y[0 ]) ||
83- along_axis (y,clicked_x[0 ],clicked_y[0 ]))
84- misclick = false ;
85- } else if (num_clicks == 2 ) {
86- // check that along other axis of first point than second point
87- if ((along_axis (y,clicked_x[0 ],clicked_y[0 ]) &&
88- along_axis (clicked_x[1 ],clicked_x[0 ],clicked_y[0 ])) ||
89- (along_axis (x,clicked_x[0 ],clicked_y[0 ]) &&
90- along_axis (clicked_y[1 ],clicked_x[0 ],clicked_y[0 ])))
91- misclick = false ;
92- } else if (num_clicks == 3 ) {
93- // check that along both axis of second and third point
94- if ((along_axis (x,clicked_x[1 ],clicked_y[1 ]) &&
95- along_axis (y,clicked_x[2 ],clicked_y[2 ])) ||
96- (along_axis (y,clicked_x[1 ],clicked_y[1 ]) &&
97- along_axis (x,clicked_x[2 ],clicked_y[2 ])))
98- misclick = false ;
72+ switch (clicked.num ) {
73+ case 1 :
74+ // check that along one axis of first point
75+ if (along_axis (x,clicked.x [UL],clicked.y [UL]) ||
76+ along_axis (y,clicked.x [UL],clicked.y [UL]))
77+ {
78+ misclick = false ;
79+ } else {
80+ trace ( " Mis-click detected, click %i (X=%i, Y=%i) not aligned with click 0 (X=%i, Y=%i) (threshold=%i)\n " ,
81+ clicked.num , x, y, clicked.x [UL], clicked.y [UL], threshold_misclick);
82+ }
83+ break ;
84+
85+ case 2 :
86+ // check that along other axis of first point than second point
87+ if ((along_axis ( y, clicked.x [UL], clicked.y [UL])
88+ && along_axis ( clicked.x [UR], clicked.x [UL], clicked.y [UL]))
89+ || (along_axis ( x, clicked.x [UL], clicked.y [UL])
90+ && along_axis ( clicked.y [UR], clicked.x [UL], clicked.y [UL])))
91+ {
92+ misclick = false ;
93+ } else {
94+ trace ( " Mis-click detected, click %i (X=%i, Y=%i) not aligned with click 0 (X=%i, Y=%i) or click 1 (X=%i, Y=%i) (threshold=%i)\n " ,
95+ clicked.num , x, y, clicked.x [UL], clicked.y [UL], clicked.x [UR], clicked.y [UR], threshold_misclick);
96+ }
97+ break ;
98+
99+ case 3 :
100+ // check that along both axis of second and third point
101+ if ( ( along_axis ( x, clicked.x [UR], clicked.y [UR])
102+ && along_axis ( y, clicked.x [LL], clicked.y [LL]) )
103+ ||( along_axis ( y, clicked.x [UR], clicked.y [UR])
104+ && along_axis ( x, clicked.x [LL], clicked.y [LL]) ) )
105+ {
106+ misclick = false ;
107+ } else {
108+ trace ( " Mis-click detected, click %i (X=%i, Y=%i) not aligned with click 1 (X=%i, Y=%i) or click 2 (X=%i, Y=%i) (threshold=%i)\n " ,
109+ clicked.num , x, y, clicked.x [UR], clicked.y [UR], clicked.x [LL], clicked.y [LL], threshold_misclick);
110+ }
99111 }
100112
101113 if (misclick) {
102- if (verbose) {
103- if (num_clicks == 1 )
104- printf (" DEBUG: Mis-click detected, click %i (X=%i, Y=%i) not aligned with click 0 (X=%i, Y=%i) (threshold=%i)\n " , num_clicks, x, y, clicked_x[0 ], clicked_y[0 ], threshold_misclick);
105- else if (num_clicks == 2 )
106- printf (" DEBUG: Mis-click detected, click %i (X=%i, Y=%i) not aligned with click 0 (X=%i, Y=%i) or click 1 (X=%i, Y=%i) (threshold=%i)\n " , num_clicks, x, y, clicked_x[0 ], clicked_y[0 ], clicked_x[1 ], clicked_y[1 ], threshold_misclick);
107- else if (num_clicks == 3 )
108- printf (" DEBUG: Mis-click detected, click %i (X=%i, Y=%i) not aligned with click 1 (X=%i, Y=%i) or click 2 (X=%i, Y=%i) (threshold=%i)\n " , num_clicks, x, y, clicked_x[1 ], clicked_y[1 ], clicked_x[2 ], clicked_y[2 ], threshold_misclick);
109- }
110-
111114 reset ();
112115 return false ;
113116 }
114117 }
115118
116- clicked_x[num_clicks] = x;
117- clicked_y[num_clicks] = y;
118- num_clicks++;
119-
120- if (verbose)
121- printf (" DEBUG: Adding click %i (X=%i, Y=%i)\n " , num_clicks-1 , x, y);
119+ clicked.x .push_back (x);
120+ clicked.y .push_back (y);
121+ clicked.num ++;
122122
123+ trace (" Adding click %i (X=%i, Y=%i)\n " , clicked.num -1 , x, y);
123124 return true ;
124125}
125126
@@ -129,47 +130,86 @@ inline bool Calibrator::along_axis(int xy, int x0, int y0)
129130 (abs (xy - y0) <= threshold_misclick));
130131}
131132
133+ // / Compute calibration on 1 axis
134+ // / (all +0.5 for float to int rounding)
135+ void Calibrator::process_axys ( int screen_dim, const AxisInfo &previous, std::vector<int > &clicked, AxisInfo &updated )
136+ {
137+ // These are scaled using the values of old_axys
138+ const float old_scale = (previous.max - previous.min )/(float )screen_dim;
139+
140+ // Sort to get lowest two and highest two whatever is the orientation
141+ std::sort ( clicked.begin (), clicked.end ());
142+ // If inverted, must undo inversion since calibration is before in evdev driver.
143+ if ( previous.invert ) {
144+ updated.min = ( (2 *screen_dim - clicked[2 ] - clicked[3 ]) * old_scale/2 ) + previous.min + 0.5 ;
145+ updated.max = ( (2 *screen_dim - clicked[0 ] - clicked[1 ]) * old_scale/2 ) + previous.min + 0.5 ;
146+ } else {
147+ updated.min = ( (clicked[0 ] + clicked[1 ]) * old_scale/2 ) + previous.min + 0.5 ;
148+ updated.max = ( (clicked[2 ] + clicked[3 ]) * old_scale/2 ) + previous.min + 0.5 ;
149+ }
150+
151+ // Add/subtract the offset that comes from not having the points in the
152+ // corners (using the new scale, assumed better than previous)
153+ const int new_delta = (updated.max - updated.min ) / (float )(num_blocks - 2 ) + 0.5 ;
154+ updated.min -= new_delta;
155+ updated.max += new_delta;
156+ }
157+
158+ // Compute calibration and correct orientation from captured coordinates
132159bool Calibrator::finish (int width, int height)
133160{
134- if (get_numclicks () != 4 ) {
161+ if (get_numclicks () != NUM_POINTS ) {
135162 return false ;
136163 }
137164
138- // Should x and y be swapped?
139- const bool swap_xy = (abs (clicked_x [UL] - clicked_x [UR]) < abs (clicked_y [UL] - clicked_y [UR]));
140- if (swap_xy) {
141- std::swap (clicked_x[LL], clicked_x[UR]);
142- std::swap (clicked_y[LL], clicked_y[UR]);
165+ trace ( " Screen size=%dx%d\n " , width, height );
166+ trace ( " Expected screen coordinates, x.min=%d, x.max=%d, y.min=%d, y.max=%d\n " ,
167+ width/num_blocks, width-width/num_blocks,
168+ height/num_blocks, height - height/num_blocks);
169+
170+ // Evdev v2.3.2 order to compute coordinates from peripheral to screen:
171+ // - swap xy axis
172+ // - calibration (offset and scale)
173+ // - invert x, invert y axis
174+
175+ trace ( " Previous orientation: swap_xy=%d, invert_x=%d, invert_y=%d\n " , old_axys.swap_xy , old_axys.x .invert , old_axys.y .invert );
176+
177+ // Compute orientation modifications from existing (if orientation is already corrected, no change)
178+ // Start reverse order vs. evdev: from screen to peripheral
179+
180+ // Check if axes are inverted ?
181+ bool invert_x = false , invert_y = false ;
182+ if ( clicked.x [UL] > clicked.x [LR] ) {
183+ invert_x = true ;
143184 }
144185
145- // Compute min/max coordinates.
146- XYinfo axys;
147- // These are scaled using the values of old_axys
148- const float scale_x = (old_axys.x_max - old_axys.x_min )/(float )width;
149- axys.x_min = ((clicked_x[UL] + clicked_x[LL]) * scale_x/2 ) + old_axys.x_min ;
150- axys.x_max = ((clicked_x[UR] + clicked_x[LR]) * scale_x/2 ) + old_axys.x_min ;
151- const float scale_y = (old_axys.y_max - old_axys.y_min )/(float )height;
152- axys.y_min = ((clicked_y[UL] + clicked_y[UR]) * scale_y/2 ) + old_axys.y_min ;
153- axys.y_max = ((clicked_y[LL] + clicked_y[LR]) * scale_y/2 ) + old_axys.y_min ;
186+ if ( clicked.y [UL] > clicked.y [LR] ) {
187+ invert_y = true ;
188+ }
154189
155- // Add/subtract the offset that comes from not having the points in the
156- // corners (using the same coordinate system they are currently in)
157- const int delta_x = (axys.x_max - axys.x_min ) / (float )(num_blocks - 2 );
158- axys.x_min -= delta_x;
159- axys.x_max += delta_x;
160- const int delta_y = (axys.y_max - axys.y_min ) / (float )(num_blocks - 2 );
161- axys.y_min -= delta_y;
162- axys.y_max += delta_y;
190+ XYinfo new_axys; // new axys origin and scaling
163191
192+ // Should x and y be swapped
193+ const bool swap_xy = (abs (clicked.x [UL] - clicked.x [UR]) < abs (clicked.y [UL] - clicked.y [UR]));
194+ trace ( " Orientation modifications: swap_xy=%d, invert_x=%d, invert_y=%d\n " , swap_xy, invert_x, invert_y);
164195
165- // If x and y has to be swapped we also have to swap the parameters
196+ // / Compute calibration
166197 if (swap_xy) {
167- std::swap (axys.x_min , axys.y_max );
168- std::swap (axys.y_min , axys.x_max );
198+ process_axys ( height, old_axys.x , clicked.y , new_axys.x );
199+ process_axys ( width, old_axys.y , clicked.x , new_axys.y );
200+ } else {
201+ process_axys ( width, old_axys.x , clicked.x , new_axys.x );
202+ process_axys ( height, old_axys.y , clicked.y , new_axys.y );
169203 }
170204
205+ new_axys.swap_xy = old_axys.swap_xy ^ swap_xy;
206+ new_axys.x .invert = old_axys.x .invert ^ invert_x;
207+ new_axys.y .invert = old_axys.y .invert ^ invert_y;
208+
209+ trace ( " New orientation: swap_xy=%d, invert_x=%d, invert_y=%d\n " , new_axys.swap_xy , new_axys.x .invert , new_axys.y .invert );
210+
171211 // finish the data, driver/calibrator specific
172- return finish_data (axys, swap_xy );
212+ return finish_data (new_axys );
173213}
174214
175215const char * Calibrator::get_sysfs_name ()
@@ -202,8 +242,7 @@ bool Calibrator::is_sysfs_name(const char* name) {
202242 std::string devname;
203243 std::getline (ifile, devname);
204244 if (devname == name) {
205- if (verbose)
206- printf (" DEBUG: Found that '%s' is a sysfs name.\n " , name);
245+ trace (" Found that '%s' is a sysfs name.\n " , name);
207246 return true ;
208247 }
209248 }
@@ -213,9 +252,8 @@ bool Calibrator::is_sysfs_name(const char* name) {
213252 }
214253 (void ) closedir (dp);
215254
216- if (verbose)
217- printf (" DEBUG: Name '%s' does not match any in '%s/event*/%s'\n " ,
218- name, SYSFS_INPUT, SYSFS_DEVNAME);
255+ trace (" Name '%s' does not match any in '%s/event*/%s'\n " ,
256+ name, SYSFS_INPUT, SYSFS_DEVNAME);
219257 return false ;
220258}
221259
@@ -227,7 +265,7 @@ bool Calibrator::has_xorgconfd_support(Display* dpy) {
227265 display = XOpenDisplay (NULL );
228266
229267 if (display == NULL ) {
230- fprintf (stderr, " Unable to connect to X server\n " );
268+ error ( " Unable to connect to X server\n " );
231269 exit (1 );
232270 }
233271
@@ -241,3 +279,46 @@ bool Calibrator::has_xorgconfd_support(Display* dpy) {
241279
242280 return has_support;
243281}
282+
283+ // / Write calibration output (to stdout)
284+ void Calibrator::output ( const char *format, ... )
285+ {
286+ va_list vl;
287+ va_start ( vl, format );
288+ vprintf ( format, vl );
289+ va_end ( vl );
290+ }
291+
292+ // / Dump debug information if verbose activated
293+ void Calibrator::trace ( const char *format, ...)
294+ {
295+ if ( verbose == true ) {
296+ printf ( " DEBUG: " );
297+ va_list vl;
298+ va_start ( vl, format );
299+ vprintf ( format, vl );
300+ va_end ( vl );
301+ }
302+ }
303+
304+ // / Information to user, if verbose mode activated
305+ void Calibrator::info ( const char *format, ... )
306+ {
307+ if ( verbose == true ) {
308+ printf ( " INFO: " );
309+ va_list vl;
310+ va_start ( vl, format );
311+ vprintf ( format, vl );
312+ va_end ( vl );
313+ }
314+ }
315+
316+ // / Error (non fatal)
317+ void Calibrator::error ( const char *format, ...)
318+ {
319+ fprintf ( stderr, " ERROR: " );
320+ va_list vl;
321+ va_start ( vl, format );
322+ vprintf ( format, vl );
323+ va_end ( vl );
324+ }
0 commit comments