Skip to content

Commit e4ffde5

Browse files
tonio73tias
authored andcommitted
Managing correctly X/Y swap and X or Y inversion: - detect swap and inversion modification - take into account for inversion in calibration computation since Evdev is doing inversion after calibration. - Mainly tested for Evdev based driver. To be tested for USB.
Sorting outputs to stdout in such a way to only get calibration commands at output => scriptable.
1 parent 62ca678 commit e4ffde5

File tree

9 files changed

+538
-393
lines changed

9 files changed

+538
-393
lines changed

src/calibrator.cpp

Lines changed: 176 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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
@@ -26,46 +27,37 @@
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

5850
bool 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
132159
bool 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

175215
const 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

Comments
 (0)