Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various fixes & improvements #36

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
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