~elementary-os/elementaryos/os-patch-gnome-control-center-precise

« back to all changes in this revision

Viewing changes to panels/wacom/calibrator/gui_gtk.c

  • Committer: Sergey "Shnatsel" Davidoff
  • Date: 2012-05-18 13:02:50 UTC
  • Revision ID: shnatsel@gmail.com-20120518130250-2u99ldq61a42rbt7
Initial import, version 1:3.4.1-0ubuntu2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2009 Tias Guns
 
3
 * Copyright (c) 2009 Soren Hauberg
 
4
 *
 
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 
6
 * of this software and associated documentation files (the "Software"), to deal
 
7
 * in the Software without restriction, including without limitation the rights
 
8
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
9
 * copies of the Software, and to permit persons to whom the Software is
 
10
 * furnished to do so, subject to the following conditions:
 
11
 *
 
12
 * The above copyright notice and this permission notice shall be included in
 
13
 * all copies or substantial portions of the Software.
 
14
 *
 
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
18
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
20
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
21
 * THE SOFTWARE.
 
22
 */
 
23
 
 
24
#include "config.h"
 
25
 
 
26
#include <math.h>
 
27
#include <stdlib.h>
 
28
#include <stdio.h>
 
29
#include <glib/gi18n.h>
 
30
#include <gtk/gtk.h>
 
31
#include <cairo.h>
 
32
 
 
33
#include "calibrator.h"
 
34
#include "gui_gtk.h"
 
35
 
 
36
struct CalibArea
 
37
{
 
38
    struct Calib calibrator;
 
39
    double X[4], Y[4];
 
40
    int display_width, display_height;
 
41
    int time_elapsed;
 
42
 
 
43
    const char* message;
 
44
 
 
45
    guint anim_id;
 
46
 
 
47
    GtkWidget *window;
 
48
 
 
49
    FinishCallback callback;
 
50
    gpointer       user_data;
 
51
};
 
52
 
 
53
 
 
54
/* Window parameters */
 
55
#define WINDOW_OPACITY          0.9
 
56
 
 
57
/* Timeout parameters */
 
58
#define TIME_STEP               100   /* in milliseconds */
 
59
#define MAX_TIME                15000 /* 5000 = 5 sec */
 
60
 
 
61
/* Clock appereance */
 
62
#define CROSS_LINES             47
 
63
#define CROSS_CIRCLE            7
 
64
#define CROSS_CIRCLE2           27
 
65
#define CLOCK_RADIUS            50
 
66
#define CLOCK_LINE_WIDTH        10
 
67
#define CLOCK_LINE_PADDING      10
 
68
 
 
69
/* Text printed on screen */
 
70
#define HELP_TEXT_TITLE N_("Screen Calibration")
 
71
#define HELP_TEXT_MAIN  N_("Please tap the target markers as they appear on screen to calibrate the tablet.")
 
72
 
 
73
static void
 
74
set_display_size(CalibArea *calib_area,
 
75
                 int               width,
 
76
                 int               height)
 
77
{
 
78
    int delta_x;
 
79
    int delta_y;
 
80
 
 
81
    calib_area->display_width = width;
 
82
    calib_area->display_height = height;
 
83
 
 
84
    /* Compute absolute circle centers */
 
85
    delta_x = calib_area->display_width/NUM_BLOCKS;
 
86
    delta_y = calib_area->display_height/NUM_BLOCKS;
 
87
 
 
88
    calib_area->X[UL] = delta_x;
 
89
    calib_area->Y[UL] = delta_y;
 
90
 
 
91
    calib_area->X[UR] = calib_area->display_width - delta_x - 1;
 
92
    calib_area->Y[UR] = delta_y;
 
93
 
 
94
    calib_area->X[LL] = delta_x;
 
95
    calib_area->Y[LL] = calib_area->display_height - delta_y - 1;
 
96
 
 
97
    calib_area->X[LR] = calib_area->display_width - delta_x - 1;
 
98
    calib_area->Y[LR] = calib_area->display_height - delta_y - 1;
 
99
 
 
100
    /* reset calibration if already started */
 
101
    reset(&calib_area->calibrator);
 
102
}
 
103
 
 
104
static void
 
105
resize_display(CalibArea *calib_area)
 
106
{
 
107
    /* check that screensize did not change (if no manually specified geometry) */
 
108
    GtkAllocation allocation;
 
109
    gtk_widget_get_allocation(calib_area->window, &allocation);
 
110
    if (calib_area->display_width != allocation.width ||
 
111
        calib_area->display_height != allocation.height)
 
112
    {
 
113
        set_display_size(calib_area, allocation.width, allocation.height);
 
114
    }
 
115
}
 
116
 
 
117
static void
 
118
draw(GtkWidget *widget, cairo_t *cr, gpointer data)
 
119
{
 
120
    CalibArea *calib_area = (CalibArea*)data;
 
121
    int i;
 
122
    double x;
 
123
    double y;
 
124
    PangoLayout *layout;
 
125
    PangoRectangle logical_rect;
 
126
    GtkStyleContext *context;
 
127
    char *markup;
 
128
 
 
129
    resize_display(calib_area);
 
130
 
 
131
    context = gtk_widget_get_style_context (widget);
 
132
 
 
133
    /* Black background and reset the operator */
 
134
    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
 
135
    cairo_paint (cr);
 
136
    cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
 
137
 
 
138
    /* Print the help lines */
 
139
    layout = pango_layout_new (gtk_widget_get_pango_context (widget));
 
140
    pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
 
141
    markup = g_strdup_printf ("<span foreground=\"white\"><big><b>%s</b></big>\n<big>%s</big></span>",
 
142
                              _(HELP_TEXT_TITLE),
 
143
                              _(HELP_TEXT_MAIN));
 
144
    pango_layout_set_markup (layout, markup, -1);
 
145
    g_free (markup);
 
146
 
 
147
    pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
148
 
 
149
    x = (calib_area->display_width - logical_rect.width) / 2 + logical_rect.x;
 
150
    y = (calib_area->display_height - logical_rect.height) / 2  - logical_rect.height - 40 + logical_rect.y;
 
151
 
 
152
    gtk_render_layout (context, cr,
 
153
                       x + logical_rect.x,
 
154
                       y + logical_rect.y,
 
155
                       layout);
 
156
    g_object_unref (layout);
 
157
 
 
158
    /* Draw the points */
 
159
    cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
 
160
 
 
161
    i = calib_area->calibrator.num_clicks;
 
162
 
 
163
    cairo_set_line_width(cr, 1);
 
164
    cairo_move_to(cr, calib_area->X[i] - CROSS_LINES, calib_area->Y[i] - 0.5);
 
165
    cairo_rel_line_to(cr, CROSS_LINES*2, 0);
 
166
    cairo_move_to(cr, calib_area->X[i] - 0.5, calib_area->Y[i] - CROSS_LINES);
 
167
    cairo_rel_line_to(cr, 0, CROSS_LINES*2);
 
168
    cairo_stroke(cr);
 
169
 
 
170
    cairo_set_line_width(cr, 2);
 
171
    cairo_arc(cr, calib_area->X[i] - 0.5, calib_area->Y[i] - 0.5, CROSS_CIRCLE, 0.0, 2.0 * M_PI);
 
172
    cairo_stroke(cr);
 
173
 
 
174
    cairo_set_line_width(cr, 5);
 
175
    cairo_arc(cr, calib_area->X[i] - 0.5, calib_area->Y[i] - 0.5, CROSS_CIRCLE2, 0.0, 2.0 * M_PI);
 
176
    cairo_stroke(cr);
 
177
 
 
178
    /* Draw the clock background */
 
179
    cairo_arc(cr, calib_area->display_width/2, calib_area->display_height/2, CLOCK_RADIUS/2, 0.0, 2.0 * M_PI);
 
180
    cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
 
181
    cairo_fill_preserve(cr);
 
182
    cairo_stroke(cr);
 
183
 
 
184
    cairo_set_line_width(cr, CLOCK_LINE_WIDTH);
 
185
    cairo_arc(cr, calib_area->display_width/2, calib_area->display_height/2, (CLOCK_RADIUS - CLOCK_LINE_WIDTH - CLOCK_LINE_PADDING)/2,
 
186
         3/2.0*M_PI, (3/2.0*M_PI) + ((double)calib_area->time_elapsed/(double)MAX_TIME) * 2*M_PI);
 
187
    cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
 
188
    cairo_stroke(cr);
 
189
 
 
190
    /* Draw the message (if any) */
 
191
    if (calib_area->message != NULL)
 
192
    {
 
193
        cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
 
194
 
 
195
        /* Frame the message */
 
196
        layout = pango_layout_new (gtk_widget_get_pango_context (widget));
 
197
        pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
 
198
        markup = g_strdup_printf ("<span foreground=\"white\"><big>%s</big></span>",
 
199
                                  _(calib_area->message));
 
200
        pango_layout_set_markup (layout, markup, -1);
 
201
        g_free (markup);
 
202
        pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
203
 
 
204
        x = (calib_area->display_width - logical_rect.width) / 2 + logical_rect.x;
 
205
        y = (calib_area->display_height - logical_rect.height + CLOCK_RADIUS) / 2 + 60 + logical_rect.y;
 
206
        cairo_set_line_width(cr, 2);
 
207
        cairo_rectangle(cr, x - 10 - 0.5 , y - 10 - 0.5,
 
208
                logical_rect.width + 20 + 1, logical_rect.height + 20 + 1);
 
209
        cairo_stroke (cr);
 
210
 
 
211
        /* Print the message */
 
212
        gtk_render_layout (context, cr,
 
213
                           x + logical_rect.x,
 
214
                           y + logical_rect.y,
 
215
                           layout);
 
216
        g_object_unref (layout);
 
217
    }
 
218
}
 
219
 
 
220
static void
 
221
draw_message(CalibArea *calib_area,
 
222
             const char       *msg)
 
223
{
 
224
    calib_area->message = msg;
 
225
}
 
226
 
 
227
static void
 
228
redraw(CalibArea *calib_area)
 
229
{
 
230
    GdkWindow *win = gtk_widget_get_window(calib_area->window);
 
231
    if (win)
 
232
    {
 
233
        GdkRectangle rect;
 
234
        rect.x = 0;
 
235
        rect.y = 0;
 
236
        rect.width = calib_area->display_width;
 
237
        rect.height = calib_area->display_height;
 
238
        gdk_window_invalidate_rect(win, &rect, FALSE);
 
239
    }
 
240
}
 
241
 
 
242
static gboolean
 
243
on_delete_event (GtkWidget *widget,
 
244
                 GdkEvent  *event,
 
245
                 CalibArea *area)
 
246
{
 
247
        if (area->anim_id > 0) {
 
248
                g_source_remove (area->anim_id);
 
249
                area->anim_id = 0;
 
250
        }
 
251
 
 
252
        gtk_widget_hide (area->window);
 
253
 
 
254
        (*area->callback) (area, area->user_data);
 
255
 
 
256
        return TRUE;
 
257
}
 
258
 
 
259
static gboolean
 
260
on_button_press_event(GtkWidget      *widget,
 
261
                      GdkEventButton *event,
 
262
                      CalibArea      *area)
 
263
{
 
264
    gboolean success;
 
265
 
 
266
    /* Handle click */
 
267
    area->time_elapsed = 0;
 
268
    success = add_click(&area->calibrator, (int)event->x_root, (int)event->y_root);
 
269
 
 
270
    if (!success && area->calibrator.num_clicks == 0)
 
271
        draw_message(area, N_("Mis-click detected, restarting..."));
 
272
    else
 
273
        draw_message(area, NULL);
 
274
 
 
275
    /* Are we done yet? */
 
276
    if (area->calibrator.num_clicks >= 4)
 
277
    {
 
278
        on_delete_event (widget, NULL, area);
 
279
        return FALSE;
 
280
    }
 
281
 
 
282
    /* Force a redraw */
 
283
    redraw(area);
 
284
 
 
285
    return FALSE;
 
286
}
 
287
 
 
288
static gboolean
 
289
on_key_release_event(GtkWidget   *widget,
 
290
                     GdkEventKey *event,
 
291
                     CalibArea   *area)
 
292
{
 
293
    if (event->type != GDK_KEY_RELEASE)
 
294
        return FALSE;
 
295
    if (event->keyval != GDK_KEY_Escape)
 
296
        return FALSE;
 
297
 
 
298
    on_delete_event (widget, NULL, area);
 
299
 
 
300
    return FALSE;
 
301
}
 
302
 
 
303
static gboolean
 
304
on_timer_signal(CalibArea *area)
 
305
{
 
306
    GdkWindow *win;
 
307
 
 
308
    area->time_elapsed += TIME_STEP;
 
309
    if (area->time_elapsed > MAX_TIME)
 
310
    {
 
311
        on_delete_event (NULL, NULL, area);
 
312
        return FALSE;
 
313
    }
 
314
 
 
315
    /* Update clock */
 
316
    win = gtk_widget_get_window (area->window);
 
317
    if (win)
 
318
    {
 
319
        GdkRectangle rect;
 
320
        rect.x = area->display_width/2 - CLOCK_RADIUS - CLOCK_LINE_WIDTH;
 
321
        rect.y = area->display_height/2 - CLOCK_RADIUS - CLOCK_LINE_WIDTH;
 
322
        rect.width = 2 * CLOCK_RADIUS + 1 + 2 * CLOCK_LINE_WIDTH;
 
323
        rect.height = 2 * CLOCK_RADIUS + 1 + 2 * CLOCK_LINE_WIDTH;
 
324
        gdk_window_invalidate_rect(win, &rect, FALSE);
 
325
    }
 
326
 
 
327
    return TRUE;
 
328
}
 
329
 
 
330
/**
 
331
 * Creates the windows and other objects required to do calibration
 
332
 * under GTK. When the window is closed (timed out, calibration finished
 
333
 * or user cancellation), callback will be called, where you should call
 
334
 * calib_area_finish().
 
335
 */
 
336
CalibArea *
 
337
calib_area_new (GdkScreen      *screen,
 
338
                int             monitor,
 
339
                FinishCallback  callback,
 
340
                gpointer        user_data,
 
341
                XYinfo         *old_axis,
 
342
                int             threshold_doubleclick,
 
343
                int             threshold_misclick)
 
344
{
 
345
        CalibArea *calib_area;
 
346
        GdkRectangle rect;
 
347
        GdkWindow *window;
 
348
        GdkRGBA black;
 
349
        GdkCursor *cursor;
 
350
 
 
351
        g_return_val_if_fail (old_axis, NULL);
 
352
        g_return_val_if_fail (callback, NULL);
 
353
 
 
354
        g_debug ("Current calibration: %d, %d, %d, %d\n",
 
355
                 old_axis->x_min,
 
356
                 old_axis->y_min,
 
357
                 old_axis->x_max,
 
358
                 old_axis->y_max);
 
359
 
 
360
        calib_area = g_new0 (CalibArea, 1);
 
361
        calib_area->callback = callback;
 
362
        calib_area->user_data = user_data;
 
363
        calib_area->calibrator.old_axis.x_min = old_axis->x_min;
 
364
        calib_area->calibrator.old_axis.x_max = old_axis->x_max;
 
365
        calib_area->calibrator.old_axis.y_min = old_axis->y_min;
 
366
        calib_area->calibrator.old_axis.y_max = old_axis->y_max;
 
367
        calib_area->calibrator.threshold_doubleclick = threshold_doubleclick;
 
368
        calib_area->calibrator.threshold_misclick = threshold_misclick;
 
369
 
 
370
        /* Set up the window */
 
371
        calib_area->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
372
        gtk_widget_set_app_paintable (GTK_WIDGET (calib_area->window), TRUE);
 
373
 
 
374
        /* Black background */
 
375
        gdk_rgba_parse (&black, "rgb(0,0,0)");
 
376
        gtk_window_set_opacity (GTK_WINDOW (calib_area->window), WINDOW_OPACITY);
 
377
 
 
378
        gtk_widget_realize (calib_area->window);
 
379
        window = gtk_widget_get_window (calib_area->window);
 
380
        gdk_window_set_background_rgba (window, &black);
 
381
 
 
382
        /* No cursor */
 
383
        cursor = gdk_cursor_new (GDK_BLANK_CURSOR);
 
384
        gdk_window_set_cursor (window, cursor);
 
385
        g_object_unref (cursor);
 
386
 
 
387
        /* Listen for mouse events */
 
388
        gtk_widget_add_events (calib_area->window, GDK_KEY_RELEASE_MASK | GDK_BUTTON_PRESS_MASK);
 
389
        gtk_widget_set_can_focus (calib_area->window, TRUE);
 
390
        gtk_window_fullscreen (GTK_WINDOW (calib_area->window));
 
391
 
 
392
        /* Connect callbacks */
 
393
        g_signal_connect (calib_area->window, "draw",
 
394
                          G_CALLBACK(draw), calib_area);
 
395
        g_signal_connect (calib_area->window, "button-press-event",
 
396
                          G_CALLBACK(on_button_press_event), calib_area);
 
397
        g_signal_connect (calib_area->window, "key-release-event",
 
398
                          G_CALLBACK(on_key_release_event), calib_area);
 
399
        g_signal_connect (calib_area->window, "delete-event",
 
400
                          G_CALLBACK(on_delete_event), calib_area);
 
401
 
 
402
        /* Setup timer for animation */
 
403
        calib_area->anim_id = g_timeout_add(TIME_STEP, (GSourceFunc)on_timer_signal, calib_area);
 
404
 
 
405
        /* Move to correct screen */
 
406
        if (screen == NULL)
 
407
                screen = gdk_screen_get_default ();
 
408
        gdk_screen_get_monitor_geometry (screen, monitor, &rect);
 
409
        gtk_window_move (GTK_WINDOW (calib_area->window), rect.x, rect.y);
 
410
        gtk_window_set_default_size (GTK_WINDOW (calib_area->window), rect.width, rect.height);
 
411
 
 
412
        calib_area->calibrator.geometry.x = rect.x;
 
413
        calib_area->calibrator.geometry.y = rect.y;
 
414
        calib_area->calibrator.geometry.width = rect.width;
 
415
        calib_area->calibrator.geometry.height = rect.height;
 
416
 
 
417
        gtk_widget_show_all (calib_area->window);
 
418
 
 
419
        return calib_area;
 
420
}
 
421
 
 
422
/* Finishes the calibration. Note that CalibArea
 
423
 * needs to be destroyed with calib_area_free() afterwards */
 
424
gboolean
 
425
calib_area_finish (CalibArea *area,
 
426
                   XYinfo    *new_axis,
 
427
                   gboolean  *swap_xy)
 
428
{
 
429
        gboolean success;
 
430
 
 
431
        g_return_val_if_fail (area != NULL, FALSE);
 
432
 
 
433
        success = finish (&area->calibrator, new_axis, swap_xy);
 
434
 
 
435
        if (success)
 
436
                g_debug ("Final calibration: %d, %d, %d, %d\n",
 
437
                         new_axis->x_min,
 
438
                         new_axis->y_min,
 
439
                         new_axis->x_max,
 
440
                         new_axis->y_max);
 
441
        else
 
442
                g_debug ("Calibration was aborted or timed out");
 
443
 
 
444
        return success;
 
445
}
 
446
 
 
447
void
 
448
calib_area_free (CalibArea *area)
 
449
{
 
450
        g_return_if_fail (area != NULL);
 
451
 
 
452
        if (area->anim_id > 0) {
 
453
                g_source_remove (area->anim_id);
 
454
                area->anim_id = 0;
 
455
        }
 
456
        gtk_widget_destroy (area->window);
 
457
        g_free (area);
 
458
}