2 GNOME stroke implementation
3 Copyright (c) 2000, 2001 Dan Nicolaescu
4 See the file COPYING for distribution information.
17 #include "gstroke-internal.h"
20 #include <X11/Xutil.h>
22 #include "gtk3compat.h"
24 static void gstroke_invisible_window_init (GtkWidget
*widget
);
25 /*FIXME: Maybe these should be put in a structure, and not static...*/
26 static Display
* gstroke_disp
= NULL
;
27 static Window gstroke_window
;
29 static int mouse_button
= 2;
30 static gboolean draw_strokes
= FALSE
;
32 #define GSTROKE_TIMEOUT_DURATION 10
34 #define GSTROKE_SIGNALS "gstroke_signals"
36 struct gstroke_func_and_data
{
37 void (*func
)(GtkWidget
*, void *);
42 /*FIXME: maybe it's better to just make 2 static variables, not a
44 struct mouse_position
{
45 struct s_point last_point
;
50 static struct mouse_position last_mouse_position
;
51 static guint timer_id
;
53 static void gstroke_execute (GtkWidget
*widget
, const gchar
*name
);
56 record_stroke_segment(GtkWidget
*widget
)
59 struct gstroke_metrics
*metrics
;
60 GdkDeviceManager
*devmgr
;
63 g_return_if_fail(widget
!= NULL
);
65 devmgr
= gdk_display_get_device_manager(gtk_widget_get_display(widget
));
66 dev
= gdk_device_manager_get_client_pointer(devmgr
);
67 gdk_window_get_device_position(gtk_widget_get_window(widget
),
70 if (last_mouse_position
.invalid
)
71 last_mouse_position
.invalid
= FALSE
;
72 else if (gstroke_draw_strokes()) {
74 XDrawLine(gstroke_disp
, gstroke_window
, gstroke_gc
,
75 last_mouse_position
.last_point
.x
,
76 last_mouse_position
.last_point
.y
, x
, y
);
77 /* XFlush (gstroke_disp); */
79 /* FIXME: this does not work. It will only work if we create
80 * a corresponding GDK window for stroke_window and draw on
82 gdk_draw_line(gtk_widget_get_window(widget
),
83 widget
->style
->fg_gc
[GTK_STATE_NORMAL
],
84 last_mouse_position
.last_point
.x
,
85 last_mouse_position
.last_point
.y
, x
, y
);
89 if (last_mouse_position
.last_point
.x
!= x
||
90 last_mouse_position
.last_point
.y
!= y
)
92 last_mouse_position
.last_point
.x
= x
;
93 last_mouse_position
.last_point
.y
= y
;
94 metrics
= g_object_get_data(G_OBJECT(widget
), GSTROKE_METRICS
);
95 _gstroke_record (x
, y
, metrics
);
100 gstroke_timeout (gpointer data
)
104 g_return_val_if_fail(data
!= NULL
, FALSE
);
106 widget
= GTK_WIDGET (data
);
107 record_stroke_segment (widget
);
112 static void gstroke_cancel(GdkEvent
*event
)
114 last_mouse_position
.invalid
= TRUE
;
117 g_source_remove (timer_id
);
122 gdk_device_ungrab(gdk_event_get_device(event
), event
->button
.time
);
124 if (gstroke_draw_strokes() && gstroke_disp
!= NULL
) {
125 /* get rid of the invisible stroke window */
126 XUnmapWindow (gstroke_disp
, gstroke_window
);
127 XFlush (gstroke_disp
);
133 process_event (GtkWidget
*widget
, GdkEvent
*event
, gpointer data G_GNUC_UNUSED
)
135 static GtkWidget
*original_widget
= NULL
;
136 static GdkCursor
*cursor
= NULL
;
138 switch (event
->type
) {
139 case GDK_BUTTON_PRESS
:
140 if (event
->button
.button
!= gstroke_get_mouse_button()) {
141 /* Similar to the bug below catch when any other button is
142 * clicked after the middle button is clicked (but possibly
145 gstroke_cancel(event
);
146 original_widget
= NULL
;
150 original_widget
= widget
; /* remeber the widget where
151 the stroke started */
153 gstroke_invisible_window_init (widget
);
155 record_stroke_segment (widget
);
157 if (cursor
== NULL
) {
158 GdkDisplay
*display
= gtk_widget_get_display(widget
);
159 cursor
= gdk_cursor_new_for_display(display
, GDK_PENCIL
);
162 gdk_device_grab(gdk_event_get_device(event
),
163 gtk_widget_get_window(widget
), GDK_OWNERSHIP_WINDOW
,
164 FALSE
, GDK_BUTTON_RELEASE_MASK
, cursor
,
166 timer_id
= g_timeout_add (GSTROKE_TIMEOUT_DURATION
,
167 gstroke_timeout
, widget
);
170 case GDK_BUTTON_RELEASE
:
171 if ((event
->button
.button
!= gstroke_get_mouse_button())
172 || (original_widget
== NULL
)) {
174 /* Nice bug when you hold down one button and press another. */
175 /* We'll just cancel the gesture instead. */
176 gstroke_cancel(event
);
177 original_widget
= NULL
;
181 last_mouse_position
.invalid
= TRUE
;
182 original_widget
= NULL
;
183 g_source_remove (timer_id
);
184 gdk_device_ungrab(gdk_event_get_device(event
), event
->button
.time
);
188 char result
[GSTROKE_MAX_SEQUENCE
];
189 struct gstroke_metrics
*metrics
;
191 metrics
= (struct gstroke_metrics
*)g_object_get_data(G_OBJECT (widget
),
193 if (gstroke_draw_strokes()) {
194 /* get rid of the invisible stroke window */
195 XUnmapWindow (gstroke_disp
, gstroke_window
);
196 XFlush (gstroke_disp
);
199 _gstroke_canonical (result
, metrics
);
200 gstroke_execute (widget
, result
);
212 gstroke_set_draw_strokes(gboolean draw
)
218 gstroke_draw_strokes(void)
224 gstroke_set_mouse_button(gint button
)
226 mouse_button
= button
;
230 gstroke_get_mouse_button(void)
236 gstroke_enable (GtkWidget
*widget
)
238 struct gstroke_metrics
*
239 metrics
= (struct gstroke_metrics
*)g_object_get_data(G_OBJECT(widget
),
243 metrics
= (struct gstroke_metrics
*)g_malloc (sizeof
244 (struct gstroke_metrics
));
245 metrics
->pointList
= NULL
;
246 metrics
->min_x
= 10000;
247 metrics
->min_y
= 10000;
250 metrics
->point_count
= 0;
252 g_object_set_data(G_OBJECT(widget
), GSTROKE_METRICS
, metrics
);
254 g_signal_connect(G_OBJECT(widget
), "event",
255 G_CALLBACK(process_event
), NULL
);
258 _gstroke_init (metrics
);
260 last_mouse_position
.invalid
= TRUE
;
264 gstroke_disable(GtkWidget
*widget
)
266 g_signal_handlers_disconnect_by_func(G_OBJECT(widget
), G_CALLBACK(process_event
), NULL
);
270 gstroke_signal_connect (GtkWidget
*widget
,
272 void (*func
)(GtkWidget
*widget
, void *data
),
275 struct gstroke_func_and_data
*func_and_data
;
276 GHashTable
*hash_table
=
277 (GHashTable
*)g_object_get_data(G_OBJECT(widget
), GSTROKE_SIGNALS
);
281 hash_table
= g_hash_table_new (g_str_hash
, g_str_equal
);
282 g_object_set_data(G_OBJECT(widget
), GSTROKE_SIGNALS
,
283 (gpointer
)hash_table
);
285 func_and_data
= g_new (struct gstroke_func_and_data
, 1);
286 func_and_data
->func
= func
;
287 func_and_data
->data
= data
;
288 g_hash_table_insert (hash_table
, (gpointer
)name
, (gpointer
)func_and_data
);
293 gstroke_execute (GtkWidget
*widget
, const gchar
*name
)
296 GHashTable
*hash_table
=
297 (GHashTable
*)g_object_get_data(G_OBJECT(widget
), GSTROKE_SIGNALS
);
300 purple_debug(PURPLE_DEBUG_MISC
, "gestures", "gstroke %s\n", name
);
305 struct gstroke_func_and_data
*fd
=
306 (struct gstroke_func_and_data
*)g_hash_table_lookup (hash_table
, name
);
308 (*fd
->func
)(widget
, fd
->data
);
313 gstroke_cleanup (GtkWidget
*widget
)
315 struct gstroke_metrics
*metrics
;
316 GHashTable
*hash_table
=
317 (GHashTable
*)g_object_get_data(G_OBJECT(widget
), GSTROKE_SIGNALS
);
319 /* FIXME: does this delete the elements too? */
320 g_hash_table_destroy (hash_table
);
322 g_object_steal_data(G_OBJECT(widget
), GSTROKE_SIGNALS
);
324 metrics
= (struct gstroke_metrics
*)g_object_get_data(G_OBJECT(widget
),
327 g_object_steal_data(G_OBJECT(widget
), GSTROKE_METRICS
);
331 /* This function should be written using GTK+ primitives*/
333 gstroke_invisible_window_init (GtkWidget
*widget
)
335 XSetWindowAttributes w_attr
;
336 XWindowAttributes orig_w_attr
;
337 unsigned long mask
, col_border
, col_background
;
338 unsigned int border_width
;
340 Display
*disp
= GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget
));
341 Window wind
= gdk_x11_window_get_xid(gtk_widget_get_window(widget
));
342 int screen
= DefaultScreen (disp
);
344 if (!gstroke_draw_strokes())
349 /* X server should save what's underneath */
350 XGetWindowAttributes (gstroke_disp
, wind
, &orig_w_attr
);
351 hints
.x
= orig_w_attr
.x
;
352 hints
.y
= orig_w_attr
.y
;
353 hints
.width
= orig_w_attr
.width
;
354 hints
.height
= orig_w_attr
.height
;
356 w_attr
.save_under
= True
;
358 /* inhibit all the decorations */
359 mask
|= CWOverrideRedirect
;
360 w_attr
.override_redirect
= True
;
362 /* Don't set a background, transparent window */
363 mask
|= CWBackPixmap
;
364 w_attr
.background_pixmap
= None
;
366 /* Default input window look */
367 col_background
= WhitePixel (gstroke_disp
, screen
);
369 /* no border for the window */
375 col_border
= BlackPixel (gstroke_disp
, screen
);
377 gstroke_window
= XCreateSimpleWindow (gstroke_disp
, wind
,
379 hints
.width
- 2 * border_width
,
380 hints
.height
- 2 * border_width
,
382 col_border
, col_background
);
384 gstroke_gc
= XCreateGC (gstroke_disp
, gstroke_window
, 0, NULL
);
386 XSetFunction (gstroke_disp
, gstroke_gc
, GXinvert
);
388 XChangeWindowAttributes (gstroke_disp
, gstroke_window
, mask
, &w_attr
);
390 XSetLineAttributes (gstroke_disp
, gstroke_gc
, 2, LineSolid
,
392 XMapRaised (gstroke_disp
, gstroke_window
);
395 /*FIXME: is this call really needed? If yes, does it need the real
397 hints
.flags
= PPosition
| PSize
;
398 XSetStandardProperties (gstroke_disp
, gstroke_window
, "gstroke_test", NULL
,
399 (Pixmap
)NULL
, NULL
, 0, &hints
);
402 /* Receive the close window client message */
404 /* FIXME: is this really needed? If yes, something should be done
406 Atom wmdelete
= XInternAtom (gstroke_disp
, "WM_DELETE_WINDOW",
408 XSetWMProtocols (gstroke_disp
, gstroke_window
, &wmdelete
, True
);