1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
#include <X11/Xatom.h>
#include <gdk/gdkx.h>
#include "../global.h"
#if GTK_VERSION == 3
#include <gtk/gtkx.h>
#endif
#include "topmenu-client.h"
#define OBJECT_DATA_KEY_PLUG "topmenu-plug"
static gboolean handle_plug_delete(GtkPlug *plug, GdkEvent *event, GdkWindow *window)
{
return TRUE; // Prevent deletion of plug window
}
static gboolean handle_widget_button_event(GtkWidget *widget, GdkEvent *event, GtkPlug *plug)
{
if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) {
return FALSE;
}
if (event->button.button == 1) {
return FALSE;
}
GdkWindow *socket = gtk_plug_get_socket_window(plug);
if (socket) {
GdkDisplay * display = gdk_window_get_display(socket);
GdkScreen *screen = gdk_window_get_screen(socket);
GdkWindow *root = gdk_screen_get_root_window(screen);
Display *dpy = GDK_DISPLAY_XDISPLAY(display);
Window xwin = GDK_WINDOW_XID(socket);
if (event->type == GDK_BUTTON_PRESS) {
gdk_display_pointer_ungrab(gtk_widget_get_display(widget),
GDK_CURRENT_TIME);
}
XEvent e;
long mask = event->type == GDK_BUTTON_PRESS ? ButtonPressMask : ButtonReleaseMask;
e.type = event->type == GDK_BUTTON_PRESS ? ButtonPress : ButtonRelease;
e.xbutton.window = xwin;
e.xbutton.display = dpy;
e.xbutton.root = GDK_WINDOW_XID(root);
e.xbutton.time = event->button.time;
e.xbutton.button = event->button.button;
e.xbutton.state = event->button.state;
e.xbutton.x = event->button.x;
e.xbutton.y = event->button.y;
e.xbutton.x_root = event->button.x_root;
e.xbutton.y_root = event->button.y_root;
e.xbutton.same_screen = True;
gdk_error_trap_push();
XSendEvent(dpy, xwin, True, mask, &e);
g_debug("Forwarding button %d %s event to 0x%lx",
e.xbutton.button,
event->type == GDK_BUTTON_PRESS ? "press" : "release",
xwin);
gdk_flush();
gdk_error_trap_pop();
return TRUE;
}
return FALSE;
}
void topmenu_client_connect_window_widget(GdkWindow *window, GtkWidget *widget)
{
Display *display = GDK_WINDOW_XDISPLAY(window);
if (g_object_get_data(G_OBJECT(window), OBJECT_DATA_KEY_PLUG)) {
topmenu_client_disconnect_window(window);
}
Window xwin = GDK_WINDOW_XID(window);
GtkPlug *plug = GTK_PLUG(gtk_plug_new(0));
gtk_container_add(GTK_CONTAINER(plug), widget);
g_signal_connect_object(plug, "delete-event",
G_CALLBACK(handle_plug_delete), window, 0);
g_signal_connect_object(widget, "button-press-event",
G_CALLBACK(handle_widget_button_event), plug, 0);
g_signal_connect_object(widget, "button-release-event",
G_CALLBACK(handle_widget_button_event), plug, 0);
gtk_widget_show(GTK_WIDGET(plug));
// Gtk+ should keep a reference to plug as it is a "top level" widget.
// Otherwise I failed to parse the documentation.
g_warn_if_fail(G_OBJECT(plug)->ref_count == 1);
Window plug_xwin = gtk_plug_get_id(plug);
Atom atom = XInternAtom(display, ATOM_TOPMENU_WINDOW, False);
XChangeProperty(display, xwin, atom,
XA_WINDOW, 32, PropModeReplace,
(unsigned char*)&plug_xwin, 1);
g_object_set_data_full(G_OBJECT(window), OBJECT_DATA_KEY_PLUG, plug,
(GDestroyNotify) >k_widget_destroy);
}
void topmenu_client_disconnect_window(GdkWindow *window)
{
Display *display = GDK_WINDOW_XDISPLAY(window);
gpointer window_data = g_object_steal_data(G_OBJECT(window), OBJECT_DATA_KEY_PLUG);
g_return_if_fail(window_data);
Window xwin = GDK_WINDOW_XID(window);
GtkPlug *plug = GTK_PLUG(window_data);
g_return_if_fail(plug);
Atom atom = XInternAtom(display, ATOM_TOPMENU_WINDOW, False);
XDeleteProperty(display, xwin, atom);
g_warn_if_fail(G_OBJECT(plug)->ref_count == 1);
gtk_widget_destroy(GTK_WIDGET(plug));
}
|