Skip to content
17 changes: 15 additions & 2 deletions src/calibrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,26 @@ Calibrator::Calibrator(const char* const device_name0, const XYinfo& axys0,
threshold_doubleclick(thr_doubleclick), threshold_misclick(thr_misclick),
output_type(output_type0), geometry(geometry0)
{
old_axys = axys0;
orig_axys = old_axys = axys0;

clicked.num = 0;
//clicked.x(NUM_POINTS);
//clicked.y(NUM_POINTS);
}

bool Calibrator::apply(XYinfo new_axis)
{
fprintf(stderr, "Dynamic recalibration not supported for this device.\n");
return false;
}

void Calibrator::abort()
{
if (verbose)
printf("DEBUG: Restoring original calibration parameters:\n");
apply(orig_axys);
}

bool Calibrator::add_click(int x, int y)
{
// Double-click detection
Expand Down Expand Up @@ -139,7 +152,7 @@ bool Calibrator::finish(int width, int height)
}

// new axis origin and scaling
// based on old_axys: inversion/swapping is relative to the old axis
// based on old_axys: swapping is relative to the old axis
XYinfo new_axis(old_axys);


Expand Down
29 changes: 19 additions & 10 deletions src/calibrator.hh
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,16 @@ const int num_blocks = 8;

struct AxisInfo {
int min, max;
bool invert;

AxisInfo() : min(-1), max(-1), invert(false) { }
AxisInfo() : min(-1), max(-1) { }
AxisInfo(int mi, int ma, bool inv = false) :
min(mi), max(ma), invert(inv) { }
min(mi), max(ma) { }
AxisInfo(const AxisInfo& old) :
min(old.min), max(old.max), invert(old.invert) { }
min(old.min), max(old.max) { }

void do_invert() {
invert = !invert;
}
int real_min() const { return std::min(min, max); }
int real_max() const { return std::max(min, max); }
bool is_inverted() const { return min > max; }
};

/// struct to hold min/max info of the X and Y axis
Expand All @@ -100,8 +99,8 @@ struct XYinfo {
}

void print(const char* xtra="\n") {
printf("XYinfo: x.min=%i, x.max=%i, y.min=%i, y.max=%i, swap_xy=%i, invert_x=%i, invert_y=%i%s",
x.min, x.max, y.min, y.max, swap_xy, x.invert, y.invert, xtra);
printf("XYinfo: x.min=%i, x.max=%i, y.min=%i, y.max=%i, swap_xy=%i%s",
x.min, x.max, y.min, y.max, swap_xy, xtra);
}
};

Expand Down Expand Up @@ -168,10 +167,18 @@ public:
void reset()
{ clicked.num = 0; clicked.x.clear(); clicked.y.clear();}

virtual void detect_axys() { return; }
void set_old_axys(XYinfo x) { old_axys = x; }

virtual bool apply(const XYinfo new_axys);

// restore input device state
void abort();

/// add a click with the given coordinates
bool add_click(int x, int y);
/// calculate and apply the calibration
virtual bool finish(int width, int height);
bool finish(int width, int height);
/// get the sysfs name of the device,
/// returns NULL if it can not be found
const char* get_sysfs_name();
Expand All @@ -197,6 +204,8 @@ protected:
const char* const device_name;

/// Original values
XYinfo orig_axys;
/// Values in use during calibration
XYinfo old_axys;

/// Be verbose or not
Expand Down
237 changes: 93 additions & 144 deletions src/calibrator/Evdev.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ CalibratorEvdev::CalibratorEvdev(const char* const device_name0,

#ifndef HAVE_XI_PROP
throw WrongCalibratorException("Evdev: you need at least libXi 1.2 and inputproto 1.5 for dynamic recalibration of evdev.");
#else
#endif

// XGetDeviceProperty vars
Atom property;
Atom act_type;
int act_format;
unsigned long nitems, bytes_after;
unsigned char *data, *ptr;
unsigned char *data;

// get "Evdev Axis Calibration" property
property = xinput_parse_atom(display, "Evdev Axis Calibration");
Expand All @@ -91,34 +91,67 @@ CalibratorEvdev::CalibratorEvdev(const char* const device_name0,
XCloseDevice(display, dev);
XCloseDisplay(display);
throw WrongCalibratorException("Evdev: \"Evdev Axis Calibration\" property missing, not a (valid) evdev device");

} else {
if (act_format != 32 || act_type != XA_INTEGER) {
XCloseDevice(display, dev);
XCloseDisplay(display);
throw WrongCalibratorException("Evdev: invalid \"Evdev Axis Calibration\" property format");
XFree(data);
}

} else if (nitems == 0) {
if (verbose)
printf("DEBUG: Evdev Axis Calibration not set, setting to axis valuators to be sure.\n");

// No axis calibration set, set it to the default one
// QUIRK: when my machine resumes from a sleep,
// the calibration property is no longer exported through xinput, but still active
// not setting the values here would result in a wrong first calibration
(void) set_calibration(old_axys);

} else if (nitems > 0) {
ptr = data;

old_axys.x.min = *((long*)ptr);
ptr += sizeof(long);
old_axys.x.max = *((long*)ptr);
ptr += sizeof(long);
old_axys.y.min = *((long*)ptr);
ptr += sizeof(long);
old_axys.y.max = *((long*)ptr);
ptr += sizeof(long);
printf("Calibrating EVDEV driver for \"%s\" id=%i\n", device_name, (int)device_id);
}

// protected pass-through constructor for subclasses
CalibratorEvdev::CalibratorEvdev(const char* const device_name0,
const XYinfo& axys0,
const int thr_misclick,
const int thr_doubleclick,
const OutputType output_type,
const char* geometry)
: Calibrator(device_name0, axys0, thr_misclick, thr_doubleclick, output_type, geometry) { }

// Destructor
CalibratorEvdev::~CalibratorEvdev () {
XCloseDevice(display, dev);
XCloseDisplay(display);
}

void CalibratorEvdev::detect_axys()
{
// XGetDeviceProperty vars
Atom property;
Atom act_type;
int act_format;
unsigned long nitems, bytes_after;
unsigned char *data, *ptr;

// get "Evdev Axis Calibration" property
property = xinput_parse_atom(display, "Evdev Axis Calibration");
if (XGetDeviceProperty(display, dev, property, 0, 1000, False,
AnyPropertyType, &act_type, &act_format,
&nitems, &bytes_after, &data) == Success)
{

if (act_format == 32 && act_type == XA_INTEGER) {
if (nitems == 0) {
if (verbose)
printf("DEBUG: Evdev Axis Calibration not set, setting to axis valuators to be sure.\n");

// No axis calibration set, set it to the default one
// QUIRK: when my machine resumes from a sleep, the calibration
// property is no longer exported through xinput, but still
// active not setting the values here would result in a wrong
// first calibration
(void) set_calibration(old_axys);
} else {
ptr = data;

old_axys.x.min = *((long*)ptr);
ptr += sizeof(long);
old_axys.x.max = *((long*)ptr);
ptr += sizeof(long);
old_axys.y.min = *((long*)ptr);
ptr += sizeof(long);
old_axys.y.max = *((long*)ptr);
ptr += sizeof(long);
}
}

XFree(data);
Expand All @@ -144,137 +177,31 @@ CalibratorEvdev::CalibratorEvdev(const char* const device_name0,
AnyPropertyType, &act_type, &act_format,
&nitems, &bytes_after, &data) == Success) {
if (act_format == 8 && act_type == XA_INTEGER && nitems == 2) {
old_axys.x.invert = *((char*)data++);
old_axys.y.invert = *((char*)data);
bool invert_x = *((char*)data++);
bool invert_y = *((char*)data);

if (verbose)
printf("DEBUG: Read InvertX=%i, InvertY=%i.\n", old_axys.x.invert, old_axys.y.invert);
printf("DEBUG: Read InvertX=%i, InvertY=%i.\n", invert_x, invert_y);

if (invert_x)
std::swap(old_axys.x.min, old_axys.x.max);
if (invert_y)
std::swap(old_axys.y.min, old_axys.y.max);
}
}

printf("Calibrating EVDEV driver for \"%s\" id=%i\n", device_name, (int)device_id);
printf("\tcurrent calibration values (from XInput): min_x=%d, max_x=%d and min_y=%d, max_y=%d\n",
old_axys.x.min, old_axys.x.max, old_axys.y.min, old_axys.y.max);
#endif // HAVE_XI_PROP

}
// protected pass-through constructor for subclasses
CalibratorEvdev::CalibratorEvdev(const char* const device_name0,
const XYinfo& axys0,
const int thr_misclick,
const int thr_doubleclick,
const OutputType output_type,
const char* geometry)
: Calibrator(device_name0, axys0, thr_misclick, thr_doubleclick, output_type, geometry) { }

// Destructor
CalibratorEvdev::~CalibratorEvdev () {
XCloseDevice(display, dev);
XCloseDisplay(display);
}

// From Calibrator but with evdev specific invertion option
// KEEP IN SYNC with Calibrator::finish() !!
bool CalibratorEvdev::finish(int width, int height)
{
if (get_numclicks() != NUM_POINTS) {
return false;
}

// new axis origin and scaling
// based on old_axys: inversion/swapping is relative to the old axis
XYinfo new_axis(old_axys);


// calculate average of clicks
float x_min = (clicked.x[UL] + clicked.x[LL])/2.0;
float x_max = (clicked.x[UR] + clicked.x[LR])/2.0;
float y_min = (clicked.y[UL] + clicked.y[UR])/2.0;
float y_max = (clicked.y[LL] + clicked.y[LR])/2.0;


// When evdev detects an invert_X/Y option,
// it performs the following *crazy* code just before returning
// val = (pEvdev->absinfo[i].maximum - val + pEvdev->absinfo[i].minimum);
// undo this crazy step before doing the regular calibration routine
if (old_axys.x.invert) {
x_min = width - x_min;
x_max = width - x_max;
// avoid invert_x property from here on,
// the calibration code can handle this dynamically!
new_axis.x.invert = false;
}
if (old_axys.y.invert) {
y_min = height - y_min;
y_max = height - y_max;
// avoid invert_y property from here on,
// the calibration code can handle this dynamically!
new_axis.y.invert = false;
}
// end of evdev inversion crazyness


// Should x and y be swapped?
if (abs(clicked.x[UL] - clicked.x[UR]) < abs(clicked.y[UL] - clicked.y[UR])) {
new_axis.swap_xy = !new_axis.swap_xy;
std::swap(x_min, y_min);
std::swap(x_max, y_max);
}

// the screen was divided in num_blocks blocks, and the touch points were at
// one block away from the true edges of the screen.
const float block_x = width/(float)num_blocks;
const float block_y = height/(float)num_blocks;
// rescale these blocks from the range of the drawn touchpoints to the range of the
// actually clicked coordinates, and substract/add from the clicked coordinates
// to obtain the coordinates corresponding to the edges of the screen.
float scale_x = (x_max - x_min)/(width - 2*block_x);
x_min -= block_x * scale_x;
x_max += block_x * scale_x;
float scale_y = (y_max - y_min)/(height - 2*block_y);
y_min -= block_y * scale_y;
y_max += block_y * scale_y;

// now, undo the transformations done by the X server, to obtain the true 'raw' value in X.
// The raw value was scaled from old_axis to the device min/max, and from the device min/max
// to the screen min/max
// hence, the reverse transformation is from screen to old_axis
x_min = scaleAxis(x_min, old_axys.x.max, old_axys.x.min, width, 0);
x_max = scaleAxis(x_max, old_axys.x.max, old_axys.x.min, width, 0);
y_min = scaleAxis(y_min, old_axys.y.max, old_axys.y.min, height, 0);
y_max = scaleAxis(y_max, old_axys.y.max, old_axys.y.min, height, 0);


// round and put in new_axis struct
new_axis.x.min = round(x_min); new_axis.x.max = round(x_max);
new_axis.y.min = round(y_min); new_axis.y.max = round(y_max);

// finish the data, driver/calibrator specific
return finish_data(new_axis);
orig_axys = old_axys;
}

// Activate calibrated data and output it
bool CalibratorEvdev::finish_data(const XYinfo new_axys)
{
bool success = true;

printf("\nDoing dynamic recalibration:\n");
// Evdev Axes Swap
if (old_axys.swap_xy != new_axys.swap_xy) {
success &= set_swapxy(new_axys.swap_xy);
}

// Evdev Axis Inversion
if (old_axys.x.invert != new_axys.x.invert ||
old_axys.y.invert != new_axys.y.invert) {
success &= set_invert_xy(new_axys.x.invert, new_axys.y.invert);
}

// Evdev Axis Calibration
success &= set_calibration(new_axys);

// close
XSync(display, False);
success &= apply(new_axys);

printf("\t--> Making the calibration permanent <--\n");
switch (output_type) {
Expand Down Expand Up @@ -367,6 +294,28 @@ bool CalibratorEvdev::set_calibration(const XYinfo new_axys)
return ret;
}

bool CalibratorEvdev::apply(const XYinfo new_axys)
{
bool success = true;

printf("\nDoing dynamic recalibration:\n");
// Evdev Axes Swap
if (old_axys.swap_xy != new_axys.swap_xy) {
success &= set_swapxy(new_axys.swap_xy);
}

// Evdev Axis Inversion
set_invert_xy(false, false);

// Evdev Axis Calibration
success &= set_calibration(new_axys);

// close
XSync(display, False);

return success;
}

Atom CalibratorEvdev::xinput_parse_atom(Display *display, const char *name)
{
Bool is_atom = True;
Expand Down
Loading