pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Remove the queued_output_stream_clear_on_error test as it was more of a heurisitc test than anything else
2019-11-07, Gary Kramlich
08952f115a7c
Remove the queued_output_stream_clear_on_error test as it was more of a heurisitc test than anything else
/*
GNOME stroke implementation
Copyright (c) 2000, 2001 Dan Nicolaescu
See the file COPYING for distribution information.
*/
#include
"config.h"
#include
<unistd.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<glib.h>
#include
<gtk/gtk.h>
#include
<gdk/gdkx.h>
#include
"gstroke.h"
#include
"gstroke-internal.h"
#include
<X11/Xlib.h>
#include
<X11/Xutil.h>
#include
"gtk3compat.h"
static
void
gstroke_invisible_window_init
(
GtkWidget
*
widget
);
/*FIXME: Maybe these should be put in a structure, and not static...*/
static
Display
*
gstroke_disp
=
NULL
;
static
Window
gstroke_window
;
static
GC
gstroke_gc
;
static
int
mouse_button
=
2
;
static
gboolean
draw_strokes
=
FALSE
;
#define GSTROKE_TIMEOUT_DURATION 10
#define GSTROKE_SIGNALS "gstroke_signals"
struct
gstroke_func_and_data
{
void
(
*
func
)(
GtkWidget
*
,
void
*
);
gpointer
data
;
};
/*FIXME: maybe it's better to just make 2 static variables, not a
structure */
struct
mouse_position
{
struct
s_point
last_point
;
gboolean
invalid
;
};
static
struct
mouse_position
last_mouse_position
;
static
guint
timer_id
;
static
void
gstroke_execute
(
GtkWidget
*
widget
,
const
gchar
*
name
);
static
void
record_stroke_segment
(
GtkWidget
*
widget
)
{
gint
x
,
y
;
struct
gstroke_metrics
*
metrics
;
GdkSeat
*
seat
;
GdkDevice
*
dev
;
g_return_if_fail
(
widget
!=
NULL
);
seat
=
gdk_display_get_default_seat
(
gtk_widget_get_display
(
widget
));
dev
=
gdk_seat_get_pointer
(
seat
);
gdk_window_get_device_position
(
gtk_widget_get_window
(
widget
),
dev
,
&
x
,
&
y
,
NULL
);
if
(
last_mouse_position
.
invalid
)
last_mouse_position
.
invalid
=
FALSE
;
else
if
(
gstroke_draw_strokes
())
{
#if 1
XDrawLine
(
gstroke_disp
,
gstroke_window
,
gstroke_gc
,
last_mouse_position
.
last_point
.
x
,
last_mouse_position
.
last_point
.
y
,
x
,
y
);
/* XFlush (gstroke_disp); */
#else
/* FIXME: this does not work. It will only work if we create
* a corresponding GDK window for stroke_window and draw on
* that... */
gdk_draw_line
(
gtk_widget_get_window
(
widget
),
widget
->
style
->
fg_gc
[
GTK_STATE_NORMAL
],
last_mouse_position
.
last_point
.
x
,
last_mouse_position
.
last_point
.
y
,
x
,
y
);
#endif
}
if
(
last_mouse_position
.
last_point
.
x
!=
x
||
last_mouse_position
.
last_point
.
y
!=
y
)
{
last_mouse_position
.
last_point
.
x
=
x
;
last_mouse_position
.
last_point
.
y
=
y
;
metrics
=
g_object_get_data
(
G_OBJECT
(
widget
),
GSTROKE_METRICS
);
_gstroke_record
(
x
,
y
,
metrics
);
}
}
static
gint
gstroke_timeout
(
gpointer
data
)
{
GtkWidget
*
widget
;
g_return_val_if_fail
(
data
!=
NULL
,
FALSE
);
widget
=
GTK_WIDGET
(
data
);
record_stroke_segment
(
widget
);
return
TRUE
;
}
static
void
gstroke_cancel
(
GdkEvent
*
event
)
{
last_mouse_position
.
invalid
=
TRUE
;
if
(
timer_id
>
0
)
g_source_remove
(
timer_id
);
timer_id
=
0
;
if
(
event
!=
NULL
)
{
gdk_seat_ungrab
(
gdk_event_get_seat
(
event
));
}
if
(
gstroke_draw_strokes
()
&&
gstroke_disp
!=
NULL
)
{
/* get rid of the invisible stroke window */
XUnmapWindow
(
gstroke_disp
,
gstroke_window
);
XFlush
(
gstroke_disp
);
}
}
static
gint
process_event
(
GtkWidget
*
widget
,
GdkEvent
*
event
,
gpointer
data
G_GNUC_UNUSED
)
{
static
GtkWidget
*
original_widget
=
NULL
;
static
GdkCursor
*
cursor
=
NULL
;
switch
(
event
->
type
)
{
case
GDK_BUTTON_PRESS
:
if
(
event
->
button
.
button
!=
gstroke_get_mouse_button
())
{
/* Similar to the bug below catch when any other button is
* clicked after the middle button is clicked (but possibly
* not released)
*/
gstroke_cancel
(
event
);
original_widget
=
NULL
;
break
;
}
original_widget
=
widget
;
/* remeber the widget where
the stroke started */
gstroke_invisible_window_init
(
widget
);
record_stroke_segment
(
widget
);
if
(
cursor
==
NULL
)
{
GdkDisplay
*
display
=
gtk_widget_get_display
(
widget
);
cursor
=
gdk_cursor_new_for_display
(
display
,
GDK_PENCIL
);
}
gdk_seat_grab
(
gdk_event_get_seat
(
event
),
gtk_widget_get_window
(
widget
),
GDK_SEAT_CAPABILITY_ALL_POINTING
,
FALSE
,
cursor
,
event
,
NULL
,
NULL
);
timer_id
=
g_timeout_add
(
GSTROKE_TIMEOUT_DURATION
,
gstroke_timeout
,
widget
);
return
TRUE
;
case
GDK_BUTTON_RELEASE
:
if
((
event
->
button
.
button
!=
gstroke_get_mouse_button
())
||
(
original_widget
==
NULL
))
{
/* Nice bug when you hold down one button and press another. */
/* We'll just cancel the gesture instead. */
gstroke_cancel
(
event
);
original_widget
=
NULL
;
break
;
}
last_mouse_position
.
invalid
=
TRUE
;
original_widget
=
NULL
;
g_source_remove
(
timer_id
);
gdk_seat_ungrab
(
gdk_event_get_seat
(
event
));
timer_id
=
0
;
{
char
result
[
GSTROKE_MAX_SEQUENCE
];
struct
gstroke_metrics
*
metrics
;
metrics
=
(
struct
gstroke_metrics
*
)
g_object_get_data
(
G_OBJECT
(
widget
),
GSTROKE_METRICS
);
if
(
gstroke_draw_strokes
())
{
/* get rid of the invisible stroke window */
XUnmapWindow
(
gstroke_disp
,
gstroke_window
);
XFlush
(
gstroke_disp
);
}
_gstroke_canonical
(
result
,
metrics
);
gstroke_execute
(
widget
,
result
);
}
return
FALSE
;
default
:
break
;
}
return
FALSE
;
}
void
gstroke_set_draw_strokes
(
gboolean
draw
)
{
draw_strokes
=
draw
;
}
gboolean
gstroke_draw_strokes
(
void
)
{
return
draw_strokes
;
}
void
gstroke_set_mouse_button
(
gint
button
)
{
mouse_button
=
button
;
}
guint
gstroke_get_mouse_button
(
void
)
{
return
mouse_button
;
}
void
gstroke_enable
(
GtkWidget
*
widget
)
{
struct
gstroke_metrics
*
metrics
=
(
struct
gstroke_metrics
*
)
g_object_get_data
(
G_OBJECT
(
widget
),
GSTROKE_METRICS
);
if
(
metrics
==
NULL
)
{
metrics
=
(
struct
gstroke_metrics
*
)
g_malloc
(
sizeof
(
struct
gstroke_metrics
));
metrics
->
pointList
=
NULL
;
metrics
->
min_x
=
10000
;
metrics
->
min_y
=
10000
;
metrics
->
max_x
=
0
;
metrics
->
max_y
=
0
;
metrics
->
point_count
=
0
;
g_object_set_data
(
G_OBJECT
(
widget
),
GSTROKE_METRICS
,
metrics
);
g_signal_connect
(
G_OBJECT
(
widget
),
"event"
,
G_CALLBACK
(
process_event
),
NULL
);
}
else
_gstroke_init
(
metrics
);
last_mouse_position
.
invalid
=
TRUE
;
}
void
gstroke_disable
(
GtkWidget
*
widget
)
{
g_signal_handlers_disconnect_by_func
(
G_OBJECT
(
widget
),
G_CALLBACK
(
process_event
),
NULL
);
}
guint
gstroke_signal_connect
(
GtkWidget
*
widget
,
const
gchar
*
name
,
void
(
*
func
)(
GtkWidget
*
widget
,
void
*
data
),
gpointer
data
)
{
struct
gstroke_func_and_data
*
func_and_data
;
GHashTable
*
hash_table
=
(
GHashTable
*
)
g_object_get_data
(
G_OBJECT
(
widget
),
GSTROKE_SIGNALS
);
if
(
!
hash_table
)
{
hash_table
=
g_hash_table_new
(
g_str_hash
,
g_str_equal
);
g_object_set_data
(
G_OBJECT
(
widget
),
GSTROKE_SIGNALS
,
(
gpointer
)
hash_table
);
}
func_and_data
=
g_new
(
struct
gstroke_func_and_data
,
1
);
func_and_data
->
func
=
func
;
func_and_data
->
data
=
data
;
g_hash_table_insert
(
hash_table
,
(
gpointer
)
name
,
(
gpointer
)
func_and_data
);
return
TRUE
;
}
static
void
gstroke_execute
(
GtkWidget
*
widget
,
const
gchar
*
name
)
{
GHashTable
*
hash_table
=
(
GHashTable
*
)
g_object_get_data
(
G_OBJECT
(
widget
),
GSTROKE_SIGNALS
);
#if 0
purple_debug(PURPLE_DEBUG_MISC, "gestures", "gstroke %s\n", name);
#endif
if
(
hash_table
)
{
struct
gstroke_func_and_data
*
fd
=
(
struct
gstroke_func_and_data
*
)
g_hash_table_lookup
(
hash_table
,
name
);
if
(
fd
)
(
*
fd
->
func
)(
widget
,
fd
->
data
);
}
}
void
gstroke_cleanup
(
GtkWidget
*
widget
)
{
struct
gstroke_metrics
*
metrics
;
GHashTable
*
hash_table
=
(
GHashTable
*
)
g_object_get_data
(
G_OBJECT
(
widget
),
GSTROKE_SIGNALS
);
if
(
hash_table
)
/* FIXME: does this delete the elements too? */
g_hash_table_destroy
(
hash_table
);
g_object_steal_data
(
G_OBJECT
(
widget
),
GSTROKE_SIGNALS
);
metrics
=
(
struct
gstroke_metrics
*
)
g_object_get_data
(
G_OBJECT
(
widget
),
GSTROKE_METRICS
);
g_free
(
metrics
);
g_object_steal_data
(
G_OBJECT
(
widget
),
GSTROKE_METRICS
);
}
/* This function should be written using GTK+ primitives*/
static
void
gstroke_invisible_window_init
(
GtkWidget
*
widget
)
{
XSetWindowAttributes
w_attr
;
XWindowAttributes
orig_w_attr
;
unsigned
long
mask
,
col_border
,
col_background
;
unsigned
int
border_width
;
XSizeHints
hints
;
Display
*
disp
=
GDK_WINDOW_XDISPLAY
(
gtk_widget_get_window
(
widget
));
Window
wind
=
gdk_x11_window_get_xid
(
gtk_widget_get_window
(
widget
));
int
screen
=
DefaultScreen
(
disp
);
if
(
!
gstroke_draw_strokes
())
return
;
gstroke_disp
=
disp
;
/* X server should save what's underneath */
XGetWindowAttributes
(
gstroke_disp
,
wind
,
&
orig_w_attr
);
hints
.
x
=
orig_w_attr
.
x
;
hints
.
y
=
orig_w_attr
.
y
;
hints
.
width
=
orig_w_attr
.
width
;
hints
.
height
=
orig_w_attr
.
height
;
mask
=
CWSaveUnder
;
w_attr
.
save_under
=
True
;
/* inhibit all the decorations */
mask
|=
CWOverrideRedirect
;
w_attr
.
override_redirect
=
True
;
/* Don't set a background, transparent window */
mask
|=
CWBackPixmap
;
w_attr
.
background_pixmap
=
None
;
/* Default input window look */
col_background
=
WhitePixel
(
gstroke_disp
,
screen
);
/* no border for the window */
#if 0
border_width = 5;
#endif
border_width
=
0
;
col_border
=
BlackPixel
(
gstroke_disp
,
screen
);
gstroke_window
=
XCreateSimpleWindow
(
gstroke_disp
,
wind
,
0
,
0
,
hints
.
width
-
2
*
border_width
,
hints
.
height
-
2
*
border_width
,
border_width
,
col_border
,
col_background
);
gstroke_gc
=
XCreateGC
(
gstroke_disp
,
gstroke_window
,
0
,
NULL
);
XSetFunction
(
gstroke_disp
,
gstroke_gc
,
GXinvert
);
XChangeWindowAttributes
(
gstroke_disp
,
gstroke_window
,
mask
,
&
w_attr
);
XSetLineAttributes
(
gstroke_disp
,
gstroke_gc
,
2
,
LineSolid
,
CapButt
,
JoinMiter
);
XMapRaised
(
gstroke_disp
,
gstroke_window
);
#if 0
/*FIXME: is this call really needed? If yes, does it need the real
argc and argv? */
hints.flags = PPosition | PSize;
XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL,
(Pixmap)NULL, NULL, 0, &hints);
/* Receive the close window client message */
{
/* FIXME: is this really needed? If yes, something should be done
with wmdelete...*/
Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW",
False);
XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True);
}
#endif
}