]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/actions.c
Merge branch 'mikabox/personal' into wip/mikabox
[mikachu/openbox.git] / openbox / actions.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    actions.c for the Openbox window manager
4    Copyright (c) 2007        Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "actions.h"
20 #include "gettext.h"
21 #include "grab.h"
22 #include "screen.h"
23 #include "event.h"
24 #include "config.h"
25 #include "client.h"
26 #include "focus.h"
27 #include "openbox.h"
28 #include "debug.h"
29
30 #include "actions/all.h"
31
32 static void     actions_definition_ref(ObActionsDefinition *def);
33 static void     actions_definition_unref(ObActionsDefinition *def);
34 static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state);
35 static void     actions_interactive_end_act();
36 static ObActionsAct* actions_build_act_from_string(const gchar *name);
37
38 static ObActionsAct *interactive_act = NULL;
39 static guint         interactive_initial_state = 0;
40 static gboolean      stop_running = FALSE;
41
42 struct _ObActionsDefinition {
43     guint ref;
44
45     gchar *name;
46
47     gboolean canbeinteractive;
48     union {
49         ObActionsIDataSetupFunc i;
50         ObActionsDataSetupFunc n;
51     } setup;
52     ObActionsDataFreeFunc free;
53     ObActionsRunFunc run;
54     ObActionsShutdownFunc shutdown;
55     gboolean modifies_focused_window;
56 };
57
58 struct _ObActionsAct {
59     guint ref;
60
61     ObActionsDefinition *def;
62     ObActionsIPreFunc i_pre;
63     ObActionsIInputFunc i_input;
64     ObActionsICancelFunc i_cancel;
65     ObActionsIPostFunc i_post;
66     gpointer options;
67 };
68
69 static GSList *registered = NULL;
70
71 void actions_startup(gboolean reconfig)
72 {
73     if (reconfig) return;
74
75     action_all_startup();
76 }
77
78 void actions_shutdown(gboolean reconfig)
79 {
80     actions_interactive_cancel_act();
81
82     if (reconfig) return;
83
84     /* free all the registered actions */
85     while (registered) {
86         ObActionsDefinition *d = registered->data;
87         if (d->shutdown) d->shutdown();
88         actions_definition_unref(d);
89         registered = g_slist_delete_link(registered, registered);
90     }
91 }
92
93 ObActionsDefinition* do_register(const gchar *name,
94                                  ObActionsDataFreeFunc free,
95                                  ObActionsRunFunc run)
96 {
97     GSList *it;
98     ObActionsDefinition *def;
99
100     g_assert(run != NULL);
101
102     for (it = registered; it; it = g_slist_next(it)) {
103         def = it->data;
104         if (!g_ascii_strcasecmp(name, def->name)) /* already registered */
105             return NULL;
106     }
107
108     def = g_slice_new0(ObActionsDefinition);
109     def->ref = 1;
110     def->name = g_strdup(name);
111     def->free = free;
112     def->run = run;
113     def->shutdown = NULL;
114     def->modifies_focused_window = TRUE;
115
116     registered = g_slist_prepend(registered, def);
117     return def;
118 }
119
120 gboolean actions_register_i(const gchar *name,
121                             ObActionsIDataSetupFunc setup,
122                             ObActionsDataFreeFunc free,
123                             ObActionsRunFunc run)
124 {
125     ObActionsDefinition *def = do_register(name, free, run);
126     if (def) {
127         def->canbeinteractive = TRUE;
128         def->setup.i = setup;
129     }
130     return def != NULL;
131 }
132
133 gboolean actions_register(const gchar *name,
134                           ObActionsDataSetupFunc setup,
135                           ObActionsDataFreeFunc free,
136                           ObActionsRunFunc run)
137 {
138     ObActionsDefinition *def = do_register(name, free, run);
139     if (def) {
140         def->canbeinteractive = FALSE;
141         def->setup.n = setup;
142     }
143     return def != NULL;
144 }
145
146 gboolean actions_set_shutdown(const gchar *name,
147                               ObActionsShutdownFunc shutdown)
148 {
149     GSList *it;
150     ObActionsDefinition *def;
151
152     for (it = registered; it; it = g_slist_next(it)) {
153         def = it->data;
154         if (!g_ascii_strcasecmp(name, def->name)) {
155             def->shutdown = shutdown;
156             return TRUE;
157         }
158     }
159     return FALSE;
160 }
161
162 gboolean actions_set_modifies_focused_window(const gchar *name,
163                                              gboolean modifies)
164 {
165     GSList *it;
166     ObActionsDefinition *def;
167
168     for (it = registered; it; it = g_slist_next(it)) {
169         def = it->data;
170         if (!g_ascii_strcasecmp(name, def->name)) {
171             def->modifies_focused_window = modifies;
172             return TRUE;
173         }
174     }
175     return FALSE;
176 }
177
178 static void actions_definition_ref(ObActionsDefinition *def)
179 {
180     ++def->ref;
181 }
182
183 static void actions_definition_unref(ObActionsDefinition *def)
184 {
185     if (def && --def->ref == 0) {
186         g_free(def->name);
187         g_slice_free(ObActionsDefinition, def);
188     }
189 }
190
191 static ObActionsAct* actions_build_act_from_string(const gchar *name)
192 {
193     GSList *it;
194     ObActionsDefinition *def = NULL;
195     ObActionsAct *act = NULL;
196
197     /* find the requested action */
198     for (it = registered; it; it = g_slist_next(it)) {
199         def = it->data;
200         if (!g_ascii_strcasecmp(name, def->name))
201             break;
202         def = NULL;
203     }
204
205     /* if we found the action */
206     if (def) {
207         act = g_slice_new(ObActionsAct);
208         act->ref = 1;
209         act->def = def;
210         actions_definition_ref(act->def);
211         act->i_pre = NULL;
212         act->i_input = NULL;
213         act->i_cancel = NULL;
214         act->i_post = NULL;
215         act->options = NULL;
216     } else
217         g_message(_("Invalid action \"%s\" requested. No such action exists."),
218                   name);
219
220     return act;
221 }
222
223 ObActionsAct* actions_parse_string(const gchar *name)
224 {
225     ObActionsAct *act = NULL;
226
227     if ((act = actions_build_act_from_string(name))) {
228         if (act->def->canbeinteractive) {
229             if (act->def->setup.i)
230                 act->options = act->def->setup.i(NULL,
231                                                  &act->i_pre,
232                                                  &act->i_input,
233                                                  &act->i_cancel,
234                                                  &act->i_post);
235         }
236         else {
237             if (act->def->setup.n)
238                 act->options = act->def->setup.n(NULL);
239         }
240     }
241                 
242
243     return act;
244 }
245
246 ObActionsAct* actions_parse(xmlNodePtr node)
247 {
248     gchar *name;
249     ObActionsAct *act = NULL;
250
251     if (obt_xml_attr_string(node, "name", &name)) {
252         if ((act = actions_build_act_from_string(name))) {
253             /* there is more stuff to parse here */
254             if (act->def->canbeinteractive) {
255                 if (act->def->setup.i)
256                     act->options = act->def->setup.i(node->children,
257                                                      &act->i_pre,
258                                                      &act->i_input,
259                                                      &act->i_cancel,
260                                                      &act->i_post);
261             }
262             else {
263                 if (act->def->setup.n)
264                     act->options = act->def->setup.n(node->children);
265             }
266         }
267         g_free(name);
268     }
269
270     return act;
271 }
272
273 gboolean actions_act_is_interactive(ObActionsAct *act)
274 {
275     return act->i_input != NULL;
276 }
277
278 void actions_act_ref(ObActionsAct *act)
279 {
280     ++act->ref;
281 }
282
283 void actions_act_unref(ObActionsAct *act)
284 {
285     if (act && --act->ref == 0) {
286         /* free the action specific options */
287         if (act->def->free)
288             act->def->free(act->options);
289         /* unref the definition */
290         actions_definition_unref(act->def);
291         g_slice_free(ObActionsAct, act);
292     }
293 }
294
295 static void actions_setup_data(ObActionsData *data,
296                                ObUserAction uact,
297                                guint state,
298                                gint x,
299                                gint y,
300                                gint button,
301                                ObFrameContext con,
302                                struct _ObClient *client)
303 {
304     data->uact = uact;
305     data->state = state;
306     data->x = x;
307     data->y = y;
308     data->button = button;
309     data->context = con;
310     data->client = client;
311 }
312
313 void actions_stop_running()
314 {
315     stop_running = TRUE;
316 }
317
318 void actions_run_acts(GSList *acts,
319                       ObUserAction uact,
320                       guint state,
321                       gint x,
322                       gint y,
323                       gint button,
324                       ObFrameContext con,
325                       struct _ObClient *client)
326 {
327     GSList *it;
328     gboolean update_user_time;
329
330     stop_running = FALSE;
331
332     /* Don't allow saving the initial state when running things from the
333        menu */
334     if (uact == OB_USER_ACTION_MENU_SELECTION)
335         state = 0;
336     /* If x and y are < 0 then use the current pointer position */
337     if (x < 0 && y < 0)
338         screen_pointer_pos(&x, &y);
339
340     update_user_time = FALSE;
341     for (it = acts; it; it = g_slist_next(it)) {
342         ObActionsData data;
343         ObActionsAct *act = it->data;
344         gboolean ok = TRUE;
345
346         actions_setup_data(&data, uact, state, x, y, button, con, client);
347
348         /* if they have the same run function, then we'll assume they are
349            cooperating and not cancel eachother out */
350         if (!interactive_act || interactive_act->def->run != act->def->run) {
351             if (actions_act_is_interactive(act)) {
352                 /* cancel the old one */
353                 if (interactive_act)
354                     actions_interactive_cancel_act();
355                 if (act->i_pre)
356                     if (!act->i_pre(state, act->options))
357                         act->i_input = NULL; /* remove the interactivity */
358             }
359             /* check again cuz it might have been cancelled */
360             if (actions_act_is_interactive(act))
361                 ok = actions_interactive_begin_act(act, state);
362         }
363
364         /* fire the action's run function with this data */
365         if (ok) {
366             if (!act->def->run(&data, act->options)) {
367                 if (actions_act_is_interactive(act)) {
368                     actions_interactive_end_act();
369                 } else if (stop_running) {
370                     stop_running = FALSE;
371                     break;
372                 }
373                 if (client && client == focus_client &&
374                     act->def->modifies_focused_window)
375                 {
376                     update_user_time = TRUE;
377                 }
378             } else {
379                 /* make sure its interactive if it returned TRUE */
380                 g_assert(act->i_input);
381
382                 /* no actions are run after the interactive one */
383                 break;
384             }
385         }
386     }
387     if (update_user_time)
388         event_update_user_time();
389 }
390
391 gboolean actions_interactive_act_running(void)
392 {
393     return interactive_act != NULL;
394 }
395
396 void actions_interactive_cancel_act(void)
397 {
398     if (interactive_act) {
399         if (interactive_act->i_cancel)
400             interactive_act->i_cancel(interactive_act->options);
401         actions_interactive_end_act();
402     }
403 }
404
405 static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state)
406 {
407     if (grab_keyboard()) {
408         interactive_act = act;
409         actions_act_ref(interactive_act);
410
411         interactive_initial_state = state;
412
413         /* if using focus_delay, stop the timer now so that focus doesn't go
414            moving on us, which would kill the action */
415         event_halt_focus_delay();
416
417         return TRUE;
418     }
419     else
420         return FALSE;
421 }
422
423 static void actions_interactive_end_act(void)
424 {
425     if (interactive_act) {
426         ObActionsAct *ia = interactive_act;
427
428         /* set this to NULL first so the i_post() function can't cause this to
429            get called again (if it decides it wants to cancel any ongoing
430            interactive action). */
431         interactive_act = NULL;
432
433         ungrab_keyboard();
434
435         if (ia->i_post)
436             ia->i_post(ia->options);
437
438         actions_act_unref(ia);
439     }
440 }
441
442 gboolean actions_interactive_input_event(XEvent *e)
443 {
444     gboolean used = FALSE;
445     if (interactive_act) {
446         if (!interactive_act->i_input(interactive_initial_state, e,
447                                       grab_input_context(),
448                                       interactive_act->options, &used))
449         {
450             used = TRUE; /* if it cancelled the action then it has to of
451                             been used */
452             actions_interactive_end_act();
453         }
454     }
455     return used;
456 }
457
458 void actions_client_move(ObActionsData *data, gboolean start)
459 {
460     static gulong ignore_start = 0;
461     if (start)
462         ignore_start = event_start_ignore_all_enters();
463     else if (config_focus_follow &&
464              data->context != OB_FRAME_CONTEXT_CLIENT)
465     {
466         if (data->uact == OB_USER_ACTION_MOUSE_PRESS) {
467             struct _ObClient *c;
468
469             /* usually this is sorta redundant, but with a press action
470                that moves windows our from under the cursor, the enter
471                event will come as a GrabNotify which is ignored, so this
472                makes a fake enter event
473
474                don't do this if there is a grab on the pointer.  enter events
475                are ignored during a grab, so don't force fake ones when they
476                should be ignored
477             */
478             if (!grab_on_pointer()) {
479                 if ((c = client_under_pointer()) && c != data->client) {
480                     ob_debug_type(OB_DEBUG_FOCUS,
481                                   "Generating fake enter because we did a "
482                                   "mouse-event action");
483                     event_enter_client(c);
484                 }
485                 else if (!c && c != data->client) {
486                     ob_debug_type(OB_DEBUG_FOCUS,
487                                   "Generating fake leave because we did a "
488                                   "mouse-event action");
489                     event_leave_client(data->client);
490                 }
491             }
492         }
493         else if (!data->button && !config_focus_under_mouse)
494             event_end_ignore_all_enters(ignore_start);
495     }
496 }
497
498 gboolean actions_client_locked(ObActionsData *data)
499 {
500     ObClient *c = data->client;
501
502     return !c || (c && c->locked);
503 }