diff --git a/.gitignore b/.gitignore index 661f4882..69c5d61b 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,7 @@ weston-simple-damage weston-smoke weston-stacking weston-subsurfaces +weston-touch-calibrator weston-transformed weston-view diff --git a/Makefile.am b/Makefile.am index 5260bd8a..664eb2eb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -546,6 +546,7 @@ demo_clients = \ weston-fullscreen \ weston-stacking \ weston-calibrator \ + weston-touch-calibrator \ weston-scaler if INSTALL_DEMO_CLIENTS @@ -785,6 +786,17 @@ weston_calibrator_SOURCES = \ weston_calibrator_LDADD = libtoytoolkit.la weston_calibrator_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) +weston_touch_calibrator_SOURCES = \ + clients/touch-calibrator.c \ + shared/helpers.h \ + shared/matrix.c \ + shared/matrix.h +nodist_weston_touch_calibrator_SOURCES = \ + protocol/weston-touch-calibration-protocol.c \ + protocol/weston-touch-calibration-client-protocol.h +weston_touch_calibrator_LDADD = libtoytoolkit.la +weston_touch_calibrator_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) + if BUILD_SUBSURFACES_CLIENT demo_clients += weston-subsurfaces weston_subsurfaces_SOURCES = \ @@ -872,6 +884,8 @@ endif BUILT_SOURCES += \ protocol/weston-screenshooter-protocol.c \ protocol/weston-screenshooter-client-protocol.h \ + protocol/weston-touch-calibration-protocol.c \ + protocol/weston-touch-calibration-client-protocol.h \ protocol/text-cursor-position-client-protocol.h \ protocol/text-cursor-position-protocol.c \ protocol/text-input-unstable-v1-protocol.c \ diff --git a/clients/touch-calibrator.c b/clients/touch-calibrator.c new file mode 100644 index 00000000..3c7e6ece --- /dev/null +++ b/clients/touch-calibrator.c @@ -0,0 +1,970 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clients/window.h" +#include "shared/helpers.h" +#include "shared/matrix.h" + +#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". + * + * + * - : + * + * 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"); + 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; +} diff --git a/clients/window.c b/clients/window.c index dee4455f..b64e96cf 100644 --- a/clients/window.c +++ b/clients/window.c @@ -4220,7 +4220,7 @@ window_get_shadow_margin(struct window *window) return 0; } -static void +void window_inhibit_redraw(struct window *window) { window->redraw_inhibited = 1; @@ -4229,7 +4229,7 @@ window_inhibit_redraw(struct window *window) window->redraw_task_scheduled = 0; } -static void +void window_uninhibit_redraw(struct window *window) { window->redraw_inhibited = 0; diff --git a/clients/window.h b/clients/window.h index 3366ab15..fde5c2f0 100644 --- a/clients/window.h +++ b/clients/window.h @@ -604,6 +604,10 @@ widget_set_axis_handlers(struct widget *widget, widget_axis_stop_handler_t axis_stop_handler, widget_axis_discrete_handler_t axis_discrete_handler); +void +window_inhibit_redraw(struct window *window); +void +window_uninhibit_redraw(struct window *window); void widget_schedule_redraw(struct widget *widget); void