merge the C branch into HEAD
[mikachu/openbox.git] / c / mbind.c
1 #include "mbind.h"
2 #include "kbind.h"
3 #include "frame.h"
4 #include "openbox.h"
5 #include "eventdata.h"
6 #include "hooks.h"
7
8 #include <glib.h>
9 #ifdef HAVE_STDLIB_H
10 #  include <stdlib.h>
11 #endif
12
13 /* GData of GSList*'s of PointerBinding*'s. */
14 static GData *bound_contexts;
15
16 static gboolean grabbed;
17
18 struct mbind_foreach_grab_temp {
19     Client *client;
20     gboolean grab;
21 };
22
23 typedef struct {
24     guint state;
25     guint button;
26     char *name;
27 } PointerBinding;
28
29 static gboolean translate(char *str, guint *state, guint *button)
30 {
31     char **parsed;
32     char *l;
33     int i;
34     gboolean ret = FALSE;
35
36     parsed = g_strsplit(str, "-", -1);
37     
38     /* first, find the button (last token) */
39     l = NULL;
40     for (i = 0; parsed[i] != NULL; ++i)
41         l = parsed[i];
42     if (l == NULL)
43         goto translation_fail;
44
45     /* figure out the mod mask */
46     *state = 0;
47     for (i = 0; parsed[i] != l; ++i) {
48         guint m = kbind_translate_modifier(parsed[i]);
49         if (!m) goto translation_fail;
50         *state |= m;
51     }
52
53     /* figure out the button */
54     *button = atoi(l);
55     if (!*button) {
56         g_warning("Invalid button '%s' in pointer binding.", l);
57         goto translation_fail;
58     }
59
60     ret = TRUE;
61
62 translation_fail:
63     g_strfreev(parsed);
64     return ret;
65 }
66
67 void grab_button(Client *client, guint state, guint button, GQuark context,
68                  gboolean grab)
69 {
70     Window win;
71     int mode = GrabModeAsync;
72     unsigned int mask;
73
74     if (context == g_quark_try_string("frame")) {
75         win = client->frame->window;
76         mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
77     } else if (context == g_quark_try_string("client")) {
78         win = client->frame->plate;
79         mode = GrabModeSync; /* this is handled in mbind_fire */
80         mask = ButtonPressMask; /* can't catch more than this with Sync mode
81                                    the release event is manufactured in
82                                    mbind_fire */
83     } else return;
84
85     if (grab)
86         XGrabButton(ob_display, button, state, win, FALSE, mask, mode,
87                     GrabModeAsync, None, None);
88     else
89         XUngrabButton(ob_display, button, state, win);
90 }
91
92 static void mbind_foreach_grab(GQuark key, gpointer data, gpointer user_data)
93 {
94     struct mbind_foreach_grab_temp *d = user_data;
95     PointerBinding *b = ((GSList *)data)->data;
96     if (b != NULL)
97         grab_button(d->client, b->state, b->button, key, d->grab);
98 }
99   
100 void mbind_grab_all(Client *client, gboolean grab)
101 {
102     struct mbind_foreach_grab_temp bt;
103     bt.client = client;
104     bt.grab = grab;
105     g_datalist_foreach(&bound_contexts, mbind_foreach_grab, &bt);
106 }
107
108 void grab_all_clients(gboolean grab)
109 {
110     GSList *it;
111
112     for (it = client_list; it != NULL; it = it->next)
113         mbind_grab_all(it->data, grab);
114 }
115
116 void mbind_startup()
117 {
118     grabbed = FALSE;
119     g_datalist_init(&bound_contexts);
120 }
121
122 void mbind_shutdown()
123 {
124     if (grabbed)
125         mbind_grab_pointer(FALSE);
126     mbind_clearall();
127     g_datalist_clear(&bound_contexts);
128 }
129
130 gboolean mbind_add(char *name, GQuark context)
131 {
132     guint state, button;
133     PointerBinding *b;
134     GSList *it;
135
136     if (!translate(name, &state, &button))
137         return FALSE;
138
139     for (it = g_datalist_id_get_data(&bound_contexts, context);
140          it != NULL; it = it->next){
141         b = it->data;
142         if (b->state == state && b->button == button)
143             return TRUE; /* already bound */
144     }
145
146     grab_all_clients(FALSE);
147
148     /* add the binding */
149     b = g_new(PointerBinding, 1);
150     b->state = state;
151     b->button = button;
152     b->name = g_strdup(name);
153     g_datalist_id_set_data(&bound_contexts, context, 
154         g_slist_append(g_datalist_id_get_data(&bound_contexts, context), b));
155     grab_all_clients(TRUE);
156
157     return TRUE;
158 }
159
160 static void mbind_foreach_clear(GQuark key, gpointer data, gpointer user_data)
161 {
162     GSList *it;
163     user_data = user_data;
164     for (it = data; it != NULL; it = it->next) {
165         PointerBinding *b = it->data;
166         g_free(b->name);
167         g_free(b);
168     }
169     g_slist_free(data);
170 }
171 void mbind_clearall()
172 {
173     grab_all_clients(FALSE);
174     g_datalist_foreach(&bound_contexts, mbind_foreach_clear, NULL);
175 }
176
177 void mbind_fire(guint state, guint button, GQuark context, EventType type,
178                 Client *client, int xroot, int yroot)
179 {
180     GSList *it;
181
182     if (grabbed) {
183             EventData *data;
184             data = eventdata_new_pointer(type, context, client, state, button,
185                                          NULL, xroot, yroot);
186             g_assert(data != NULL);
187             hooks_fire_pointer(data);
188             eventdata_free(data);
189             return;
190     }
191
192     for (it = g_datalist_id_get_data(&bound_contexts, context);
193          it != NULL; it = it->next){
194         PointerBinding *b = it->data;
195         if (b->state == state && b->button == button) {
196             EventData *data;
197             data = eventdata_new_pointer(type, context, client, state, button,
198                                          b->name, xroot, yroot);
199             g_assert(data != NULL);
200             hooks_fire(data);
201             eventdata_free(data);
202             break;
203         }
204     }
205 }
206
207 gboolean mbind_grab_pointer(gboolean grab)
208 {
209     gboolean ret = TRUE;
210     if (grab)
211         ret = XGrabPointer(ob_display, ob_root, FALSE, (ButtonPressMask |
212                                                         ButtonReleaseMask |
213                                                         ButtonMotionMask |
214                                                         PointerMotionMask),
215                            GrabModeAsync, GrabModeAsync, None, None,
216                            CurrentTime) == GrabSuccess;
217     else
218         XUngrabPointer(ob_display, CurrentTime);
219     return ret;
220 }