wacom: Bring over files from xinput-calibrator and build utility

Copies over files from a trimmed version of xinput-calibrator and
modifies the Makefile.am to build the program as a seperate utility.
This is just to verify the functionality of the code when built with
gnome-control-center.

https://bugzilla.gnome.org/show_bug.cgi?id=657423
This commit is contained in:
Jason Gerecke 2012-01-03 12:39:06 -08:00 committed by Bastien Nocera
parent a54c382377
commit e50dbea195
9 changed files with 1274 additions and 2 deletions

View file

@ -401,6 +401,7 @@ panels/user-accounts/data/gnome-user-accounts-panel.desktop.in
panels/user-accounts/data/faces/Makefile panels/user-accounts/data/faces/Makefile
panels/user-accounts/data/icons/Makefile panels/user-accounts/data/icons/Makefile
panels/wacom/Makefile panels/wacom/Makefile
panels/wacom/calibrator/Makefile
panels/wacom/gnome-wacom-panel.desktop.in panels/wacom/gnome-wacom-panel.desktop.in
po/Makefile.in po/Makefile.in
shell/Makefile shell/Makefile

View file

@ -1,9 +1,12 @@
# This is used in PANEL_CFLAGS # This is used in PANEL_CFLAGS
cappletname = wacom cappletname = wacom
SUBDIRS = calibrator
INCLUDES = \ INCLUDES = \
$(PANEL_CFLAGS) \ $(PANEL_CFLAGS) \
$(WACOM_PANEL_CFLAGS) \ $(WACOM_PANEL_CFLAGS) \
-I$(srcdir)/calibrator \
-DGNOMELOCALEDIR="\"$(datadir)/locale\"" \ -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
-DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \ -DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
-DGNOMECC_UI_DIR="\"$(uidir)\"" \ -DGNOMECC_UI_DIR="\"$(uidir)\"" \
@ -27,7 +30,7 @@ libwacom_properties_la_SOURCES = \
cc-wacom-nav-button.c \ cc-wacom-nav-button.c \
cc-wacom-nav-button.h cc-wacom-nav-button.h
libwacom_properties_la_LIBADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS) libwacom_properties_la_LIBADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS) $(builddir)/calibrator/libwacom-calibrator.la
libwacom_properties_la_LDFLAGS = $(PANEL_LDFLAGS) libwacom_properties_la_LDFLAGS = $(PANEL_LDFLAGS)
noinst_PROGRAMS = test-wacom noinst_PROGRAMS = test-wacom
@ -44,7 +47,7 @@ test_wacom_SOURCES = \
gsd-input-helper.h gsd-input-helper.h
test_wacom_CPPFLAGS = $(INCLUDES) test_wacom_CPPFLAGS = $(INCLUDES)
test_wacom_LDADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS) test_wacom_LDADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS) $(builddir)/calibrator/libwacom-calibrator.la
@INTLTOOL_DESKTOP_RULE@ @INTLTOOL_DESKTOP_RULE@

View file

@ -0,0 +1,38 @@
# This is used in PANEL_CFLAGS
cappletname = wacom
INCLUDES = \
$(PANEL_CFLAGS) \
$(WACOM_PANEL_CFLAGS) \
-DGNOMELOCALEDIR="\"$(datadir)/locale\"" \
-DGNOMECC_DATA_DIR="\"$(pkgdatadir)\"" \
-DGNOMECC_UI_DIR="\"$(uidir)\"" \
-DPIXMAP_DIR=\""$(datadir)/gnome-control-center/pixmaps"\" \
$(NULL)
noinst_LTLIBRARIES = libwacom-calibrator.la
libwacom_calibrator_la_SOURCES = \
calibrator.c \
calibrator.h \
gui_gtk.c \
gui_gtk.h
libwacom_calibrator_la_LIBADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS)
libwacom_calibrator_la_LDFLAGS = $(PANEL_LDFLAGS)
noinst_PROGRAMS = test-calibrator
test_calibrator_SOURCES = \
main.c \
main.h \
calibrator.c \
calibrator.h \
gui_gtk.c \
gui_gtk.h
test_calibrator_CPPFLAGS = $(INCLUDES)
test_calibrator_LDADD = $(PANEL_LIBS) $(WACOM_PANEL_LIBS)
-include $(top_srcdir)/git.mk

View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2009 Tias Guns
* Copyright (c) 2009 Soren Hauberg
*
* 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 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 <stdlib.h>
#include "calibrator.h"
#define SWAP(x,y) do { int t; t=(x); x=(y); y=t; } while (0)
/* reset clicks */
void
reset (struct Calib *c)
{
c->num_clicks = 0;
}
/* add a click with the given coordinates */
bool
add_click (struct Calib *c,
int x,
int y)
{
/* Double-click detection */
if (c->threshold_doubleclick > 0 && c->num_clicks > 0)
{
int i = c->num_clicks-1;
while (i >= 0)
{
if (abs(x - c->clicked_x[i]) <= c->threshold_doubleclick &&
abs(y - c->clicked_y[i]) <= c->threshold_doubleclick)
{
return false;
}
i--;
}
}
/* Mis-click detection */
if (c->threshold_misclick > 0 && c->num_clicks > 0)
{
bool misclick = true;
if (c->num_clicks == 1)
{
/* check that along one axis of first point */
if (along_axis(c, x,c->clicked_x[0],c->clicked_y[0]) ||
along_axis(c, y,c->clicked_x[0],c->clicked_y[0]))
{
misclick = false;
}
}
else if (c->num_clicks == 2)
{
/* check that along other axis of first point than second point */
if ((along_axis(c, y,c->clicked_x[0],c->clicked_y[0]) &&
along_axis(c, c->clicked_x[1],c->clicked_x[0],c->clicked_y[0])) ||
(along_axis(c, x,c->clicked_x[0],c->clicked_y[0]) &&
along_axis(c, c->clicked_y[1],c->clicked_x[0],c->clicked_y[0])))
{
misclick = false;
}
}
else if (c->num_clicks == 3)
{
/* check that along both axis of second and third point */
if ((along_axis(c, x,c->clicked_x[1],c->clicked_y[1]) &&
along_axis(c, y,c->clicked_x[2],c->clicked_y[2])) ||
(along_axis(c, y,c->clicked_x[1],c->clicked_y[1]) &&
along_axis(c, x,c->clicked_x[2],c->clicked_y[2])))
{
misclick = false;
}
}
if (misclick)
{
reset(c);
return false;
}
}
c->clicked_x[c->num_clicks] = x;
c->clicked_y[c->num_clicks] = y;
c->num_clicks++;
return true;
}
/* check whether the coordinates are along the respective axis */
bool
along_axis (struct Calib *c,
int xy,
int x0,
int y0)
{
return ((abs(xy - x0) <= c->threshold_misclick) ||
(abs(xy - y0) <= c->threshold_misclick));
}
/* calculate and apply the calibration */
bool
finish (struct Calib *c,
int width,
int height,
XYinfo *new_axys,
bool *swap)
{
bool swap_xy;
float scale_x;
float scale_y;
int delta_x;
int delta_y;
XYinfo axys = {-1, -1, -1, -1};
if (c->num_clicks != 4)
return false;
/* Should x and y be swapped? */
swap_xy = (abs (c->clicked_x [UL] - c->clicked_x [UR]) < abs (c->clicked_y [UL] - c->clicked_y [UR]));
if (swap_xy)
{
SWAP(c->clicked_x[LL], c->clicked_x[UR]);
SWAP(c->clicked_y[LL], c->clicked_y[UR]);
}
/* Compute min/max coordinates. */
/* These are scaled using the values of old_axys */
scale_x = (c->old_axys.x_max - c->old_axys.x_min)/(float)width;
axys.x_min = ((c->clicked_x[UL] + c->clicked_x[LL]) * scale_x/2) + c->old_axys.x_min;
axys.x_max = ((c->clicked_x[UR] + c->clicked_x[LR]) * scale_x/2) + c->old_axys.x_min;
scale_y = (c->old_axys.y_max - c->old_axys.y_min)/(float)height;
axys.y_min = ((c->clicked_y[UL] + c->clicked_y[UR]) * scale_y/2) + c->old_axys.y_min;
axys.y_max = ((c->clicked_y[LL] + c->clicked_y[LR]) * scale_y/2) + c->old_axys.y_min;
/* Add/subtract the offset that comes from not having the points in the
* corners (using the same coordinate system they are currently in)
*/
delta_x = (axys.x_max - axys.x_min) / (float)(NUM_BLOCKS - 2);
axys.x_min -= delta_x;
axys.x_max += delta_x;
delta_y = (axys.y_max - axys.y_min) / (float)(NUM_BLOCKS - 2);
axys.y_min -= delta_y;
axys.y_max += delta_y;
/* If x and y has to be swapped we also have to swap the parameters */
if (swap_xy)
{
SWAP(axys.x_min, axys.y_max);
SWAP(axys.y_min, axys.x_max);
}
*new_axys = axys;
*swap = swap_xy;
return true;
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2009 Tias Guns
* Copyright (c) 2009 Soren Hauberg
*
* 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 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.
*/
#ifndef _calibrator_h
#define _calibrator_h
/*
* Number of blocks. We partition the screen into 'num_blocks' x 'num_blocks'
* rectangles of equal size. We then ask the user to press points that are
* located at the corner closes to the center of the four blocks in the corners
* of the screen. The following ascii art illustrates the situation. We partition
* the screen into 8 blocks in each direction. We then let the user press the
* points marked with 'O'.
*
* +--+--+--+--+--+--+--+--+
* | | | | | | | | |
* +--O--+--+--+--+--+--O--+
* | | | | | | | | |
* +--+--+--+--+--+--+--+--+
* | | | | | | | | |
* +--+--+--+--+--+--+--+--+
* | | | | | | | | |
* +--+--+--+--+--+--+--+--+
* | | | | | | | | |
* +--+--+--+--+--+--+--+--+
* | | | | | | | | |
* +--+--+--+--+--+--+--+--+
* | | | | | | | | |
* +--O--+--+--+--+--+--O--+
* | | | | | | | | |
* +--+--+--+--+--+--+--+--+
*/
#define NUM_BLOCKS 8
/* Names of the points */
enum
{
UL = 0, /* Upper-left */
UR = 1, /* Upper-right */
LL = 2, /* Lower-left */
LR = 3 /* Lower-right */
};
/* struct to hold min/max info of the X and Y axis */
typedef struct
{
int x_min;
int x_max;
int y_min;
int y_max;
} XYinfo;
typedef enum
{
false = 0,
true = 1
} bool;
struct Calib
{
/* original axys values */
XYinfo old_axys;
/* nr of clicks registered */
int num_clicks;
/* click coordinates */
int clicked_x[4], clicked_y[4];
/* Threshold to keep the same point from being clicked twice.
* Set to zero if you don't want this check
*/
int threshold_doubleclick;
/* Threshold to detect mis-clicks (clicks not along axes)
* A lower value forces more precise calibration
* Set to zero if you don't want this check
*/
int threshold_misclick;
/* manually specified geometry string */
const char* geometry;
};
void reset (struct Calib *c);
bool add_click (struct Calib *c,
int x,
int y);
bool along_axis (struct Calib *c,
int xy,
int x0,
int y0);
bool finish (struct Calib *c,
int width,
int height,
XYinfo *new_axys,
bool *swap);
#endif /* _calibrator_h */

View file

@ -0,0 +1,411 @@
/*
* Copyright (c) 2009 Tias Guns
* Copyright (c) 2009 Soren Hauberg
*
* 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 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 <stdlib.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include <cairo.h>
#include "calibrator.h"
#include "gui_gtk.h"
#define MAXIMUM(x,y) ((x) > (y) ? (x) : (y))
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
#endif
/* Timeout parameters */
const int time_step = 100; /* in milliseconds */
const int max_time = 15000; /* 5000 = 5 sec */
/* Clock appereance */
const int cross_lines = 25;
const int cross_circle = 4;
const int clock_radius = 50;
const int clock_line_width = 10;
/* Text printed on screen */
const int font_size = 16;
#define HELP_LINES (sizeof help_text / sizeof help_text[0])
const char *help_text[] = {
"Touchscreen Calibration",
"Press the point, use a stylus to increase precision.",
"",
"(To abort, press any key or wait)"
};
struct CalibArea*
CalibrationArea_(struct Calib *c)
{
struct CalibArea *calib_area;
const char *geo = c->geometry;
calib_area = (struct CalibArea*)calloc(1, sizeof(struct CalibArea));
calib_area->calibrator = c;
calib_area->drawing_area = gtk_drawing_area_new();
/* Listen for mouse events */
gtk_widget_add_events(calib_area->drawing_area, GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK);
gtk_widget_set_can_focus(calib_area->drawing_area, TRUE);
/* Connect callbacks */
g_signal_connect(calib_area->drawing_area, "expose-event", G_CALLBACK(on_expose_event), calib_area);
g_signal_connect(calib_area->drawing_area, "draw", G_CALLBACK(draw), calib_area);
g_signal_connect(calib_area->drawing_area, "button-press-event", G_CALLBACK(on_button_press_event), calib_area);
g_signal_connect(calib_area->drawing_area, "key-press-event", G_CALLBACK(on_key_press_event), calib_area);
/* parse geometry string */
if (geo != NULL)
{
int gw,gh;
int res = sscanf(geo,"%dx%d",&gw,&gh);
if (res != 2)
geo = NULL;
else
set_display_size(calib_area, gw, gh );\
}
if (geo == NULL)
{
GtkAllocation allocation;
gtk_widget_get_allocation(calib_area->drawing_area, &allocation);
set_display_size(calib_area, allocation.width, allocation.height);
}
/* Setup timer for animation */
g_timeout_add(time_step, (GSourceFunc)on_timer_signal, calib_area);
return calib_area;
}
void
set_display_size(struct CalibArea *calib_area,
int width,
int height)
{
int delta_x;
int delta_y;
calib_area->display_width = width;
calib_area->display_height = height;
/* Compute absolute circle centers */
delta_x = calib_area->display_width/NUM_BLOCKS;
delta_y = calib_area->display_height/NUM_BLOCKS;
calib_area->X[UL] = delta_x;
calib_area->Y[UL] = delta_y;
calib_area->X[UR] = calib_area->display_width - delta_x - 1;
calib_area->Y[UR] = delta_y;
calib_area->X[LL] = delta_x;
calib_area->Y[LL] = calib_area->display_height - delta_y - 1;
calib_area->X[LR] = calib_area->display_width - delta_x - 1;
calib_area->Y[LR] = calib_area->display_height - delta_y - 1;
/* reset calibration if already started */
reset(calib_area->calibrator);
}
void
resize_display(struct CalibArea *calib_area)
{
/* check that screensize did not change (if no manually specified geometry) */
GtkAllocation allocation;
gtk_widget_get_allocation(calib_area->drawing_area, &allocation);
if (calib_area->calibrator->geometry == NULL &&
(calib_area->display_width != allocation.width ||
calib_area->display_height != allocation.height ))
{
set_display_size(calib_area, allocation.width, allocation.height);
}
}
bool
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
struct CalibArea *calib_area = (struct CalibArea*)data;
GdkWindow *window = gtk_widget_get_window(calib_area->drawing_area);
if (window)
{
cairo_t *cr = gdk_cairo_create(window);
cairo_save(cr);
cairo_rectangle(cr, event->area.x, event->area.y, event->area.width, event->area.height);
cairo_clip(cr);
draw(widget, cr, data);
cairo_restore(cr);
}
return true;
}
void
draw(GtkWidget *widget, cairo_t *cr, gpointer data)
{
struct CalibArea *calib_area = (struct CalibArea*)data;
int i;
double text_height;
double text_width;
double x;
double y;
cairo_text_extents_t extent;
resize_display(calib_area);
/* Print the text */
cairo_set_font_size(cr, font_size);
text_height = -1;
text_width = -1;
for (i = 0; i != HELP_LINES; i++)
{
cairo_text_extents(cr, help_text[i], &extent);
text_width = MAXIMUM(text_width, extent.width);
text_height = MAXIMUM(text_height, extent.height);
}
text_height += 2;
x = (calib_area->display_width - text_width) / 2;
y = (calib_area->display_height - text_height) / 2 - 60;
cairo_set_line_width(cr, 2);
cairo_rectangle(cr, x - 10, y - (HELP_LINES*text_height) - 10,
text_width + 20, (HELP_LINES*text_height) + 20);
/* Print help lines */
y -= 3;
for (i = HELP_LINES-1; i != -1; i--)
{
cairo_text_extents(cr, help_text[i], &extent);
cairo_move_to(cr, x + (text_width-extent.width)/2, y);
cairo_show_text(cr, help_text[i]);
y -= text_height;
}
cairo_stroke(cr);
/* Draw the points */
for (i = 0; i <= calib_area->calibrator->num_clicks; i++)
{
/* set color: already clicked or not */
if (i < calib_area->calibrator->num_clicks)
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
else
cairo_set_source_rgb(cr, 0.8, 0.0, 0.0);
cairo_set_line_width(cr, 1);
cairo_move_to(cr, calib_area->X[i] - cross_lines, calib_area->Y[i]);
cairo_rel_line_to(cr, cross_lines*2, 0);
cairo_move_to(cr, calib_area->X[i], calib_area->Y[i] - cross_lines);
cairo_rel_line_to(cr, 0, cross_lines*2);
cairo_stroke(cr);
cairo_arc(cr, calib_area->X[i], calib_area->Y[i], cross_circle, 0.0, 2.0 * M_PI);
cairo_stroke(cr);
}
/* Draw the clock background */
cairo_arc(cr, calib_area->display_width/2, calib_area->display_height/2, clock_radius/2, 0.0, 2.0 * M_PI);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_fill_preserve(cr);
cairo_stroke(cr);
cairo_set_line_width(cr, clock_line_width);
cairo_arc(cr, calib_area->display_width/2, calib_area->display_height/2, (clock_radius - clock_line_width)/2,
3/2.0*M_PI, (3/2.0*M_PI) + ((double)calib_area->time_elapsed/(double)max_time) * 2*M_PI);
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_stroke(cr);
/* Draw the message (if any) */
if (calib_area->message != NULL)
{
/* Frame the message */
cairo_set_font_size(cr, font_size);
cairo_text_extents(cr, calib_area->message, &extent);
text_width = extent.width;
text_height = extent.height;
x = (calib_area->display_width - text_width) / 2;
y = (calib_area->display_height - text_height + clock_radius) / 2 + 60;
cairo_set_line_width(cr, 2);
cairo_rectangle(cr, x - 10, y - text_height - 10,
text_width + 20, text_height + 25);
/* Print the message */
cairo_move_to(cr, x, y);
cairo_show_text(cr, calib_area->message);
cairo_stroke(cr);
}
}
void
redraw(struct CalibArea *calib_area)
{
GdkWindow *win = gtk_widget_get_window(calib_area->drawing_area);
if (win)
{
GdkRectangle rect;
rect.x = 0;
rect.y = 0;
rect.width = calib_area->display_width;
rect.height = calib_area->display_height;
gdk_window_invalidate_rect(win, &rect, false);
}
}
bool
on_timer_signal(struct CalibArea *calib_area)
{
GdkWindow *win;
GtkWidget *parent = gtk_widget_get_parent(calib_area->drawing_area);
calib_area->time_elapsed += time_step;
if (calib_area->time_elapsed > max_time || parent == NULL)
{
if (parent)
gtk_widget_destroy(parent);
return false;
}
/* Update clock */
win = gtk_widget_get_window(calib_area->drawing_area);
if (win)
{
GdkRectangle rect;
rect.x = calib_area->display_width/2 - clock_radius - clock_line_width;
rect.y = calib_area->display_height/2 - clock_radius - clock_line_width;
rect.width = 2 * clock_radius + 1 + 2 * clock_line_width;
rect.height = 2 * clock_radius + 1 + 2 * clock_line_width;
gdk_window_invalidate_rect(win, &rect, false);
}
return true;
}
bool
on_button_press_event(GtkWidget *widget,
GdkEventButton *event,
gpointer data)
{
struct CalibArea *calib_area = (struct CalibArea*)data;
bool success;
/* Handle click */
calib_area->time_elapsed = 0;
success = add_click(calib_area->calibrator, (int)event->x_root, (int)event->y_root);
if (!success && calib_area->calibrator->num_clicks == 0)
draw_message(calib_area, "Mis-click detected, restarting...");
else
draw_message(calib_area, NULL);
/* Are we done yet? */
if (calib_area->calibrator->num_clicks >= 4)
{
GtkWidget *parent = gtk_widget_get_parent(calib_area->drawing_area);
if (parent)
gtk_widget_destroy(parent);
return true;
}
/* Force a redraw */
redraw(calib_area);
return true;
}
void
draw_message(struct CalibArea *calib_area,
const char *msg)
{
calib_area->message = msg;
}
bool
on_key_press_event(GtkWidget *widget,
GdkEventKey *event,
gpointer data)
{
struct CalibArea *calib_area = (struct CalibArea*)data;
GtkWidget *parent = gtk_widget_get_parent(calib_area->drawing_area);
if (parent)
gtk_widget_destroy(parent);
return true;
}
/**
* Creates the windows and other objects required to do calibration
* under GTK and then starts the main loop. When the main loop exits,
* the calibration will be calculated (if possible) and this function
* will then return ('true' if successful, 'false' otherwise).
*/
bool
run_gui(struct Calib *c,
XYinfo *new_axys,
bool *swap)
{
bool success;
struct CalibArea *calib_area = CalibrationArea_(c);
printf("Current calibration: %d, %d, %d, %d\n",
c->old_axys.x_min,
c->old_axys.y_min,
c->old_axys.x_max,
c->old_axys.y_max);
GdkScreen *screen = gdk_screen_get_default();
GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GdkRectangle rect;
/*int num_monitors = screen->get_n_monitors(); TODO, multiple monitors?*/
g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gdk_screen_get_monitor_geometry(screen, 0, &rect);
/* when no window manager: explicitely take size of full screen */
gtk_window_move(GTK_WINDOW(win), rect.x, rect.y);
gtk_window_set_default_size(GTK_WINDOW(win), rect.width, rect.height);
/* in case of window manager: set as full screen to hide window decorations */
gtk_window_fullscreen(GTK_WINDOW(win));
gtk_container_add(GTK_CONTAINER(win), calib_area->drawing_area);
gtk_widget_show_all(win);
printf("gtk_main entered!\n");
gtk_main();
printf("gtk_main returned!\n");
success = finish(calib_area->calibrator, calib_area->display_width, calib_area->display_height, new_axys, swap);
printf("Final calibration: %d, %d, %d, %d\n",
new_axys->x_min,
new_axys->y_min,
new_axys->x_max,
new_axys->y_max);
return success;
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2009 Tias Guns
* Copyright (c) 2009 Soren Hauberg
*
* 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 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.
*/
#ifndef _gui_gtk_h
#define _gui_gtk_h
#include <gtk/gtk.h>
#include "calibrator.h"
struct CalibArea
{
struct Calib* calibrator;
double X[4], Y[4];
int display_width, display_height;
int time_elapsed;
const char* message;
GtkWidget *drawing_area;
};
struct CalibArea* CalibrationArea_ (struct Calib *c);
void set_display_size (struct CalibArea *calib_area,
int width,
int height);
void resize_display (struct CalibArea *calib_area);
bool on_expose_event (GtkWidget *widget,
GdkEventExpose *event,
gpointer data);
void draw (GtkWidget *widget,
cairo_t *cr,
gpointer data);
void redraw (struct CalibArea *calib_area);
bool on_timer_signal (struct CalibArea *calib_area);
bool on_button_press_event (GtkWidget *widget,
GdkEventButton *event,
gpointer data);
void draw_message (struct CalibArea *calib_area,
const char *msg);
bool on_key_press_event (GtkWidget *widget,
GdkEventKey *event,
gpointer data);
bool run_gui (struct Calib *c,
XYinfo *new_axys,
bool *swap);
#endif /* _gui_gtk_h */

View file

@ -0,0 +1,398 @@
/*
* Copyright (c) 2009 Tias Guns
* Copyright (c) 2009 Soren Hauberg
*
* 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 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 <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <dirent.h>
#include <X11/extensions/XInput.h>
#include "gui_gtk.h"
#include "main.h"
/**
* find a calibratable touchscreen device (using XInput)
*
* if pre_device is NULL, the last calibratable device is selected.
* retuns number of devices found,
* the data of the device is returned in the last 3 function parameters
*/
int find_device(const char* pre_device, bool verbose, bool list_devices,
XID* device_id, const char** device_name, XYinfo* device_axys)
{
bool pre_device_is_id = true;
int found = 0;
Display* display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Unable to connect to X server\n");
exit(1);
}
int xi_opcode, event, error;
if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) {
fprintf(stderr, "X Input extension not available.\n");
exit(1);
}
/* verbose, get Xi version */
if (verbose) {
XExtensionVersion *version = XGetExtensionVersion(display, INAME);
if (version && (version != (XExtensionVersion*) NoSuchExtension)) {
printf("DEBUG: %s version is %i.%i\n",
INAME, version->major_version, version->minor_version);
XFree(version);
}
}
if (pre_device != NULL) {
/* check whether the pre_device is an ID (only digits) */
int len = strlen(pre_device);
int loop;
for (loop=0; loop<len; loop++) {
if (!isdigit(pre_device[loop])) {
pre_device_is_id = false;
break;
}
}
}
if (verbose)
printf("DEBUG: Skipping virtual master devices and devices without axis valuators.\n");
int ndevices;
XDeviceInfoPtr list, slist;
slist=list=(XDeviceInfoPtr) XListInputDevices (display, &ndevices);
int i;
for (i=0; i<ndevices; i++, list++)
{
if (list->use == IsXKeyboard || list->use == IsXPointer) /* virtual master device */
continue;
/* if we are looking for a specific device */
if (pre_device != NULL) {
if ((pre_device_is_id && list->id == (XID) atoi(pre_device)) ||
(!pre_device_is_id && strcmp(list->name, pre_device) == 0)) {
/* OK, fall through */
} else {
/* skip, not this device */
continue;
}
}
XAnyClassPtr any = (XAnyClassPtr) (list->inputclassinfo);
int j;
for (j=0; j<list->num_classes; j++)
{
if (any->class == ValuatorClass)
{
XValuatorInfoPtr V = (XValuatorInfoPtr) any;
XAxisInfoPtr ax = (XAxisInfoPtr) V->axes;
if (V->mode != Absolute) {
if (verbose)
printf("DEBUG: Skipping device '%s' id=%i, does not report Absolute events.\n",
list->name, (int)list->id);
} else if (V->num_axes < 2 ||
(ax[0].min_value == -1 && ax[0].max_value == -1) ||
(ax[1].min_value == -1 && ax[1].max_value == -1)) {
if (verbose)
printf("DEBUG: Skipping device '%s' id=%i, does not have two calibratable axes.\n",
list->name, (int)list->id);
} else {
/* a calibratable device (has 2 axis valuators) */
found++;
*device_id = list->id;
*device_name = my_strdup(list->name);
device_axys->x_min = ax[0].min_value;
device_axys->x_max = ax[0].max_value;
device_axys->y_min = ax[1].min_value;
device_axys->y_max = ax[1].max_value;
if (list_devices)
printf("Device \"%s\" id=%i\n", *device_name, (int)*device_id);
}
}
/*
* Increment 'any' to point to the next item in the linked
* list. The length is in bytes, so 'any' must be cast to
* a character pointer before being incremented.
*/
any = (XAnyClassPtr) ((char *) any + any->length);
}
}
XFreeDeviceList(slist);
XCloseDisplay(display);
return found;
}
static void usage(char* cmd, unsigned thr_misclick)
{
fprintf(stderr, "Usage: %s [-h|--help] [-v|--verbose] [--list] [--device <device name or id>] [--precalib <minx> <maxx> <miny> <maxy>] [--misclick <nr of pixels>] [--output-type <auto|xorg.conf.d|hal|xinput>] [--fake] [--geometry <w>x<h>]\n", cmd);
fprintf(stderr, "\t-h, --help: print this help message\n");
fprintf(stderr, "\t-v, --verbose: print debug messages during the process\n");
fprintf(stderr, "\t--list: list calibratable input devices and quit\n");
fprintf(stderr, "\t--device <device name or id>: select a specific device to calibrate\n");
fprintf(stderr, "\t--precalib: manually provide the current calibration setting (eg. the values in xorg.conf)\n");
fprintf(stderr, "\t--misclick: set the misclick threshold (0=off, default: %i pixels)\n",
thr_misclick);
fprintf(stderr, "\t--fake: emulate a fake device (for testing purposes)\n");
fprintf(stderr, "\t--geometry: manually provide the geometry (width and height) for the calibration window\n");
}
struct Calib* main_common(int argc, char** argv)
{
bool verbose = false;
bool list_devices = false;
bool fake = false;
bool precalib = false;
XYinfo pre_axys = {-1, -1, -1, -1};
const char* pre_device = NULL;
const char* geometry = NULL;
unsigned thr_misclick = 15;
unsigned thr_doubleclick = 7;
/* parse input */
if (argc > 1) {
int i;
for (i=1; i!=argc; i++) {
/* Display help ? */
if (strcmp("-h", argv[i]) == 0 ||
strcmp("--help", argv[i]) == 0) {
fprintf(stderr, "xinput_calibratior, v%s\n\n", "0.0.0");
usage(argv[0], thr_misclick);
exit(0);
} else
/* Verbose output ? */
if (strcmp("-v", argv[i]) == 0 ||
strcmp("--verbose", argv[i]) == 0) {
verbose = true;
} else
/* Just list devices ? */
if (strcmp("--list", argv[i]) == 0) {
list_devices = true;
} else
/* Select specific device ? */
if (strcmp("--device", argv[i]) == 0) {
if (argc > i+1)
pre_device = argv[++i];
else {
fprintf(stderr, "Error: --device needs a device name or id as argument; use --list to list the calibratable input devices.\n\n");
usage(argv[0], thr_misclick);
exit(1);
}
} else
/* Get pre-calibration ? */
if (strcmp("--precalib", argv[i]) == 0) {
precalib = true;
if (argc > i+1)
pre_axys.x_min = atoi(argv[++i]);
if (argc > i+1)
pre_axys.x_max = atoi(argv[++i]);
if (argc > i+1)
pre_axys.y_min = atoi(argv[++i]);
if (argc > i+1)
pre_axys.y_max = atoi(argv[++i]);
} else
/* Get mis-click threshold ? */
if (strcmp("--misclick", argv[i]) == 0) {
if (argc > i+1)
thr_misclick = atoi(argv[++i]);
else {
fprintf(stderr, "Error: --misclick needs a number (the pixel threshold) as argument. Set to 0 to disable mis-click detection.\n\n");
usage(argv[0], thr_misclick);
exit(1);
}
} else
/* specify window geometry? */
if (strcmp("--geometry", argv[i]) == 0) {
geometry = argv[++i];
/* sscanf(argv[++i],"%dx%d",&win_width,&win_height); */
} else
/* Fake calibratable device ? */
if (strcmp("--fake", argv[i]) == 0) {
fake = true;
}
/* unknown option */
else {
fprintf(stderr, "Unknown option: %s\n\n", argv[i]);
usage(argv[0], thr_misclick);
exit(0);
}
}
}
/* Choose the device to calibrate */
XID device_id = (XID) -1;
const char* device_name = NULL;
XYinfo device_axys = {-1, -1, -1, -1};
if (fake) {
/* Fake a calibratable device */
device_name = "Fake_device";
device_axys.x_min=0;
device_axys.x_max=1000;
device_axys.y_min=0;
device_axys.y_max=1000;
if (verbose) {
printf("DEBUG: Faking device: %s\n", device_name);
}
} else {
/* Find the right device */
int nr_found = find_device(pre_device, verbose, list_devices, &device_id, &device_name, &device_axys);
if (list_devices) {
/* printed the list in find_device */
if (nr_found == 0)
printf("No calibratable devices found.\n");
exit(0);
}
if (nr_found == 0) {
if (pre_device == NULL)
fprintf (stderr, "Error: No calibratable devices found.\n");
else
fprintf (stderr, "Error: Device \"%s\" not found; use --list to list the calibratable input devices.\n", pre_device);
exit(1);
} else if (nr_found > 1) {
printf ("Warning: multiple calibratable devices found, calibrating last one (%s)\n\tuse --device to select another one.\n", device_name);
}
if (verbose) {
printf("DEBUG: Selected device: %s\n", device_name);
}
}
/* override min/max XY from command line ? */
if (precalib) {
if (pre_axys.x_min != -1)
device_axys.x_min = pre_axys.x_min;
if (pre_axys.x_max != -1)
device_axys.x_max = pre_axys.x_max;
if (pre_axys.y_min != -1)
device_axys.y_min = pre_axys.y_min;
if (pre_axys.y_max != -1)
device_axys.y_max = pre_axys.y_max;
if (verbose) {
printf("DEBUG: Setting precalibration: %i, %i, %i, %i\n",
device_axys.x_min, device_axys.x_max,
device_axys.y_min, device_axys.y_max);
}
}
/* lastly, presume a standard Xorg driver (evtouch, mutouch, ...) */
return CalibratorXorgPrint(device_name, &device_axys,
verbose, thr_misclick, thr_doubleclick, geometry);
}
struct Calib* CalibratorXorgPrint(const char* const device_name0, const XYinfo *axys0, const bool verbose0, const int thr_misclick, const int thr_doubleclick, const char* geometry)
{
struct Calib* c = (struct Calib*)calloc(1, sizeof(struct Calib));
c->old_axys = *axys0;
c->threshold_misclick = thr_misclick;
c->threshold_doubleclick = thr_doubleclick;
c->geometry = geometry;
printf("Calibrating standard Xorg driver \"%s\"\n", device_name0);
printf("\tcurrent calibration values: min_x=%d, max_x=%d and min_y=%d, max_y=%d\n",
c->old_axys.x_min, c->old_axys.x_max, c->old_axys.y_min, c->old_axys.y_max);
printf("\tIf these values are estimated wrong, either supply it manually with the --precalib option, or run the 'get_precalib.sh' script to automatically get it (through HAL).\n");
return c;
}
bool finish_data(struct Calib* c, const XYinfo new_axys, int swap_xy)
{
bool success = true;
/* we suppose the previous 'swap_xy' value was 0 */
/* (unfortunately there is no way to verify this (yet)) */
int new_swap_xy = swap_xy;
printf("\n\n--> Making the calibration permanent <--\n");
success &= output_xorgconfd(c, new_axys, swap_xy, new_swap_xy);
return success;
}
bool output_xorgconfd(struct Calib* c, const XYinfo new_axys, int swap_xy, int new_swap_xy)
{
const char* sysfs_name = "!!Name_Of_TouchScreen!!";
/* xorg.conf.d snippet */
printf(" copy the snippet below into '/etc/X11/xorg.conf.d/99-calibration.conf'\n");
printf("Section \"InputClass\"\n");
printf(" Identifier \"calibration\"\n");
printf(" MatchProduct \"%s\"\n", sysfs_name);
printf(" Option \"MinX\" \"%d\"\n", new_axys.x_min);
printf(" Option \"MaxX\" \"%d\"\n", new_axys.x_max);
printf(" Option \"MinY\" \"%d\"\n", new_axys.y_min);
printf(" Option \"MaxY\" \"%d\"\n", new_axys.y_max);
if (swap_xy != 0)
printf(" Option \"SwapXY\" \"%d\" # unless it was already set to 1\n", new_swap_xy);
printf("EndSection\n");
return true;
}
int main(int argc, char** argv)
{
int success = 0;
XYinfo axys;
bool swap_xy;
struct Calib* calibrator = main_common(argc, argv);
/* GTK setup */
gtk_init(&argc, &argv);
success = run_gui(calibrator, &axys, &swap_xy);
if (success)
success = finish_data(calibrator, axys, swap_xy);
if (!success) {
/* TODO, in GUI ? */
fprintf(stderr, "Error: unable to apply or save configuration values\n");
}
free(calibrator);
return success;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2009 Tias Guns
* Copyright (c) 2009 Soren Hauberg
*
* 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 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.
*/
#ifndef _main_h
#define _main_h
#include "calibrator.h"
/* strdup: non-ansi */
char* my_strdup(const char* s);
char* my_strdup(const char* s) {
size_t len = strlen(s) + 1;
void* p = malloc(len);
if (p == NULL)
return NULL;
return (char*) memcpy(p, s, len);
}
int find_device(const char*, bool, bool, XID*, const char**, XYinfo*);
static void usage(char* cmd, unsigned thr_misclick);
struct Calib* main_common(int argc, char** argv);
struct Calib* CalibratorXorgPrint(const char* const device_name, const XYinfo *axys,
const bool verbose, const int thr_misclick, const int thr_doubleclick,
const char* geometry);
bool finish_data(struct Calib*, const XYinfo new_axys, int swap_xy);
bool output_xorgconfd(struct Calib*, const XYinfo new_axys, int swap_xy, int new_swap_xy);
int main(int argc, char** argv);
#endif