weston/clients/touch-calibrator.c

973 lines
21 KiB
C
Raw Normal View History

clients: add a new touchscreen calibrator The new calibrator uses weston_touch_calibration protocol extension and provides the following features: - chooses the physical touch device to be calibrated by DEVPATH or by the output/head name; device enumeration provided - the compositor ensures the calibrator window is shown in the correct position and size - no matter how wrong the old calibration is, the touch events will always arrive in the application - the calibration is complete, not incremental; the received touch events are guaranteed to be unmodified - computes a libinput style calibration matrix directly, not the WL_CALIBRATION format - supports multiple touch devices: calibrate one device at a time, and show user feedback on touching a wrong device instead of recording bad data - uses four touch point samples: three to compute the calibration, and one to verify the calibration is roughly correct - consistent exit codes - upload the new calibration into the server after successful and verified calibration Due to using special touchscreen calibration protocol extension, this application cannot be tested without touch input from the compositor. Practically all of the above mentioned are unlike how the old calibrator client worked. Co-developed by Louis-Francis and Pekka. v2: - improve help() text - rename wrong_touch_handler() to invalid_touch_handler() - improve debug prints by adding sample number - reorganize code into sample funcs vs. touch funcs - add a state machine to properly process touch and related events Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> v1 Tested-by: Matt Hoosier <matt.hoosier@gmail.com> Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-11-30 00:38:44 +03:00
/*
* Copyright 2012 Intel Corporation
* Copyright 2017-2018 Collabora, Ltd.
* Copyright 2017-2018 General Electric Company
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cairo.h>
#include <math.h>
#include <assert.h>
#include <getopt.h>
#include <errno.h>
#include <wayland-client.h>
#include "clients/window.h"
#include "shared/helpers.h"
#include <libweston/matrix.h>
clients: add a new touchscreen calibrator The new calibrator uses weston_touch_calibration protocol extension and provides the following features: - chooses the physical touch device to be calibrated by DEVPATH or by the output/head name; device enumeration provided - the compositor ensures the calibrator window is shown in the correct position and size - no matter how wrong the old calibration is, the touch events will always arrive in the application - the calibration is complete, not incremental; the received touch events are guaranteed to be unmodified - computes a libinput style calibration matrix directly, not the WL_CALIBRATION format - supports multiple touch devices: calibrate one device at a time, and show user feedback on touching a wrong device instead of recording bad data - uses four touch point samples: three to compute the calibration, and one to verify the calibration is roughly correct - consistent exit codes - upload the new calibration into the server after successful and verified calibration Due to using special touchscreen calibration protocol extension, this application cannot be tested without touch input from the compositor. Practically all of the above mentioned are unlike how the old calibrator client worked. Co-developed by Louis-Francis and Pekka. v2: - improve help() text - rename wrong_touch_handler() to invalid_touch_handler() - improve debug prints by adding sample number - reorganize code into sample funcs vs. touch funcs - add a state machine to properly process touch and related events Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> v1 Tested-by: Matt Hoosier <matt.hoosier@gmail.com> Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-11-30 00:38:44 +03:00
#include "weston-touch-calibration-client-protocol.h"
enum exit_code {
CAL_EXIT_SUCCESS = 0,
CAL_EXIT_ERROR = 1,
CAL_EXIT_CANCELLED = 2,
};
static int debug_;
static int verbose_;
#define pr_ver(...) do { \
if (verbose_) \
printf(__VA_ARGS__); \
} while (0)
#define pr_dbg(...) do { \
if (debug_) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
static void
pr_err(const char *fmt, ...) WL_PRINTF(1, 2);
/* Our points for the calibration must be not be on a line */
static const struct {
float x_ratio, y_ratio;
} test_ratios[] = {
{ 0.15, 0.10 }, /* three points for calibration */
{ 0.85, 0.13 },
{ 0.20, 0.80 },
{ 0.70, 0.75 } /* and one for verification */
};
#define NR_SAMPLES ((int)ARRAY_LENGTH(test_ratios))
struct point {
double x;
double y;
};
struct sample {
int ind;
struct point drawn; /**< drawn point, pixels */
struct weston_touch_coordinate *pending;
struct point drawn_cal; /**< drawn point, converted */
bool conv_done;
struct point touched; /**< touch point, normalized */
bool touch_done;
};
struct poly {
struct color {
double r, g, b, a;
} color;
int n_verts;
const struct point *verts;
};
/** Touch event handling state machine
*
* Only a complete down->up->frame sequence should be accepted with user
* feedback "right", and anything that deviates from that (invalid_touch,
* cancel, multiple touch-downs) needs to undo the current sample and
* possibly show user feedback "wrong".
*
* \<STATE\>
* - \<triggers\>: \<actions\>
clients: add a new touchscreen calibrator The new calibrator uses weston_touch_calibration protocol extension and provides the following features: - chooses the physical touch device to be calibrated by DEVPATH or by the output/head name; device enumeration provided - the compositor ensures the calibrator window is shown in the correct position and size - no matter how wrong the old calibration is, the touch events will always arrive in the application - the calibration is complete, not incremental; the received touch events are guaranteed to be unmodified - computes a libinput style calibration matrix directly, not the WL_CALIBRATION format - supports multiple touch devices: calibrate one device at a time, and show user feedback on touching a wrong device instead of recording bad data - uses four touch point samples: three to compute the calibration, and one to verify the calibration is roughly correct - consistent exit codes - upload the new calibration into the server after successful and verified calibration Due to using special touchscreen calibration protocol extension, this application cannot be tested without touch input from the compositor. Practically all of the above mentioned are unlike how the old calibrator client worked. Co-developed by Louis-Francis and Pekka. v2: - improve help() text - rename wrong_touch_handler() to invalid_touch_handler() - improve debug prints by adding sample number - reorganize code into sample funcs vs. touch funcs - add a state machine to properly process touch and related events Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> v1 Tested-by: Matt Hoosier <matt.hoosier@gmail.com> Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-11-30 00:38:44 +03:00
*
* IDLE
* - touch down: sample, -> DOWN
* - touch up: no-op
* - frame: no-op
* - invalid_touch: (undo), wrong, -> WAIT
* - cancel: no-op
* DOWN (first touch down)
* - touch down: undo, wrong, -> WAIT
* - touch up: -> UP
* - frame: no-op
* - invalid_touch: undo, wrong, -> WAIT
* - cancel: undo, -> IDLE
* UP (first touch was down and up)
* - touch down: undo, wrong, -> WAIT
* - touch up: no-op
* - frame: right, touch finish, -> WAIT
* - invalid_touch: undo, wrong, -> WAIT
* - cancel: undo, -> IDLE
* WAIT (show user feedback)
* - touch down: no-op
* - touch up: no-op
* - frame, cancel, timer: if num_tp == 0 && timer_done -> IDLE
* - invalid_touch: no-op
*/
enum touch_state {
STATE_IDLE,
STATE_DOWN,
STATE_UP,
STATE_WAIT
};
struct calibrator {
struct sample samples[NR_SAMPLES];
int current_sample;
struct display *display;
struct weston_touch_calibration *calibration;
struct weston_touch_calibrator *calibrator;
struct window *window;
struct widget *widget;
int n_devices_listed;
char *match_name;
char *device_name;
int width;
int height;
bool cancelled;
const struct poly *current_poly;
bool exiting;
struct toytimer wait_timer;
bool timer_pending;
enum touch_state state;
int num_tp; /* touch points down count */
};
static struct sample *
current_sample(struct calibrator *cal)
{
return &cal->samples[cal->current_sample];
}
static void
sample_start(struct calibrator *cal, int i)
{
struct sample *s = &cal->samples[i];
assert(i >= 0 && i < NR_SAMPLES);
s->ind = i;
s->drawn.x = round(test_ratios[i].x_ratio * cal->width);
s->drawn.y = round(test_ratios[i].y_ratio * cal->height);
s->pending = NULL;
s->conv_done = false;
s->touch_done = false;
cal->current_sample = i;
}
static struct point
wire_to_point(uint32_t xu, uint32_t yu)
{
struct point p = {
.x = (double)xu / 0xffffffff,
.y = (double)yu / 0xffffffff
};
return p;
}
static void
sample_touch_down(struct calibrator *cal, uint32_t xu, uint32_t yu)
{
struct sample *s = current_sample(cal);
s->touched = wire_to_point(xu, yu);
s->touch_done = true;
pr_dbg("Down[%d] (%f, %f)\n", s->ind, s->touched.x, s->touched.y);
}
static void
coordinate_result_handler(void *data, struct weston_touch_coordinate *interface,
uint32_t xu, uint32_t yu)
{
struct sample *s = data;
weston_touch_coordinate_destroy(s->pending);
s->pending = NULL;
s->drawn_cal = wire_to_point(xu, yu);
s->conv_done = true;
pr_dbg("Conv[%d] (%f, %f)\n", s->ind, s->drawn_cal.x, s->drawn_cal.y);
}
struct weston_touch_coordinate_listener coordinate_listener = {
coordinate_result_handler
};
static void
sample_undo(struct calibrator *cal)
{
struct sample *s = current_sample(cal);
pr_dbg("Undo[%d]\n", s->ind);
s->touch_done = false;
s->conv_done = false;
if (s->pending) {
weston_touch_coordinate_destroy(s->pending);
s->pending = NULL;
}
}
static void
sample_finish(struct calibrator *cal)
{
struct sample *s = current_sample(cal);
pr_dbg("Finish[%d]\n", s->ind);
assert(!s->pending && !s->conv_done);
s->pending = weston_touch_calibrator_convert(cal->calibrator,
(int32_t)s->drawn.x,
(int32_t)s->drawn.y);
weston_touch_coordinate_add_listener(s->pending,
&coordinate_listener, s);
if (cal->current_sample + 1 < NR_SAMPLES) {
sample_start(cal, cal->current_sample + 1);
} else {
pr_dbg("got all touches\n");
cal->exiting = true;
}
}
/*
* Calibration algorithm:
*
* The equation we want to apply at event time where x' and y' are the
* calibrated co-ordinates.
*
* x' = Ax + By + C
* y' = Dx + Ey + F
*
* For example "zero calibration" would be A=1.0 B=0.0 C=0.0, D=0.0, E=1.0,
* and F=0.0.
*
* With 6 unknowns we need 6 equations to find the constants:
*
* x1' = Ax1 + By1 + C
* y1' = Dx1 + Ey1 + F
* ...
* x3' = Ax3 + By3 + C
* y3' = Dx3 + Ey3 + F
*
* In matrix form:
*
* x1' x1 y1 1 A
* x2' = x2 y2 1 x B
* x3' x3 y3 1 C
*
* So making the matrix M we can find the constants with:
*
* A x1'
* B = M^-1 x x2'
* C x3'
*
* (and similarly for D, E and F)
*
* For the calibration the desired values x, y are the same values at which
* we've drawn at.
*
*/
static int
compute_calibration(struct calibrator *cal, float *result)
{
struct weston_matrix m;
struct weston_matrix inverse;
struct weston_vector x_calib;
struct weston_vector y_calib;
int i;
assert(NR_SAMPLES >= 3);
/*
* x1 y1 1 0
* x2 y2 1 0
* x3 y3 1 0
* 0 0 0 1
*/
weston_matrix_init(&m);
for (i = 0; i < 3; i++) {
m.d[i + 0] = cal->samples[i].touched.x;
m.d[i + 4] = cal->samples[i].touched.y;
m.d[i + 8] = 1.0f;
}
m.type = WESTON_MATRIX_TRANSFORM_OTHER;
if (weston_matrix_invert(&inverse, &m) < 0) {
pr_err("non-invertible matrix during computation\n");
return -1;
}
for (i = 0; i < 3; i++) {
x_calib.f[i] = cal->samples[i].drawn_cal.x;
y_calib.f[i] = cal->samples[i].drawn_cal.y;
}
x_calib.f[3] = 0.0f;
y_calib.f[3] = 0.0f;
/* Multiples into the vector */
weston_matrix_transform(&inverse, &x_calib);
weston_matrix_transform(&inverse, &y_calib);
for (i = 0; i < 3; i++)
result[i] = x_calib.f[i];
for (i = 0; i < 3; i++)
result[i + 3] = y_calib.f[i];
return 0;
}
static int
verify_calibration(struct calibrator *cal, const float *r)
{
double thr = 0.1; /* accepted error radius */
struct point e; /* expected value; error */
const struct sample *s = &cal->samples[3];
/* transform raw touches through the matrix */
e.x = r[0] * s->touched.x + r[1] * s->touched.y + r[2];
e.y = r[3] * s->touched.x + r[4] * s->touched.y + r[5];
/* compute error */
e.x -= s->drawn_cal.x;
e.y -= s->drawn_cal.y;
pr_dbg("calibration test error: %f, %f\n", e.x, e.y);
if (e.x * e.x + e.y * e.y < thr * thr)
return 0;
pr_err("Calibration verification failed, too large error.\n");
return -1;
}
static void
send_calibration(struct calibrator *cal, float *values)
{
struct wl_array matrix;
float *f;
int i;
wl_array_init(&matrix);
for (i = 0; i < 6; i++) {
f = wl_array_add(&matrix, sizeof *f);
*f = values[i];
}
weston_touch_calibration_save(cal->calibration,
cal->device_name, &matrix);
wl_array_release(&matrix);
}
static const struct point cross_verts[] = {
{ 0.1, 0.2 },
{ 0.2, 0.1 },
{ 0.5, 0.4 },
{ 0.8, 0.1 },
{ 0.9, 0.2 },
{ 0.6, 0.5 },
{ 0.9, 0.8 },
{ 0.8, 0.9 },
{ 0.5, 0.6 },
{ 0.2, 0.9 },
{ 0.1, 0.8 },
{ 0.4, 0.5 },
};
/* a red cross, for "wrong" */
static const struct poly cross = {
.color = { 0.7, 0.0, 0.0, 1.0 },
.n_verts = ARRAY_LENGTH(cross_verts),
.verts = cross_verts
};
static const struct point check_verts[] = {
{ 0.5, 0.7 },
{ 0.8, 0.1 },
{ 0.9, 0.1 },
{ 0.55, 0.8 },
{ 0.45, 0.8 },
{ 0.3, 0.5 },
{ 0.4, 0.5 }
};
/* a green check mark, for "right" */
static const struct poly check = {
.color = { 0.0, 0.7, 0.0, 1.0 },
.n_verts = ARRAY_LENGTH(check_verts),
.verts = check_verts
};
static void
draw_poly(cairo_t *cr, const struct poly *poly)
{
int i;
cairo_set_source_rgba(cr, poly->color.r, poly->color.g,
poly->color.b, poly->color.a);
cairo_move_to(cr, poly->verts[0].x, poly->verts[0].y);
for (i = 1; i < poly->n_verts; i++)
cairo_line_to(cr, poly->verts[i].x, poly->verts[i].y);
cairo_close_path(cr);
cairo_fill(cr);
}
static void
feedback_show(struct calibrator *cal, const struct poly *what)
{
cal->current_poly = what;
widget_schedule_redraw(cal->widget);
toytimer_arm_once_usec(&cal->wait_timer, 1000 * 1000);
cal->timer_pending = true;
}
static void
feedback_hide(struct calibrator *cal)
{
cal->current_poly = NULL;
widget_schedule_redraw(cal->widget);
}
static void
try_enter_state_idle(struct calibrator *cal)
{
if (cal->num_tp != 0)
return;
if (cal->timer_pending)
return;
cal->state = STATE_IDLE;
feedback_hide(cal);
if (cal->exiting)
display_exit(cal->display);
}
static void
enter_state_wait(struct calibrator *cal)
{
assert(cal->timer_pending);
cal->state = STATE_WAIT;
}
static void
wait_timer_done(struct toytimer *tt)
{
struct calibrator *cal = container_of(tt, struct calibrator, wait_timer);
assert(cal->state == STATE_WAIT);
cal->timer_pending = false;
try_enter_state_idle(cal);
}
static void
redraw_handler(struct widget *widget, void *data)
{
struct calibrator *cal = data;
struct sample *s = current_sample(cal);
struct rectangle allocation;
cairo_surface_t *surface;
cairo_t *cr;
widget_get_allocation(cal->widget, &allocation);
assert(allocation.width == cal->width);
assert(allocation.height == cal->height);
surface = window_get_surface(cal->window);
cr = cairo_create(surface);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_paint(cr);
if (!cal->current_poly) {
cairo_translate(cr, s->drawn.x, s->drawn.y);
cairo_set_line_width(cr, 2.0);
cairo_set_source_rgb(cr, 0.7, 0.0, 0.0);
cairo_move_to(cr, 0, -10.0);
cairo_line_to(cr, 0, 10.0);
cairo_stroke(cr);
cairo_move_to(cr, -10.0, 0);
cairo_line_to(cr, 10.0, 0.0);
cairo_stroke(cr);
} else {
cairo_scale(cr, allocation.width, allocation.height);
draw_poly(cr, cal->current_poly);
}
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
static struct calibrator *
calibrator_create(struct display *display, const char *match_name)
{
struct calibrator *cal;
cal = zalloc(sizeof *cal);
if (!cal)
abort();
cal->match_name = match_name ? strdup(match_name) : NULL;
cal->window = window_create_custom(display);
cal->widget = window_add_widget(cal->window, cal);
window_inhibit_redraw(cal->window);
window_set_title(cal->window, "Touchscreen calibrator");
window_set_appid(cal->window,
"org.freedesktop.weston.touchscreen-calibrator");
clients: add a new touchscreen calibrator The new calibrator uses weston_touch_calibration protocol extension and provides the following features: - chooses the physical touch device to be calibrated by DEVPATH or by the output/head name; device enumeration provided - the compositor ensures the calibrator window is shown in the correct position and size - no matter how wrong the old calibration is, the touch events will always arrive in the application - the calibration is complete, not incremental; the received touch events are guaranteed to be unmodified - computes a libinput style calibration matrix directly, not the WL_CALIBRATION format - supports multiple touch devices: calibrate one device at a time, and show user feedback on touching a wrong device instead of recording bad data - uses four touch point samples: three to compute the calibration, and one to verify the calibration is roughly correct - consistent exit codes - upload the new calibration into the server after successful and verified calibration Due to using special touchscreen calibration protocol extension, this application cannot be tested without touch input from the compositor. Practically all of the above mentioned are unlike how the old calibrator client worked. Co-developed by Louis-Francis and Pekka. v2: - improve help() text - rename wrong_touch_handler() to invalid_touch_handler() - improve debug prints by adding sample number - reorganize code into sample funcs vs. touch funcs - add a state machine to properly process touch and related events Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com> Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk> v1 Tested-by: Matt Hoosier <matt.hoosier@gmail.com> Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2017-11-30 00:38:44 +03:00
cal->display = display;
widget_set_redraw_handler(cal->widget, redraw_handler);
toytimer_init(&cal->wait_timer, CLOCK_MONOTONIC,
display, wait_timer_done);
cal->state = STATE_IDLE;
cal->num_tp = 0;
return cal;
}
static void
configure_handler(void *data, struct weston_touch_calibrator *interface,
int32_t width, int32_t height)
{
struct calibrator *cal = data;
pr_dbg("Configure calibrator window to size %ix%i\n", width, height);
cal->width = width;
cal->height = height;
window_schedule_resize(cal->window, width, height);
window_uninhibit_redraw(cal->window);
sample_start(cal, 0);
widget_schedule_redraw(cal->widget);
}
static void
cancel_calibration_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
pr_dbg("calibration cancelled by the display server, quitting.\n");
cal->cancelled = true;
display_exit(cal->display);
}
static void
invalid_touch_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
pr_dbg("invalid touch\n");
switch (cal->state) {
case STATE_IDLE:
case STATE_DOWN:
case STATE_UP:
sample_undo(cal);
feedback_show(cal, &cross);
enter_state_wait(cal);
break;
case STATE_WAIT:
/* no-op */
break;
}
}
static void
down_handler(void *data, struct weston_touch_calibrator *interface,
uint32_t time, int32_t id, uint32_t xu, uint32_t yu)
{
struct calibrator *cal = data;
cal->num_tp++;
switch (cal->state) {
case STATE_IDLE:
sample_touch_down(cal, xu, yu);
cal->state = STATE_DOWN;
break;
case STATE_DOWN:
case STATE_UP:
sample_undo(cal);
feedback_show(cal, &cross);
enter_state_wait(cal);
break;
case STATE_WAIT:
/* no-op */
break;
}
if (cal->current_poly)
return;
}
static void
up_handler(void *data, struct weston_touch_calibrator *interface,
uint32_t time, int32_t id)
{
struct calibrator *cal = data;
cal->num_tp--;
if (cal->num_tp < 0) {
pr_dbg("Unmatched touch up.\n");
cal->num_tp = 0;
}
switch (cal->state) {
case STATE_DOWN:
cal->state = STATE_UP;
break;
case STATE_IDLE:
case STATE_UP:
case STATE_WAIT:
/* no-op */
break;
}
}
static void
motion_handler(void *data, struct weston_touch_calibrator *interface,
uint32_t time, int32_t id, uint32_t xu, uint32_t yu)
{
/* motion is ignored */
}
static void
frame_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
switch (cal->state) {
case STATE_IDLE:
case STATE_DOWN:
/* no-op */
break;
case STATE_UP:
feedback_show(cal, &check);
sample_finish(cal);
enter_state_wait(cal);
break;
case STATE_WAIT:
try_enter_state_idle(cal);
break;
}
}
static void
cancel_handler(void *data, struct weston_touch_calibrator *interface)
{
struct calibrator *cal = data;
cal->num_tp = 0;
switch (cal->state) {
case STATE_IDLE:
/* no-op */
break;
case STATE_DOWN:
case STATE_UP:
sample_undo(cal);
try_enter_state_idle(cal);
break;
case STATE_WAIT:
try_enter_state_idle(cal);
break;
}
}
struct weston_touch_calibrator_listener calibrator_listener = {
configure_handler,
cancel_calibration_handler,
invalid_touch_handler,
down_handler,
up_handler,
motion_handler,
frame_handler,
cancel_handler
};
static void
calibrator_show(struct calibrator *cal)
{
struct wl_surface *surface = window_get_wl_surface(cal->window);
cal->calibrator =
weston_touch_calibration_create_calibrator(cal->calibration,
surface,
cal->device_name);
weston_touch_calibrator_add_listener(cal->calibrator,
&calibrator_listener, cal);
}
static void
calibrator_destroy(struct calibrator *cal)
{
toytimer_fini(&cal->wait_timer);
if (cal->calibrator)
weston_touch_calibrator_destroy(cal->calibrator);
if (cal->calibration)
weston_touch_calibration_destroy(cal->calibration);
if (cal->widget)
widget_destroy(cal->widget);
if (cal->window)
window_destroy(cal->window);
free(cal->match_name);
free(cal->device_name);
free(cal);
}
static void
touch_device_handler(void *data, struct weston_touch_calibration *c,
const char *device, const char *head)
{
struct calibrator *cal = data;
cal->n_devices_listed++;
if (!cal->match_name) {
printf("device \"%s\" - head \"%s\"\n", device, head);
return;
}
if (cal->device_name)
return;
if (strcmp(cal->match_name, device) == 0 ||
strcmp(cal->match_name, head) == 0)
cal->device_name = strdup(device);
}
struct weston_touch_calibration_listener touch_calibration_listener = {
touch_device_handler
};
static void
global_handler(struct display *display, uint32_t name,
const char *interface, uint32_t version, void *data)
{
struct calibrator *cal = data;
if (strcmp(interface, "weston_touch_calibration") == 0) {
cal->calibration = display_bind(display, name,
&weston_touch_calibration_interface, 1);
weston_touch_calibration_add_listener(cal->calibration,
&touch_calibration_listener,
cal);
}
}
static int
calibrator_run(struct calibrator *cal)
{
struct wl_display *dpy;
struct sample *s;
bool wait;
int i;
int ret;
float result[6];
calibrator_show(cal);
display_run(cal->display);
if (cal->cancelled)
return CAL_EXIT_CANCELLED;
/* remove the window, no more input events */
widget_destroy(cal->widget);
cal->widget = NULL;
window_destroy(cal->window);
cal->window = NULL;
/* wait for all conversions to return */
dpy = display_get_display(cal->display);
do {
wait = false;
for (i = 0; i < NR_SAMPLES; i++)
if (cal->samples[i].pending)
wait = true;
if (wait) {
ret = wl_display_roundtrip(dpy);
if (ret < 0)
return CAL_EXIT_ERROR;
}
} while (wait);
for (i = 0; i < NR_SAMPLES; i++) {
s = &cal->samples[i];
if (!s->conv_done || !s->touch_done)
return CAL_EXIT_ERROR;
}
if (compute_calibration(cal, result) < 0)
return CAL_EXIT_ERROR;
if (verify_calibration(cal, result) < 0)
return CAL_EXIT_ERROR;
pr_ver("Calibration values:");
for (i = 0; i < 6; i++)
pr_ver(" %f", result[i]);
pr_ver("\n");
send_calibration(cal, result);
ret = wl_display_roundtrip(dpy);
if (ret < 0)
return CAL_EXIT_ERROR;
return CAL_EXIT_SUCCESS;
}
static void
pr_err(const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
fprintf(stderr, "%s error: ", program_invocation_short_name);
vfprintf(stderr, fmt, argp);
va_end(argp);
}
static void
help(void)
{
fprintf(stderr, "Compute a touchscreen calibration matrix for "
"a Wayland compositor by\n"
"having the user touch points on the screen.\n\n");
fprintf(stderr, "Usage: %s [options...] name\n\n",
program_invocation_short_name);
fprintf(stderr,
"Where 'name' can be a touch device sys path or a head name.\n"
"If 'name' is not given, all devices available for "
"calibration will be listed.\n"
"If 'name' is given, it must be exactly as listed.\n"
"Options:\n"
" --debug Print messages to help debugging.\n"
" -h, --help Display this help message\n"
" -v, --verbose Print list header and calibration result.\n");
}
int
main(int argc, char *argv[])
{
struct display *display;
struct calibrator *cal;
int c;
char *match_name = NULL;
int exit_code = CAL_EXIT_SUCCESS;
static const struct option opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "debug", no_argument, &debug_, 1 },
{ "verbose", no_argument, &verbose_, 1 },
{ 0, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "hv", opts, NULL)) != -1) {
switch (c) {
case 'h':
help();
return CAL_EXIT_SUCCESS;
case 'v':
verbose_ = 1;
break;
case 0:
break;
default:
return CAL_EXIT_ERROR;
}
}
if (optind < argc)
match_name = argv[optind++];
if (optind < argc) {
pr_err("extra arguments given.\n\n");
help();
return CAL_EXIT_ERROR;
}
display = display_create(&argc, argv);
if (!display)
return CAL_EXIT_ERROR;
cal = calibrator_create(display, match_name);
if (!cal)
return CAL_EXIT_ERROR;
display_set_user_data(display, cal);
display_set_global_handler(display, global_handler);
if (!match_name)
pr_ver("Available touch devices:\n");
/* Roundtrip to get list of available touch devices,
* first globals, then touch_device events */
wl_display_roundtrip(display_get_display(display));
wl_display_roundtrip(display_get_display(display));
if (!cal->calibration) {
exit_code = CAL_EXIT_ERROR;
pr_err("the Wayland server does not expose the calibration interface.\n");
} else if (cal->device_name) {
exit_code = calibrator_run(cal);
} else if (match_name) {
exit_code = CAL_EXIT_ERROR;
pr_err("\"%s\" was not found.\n", match_name);
} else if (cal->n_devices_listed == 0) {
fprintf(stderr, "No devices listed.\n");
}
calibrator_destroy(cal);
display_destroy(display);
return exit_code;
}