]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
Rename the fields in ObActionListRun for clarity
[dana/openbox.git] / openbox / action.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    action.c for the Openbox window manager
4    Copyright (c) 2007-2011   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 "action.h"
20 #include "action_list.h"
21 #include "action_list_run.h"
22 #include "action_filter.h"
23 #include "gettext.h"
24 #include "grab.h"
25 #include "screen.h"
26 #include "event.h"
27 #include "config.h"
28 #include "client.h"
29 #include "client_set.h"
30 #include "focus.h"
31 #include "openbox.h"
32 #include "debug.h"
33
34 #include "actions/_all.h"
35
36 static void     action_definition_ref(ObActionDefinition *def);
37 static void     action_definition_unref(ObActionDefinition *def);
38 static gboolean action_interactive_begin_act(ObAction *act, guint state);
39 static void     action_interactive_end_act();
40 static ObAction* action_find_by_name(const gchar *name);
41
42 static ObAction *current_i_act = NULL;
43 static guint     current_i_initial_state = 0;
44
45 struct _ObActionDefinition {
46     guint ref;
47
48     gchar *name;
49
50     gboolean canbeinteractive;
51     ObActionDefaultFilter def_filter;
52     union {
53         ObActionIDataSetupFunc i;
54         ObActionDataSetupFunc n;
55     } setup;
56     ObActionDataFreeFunc free;
57     ObActionRunFunc run;
58     ObActionShutdownFunc shutdown;
59 };
60
61 struct _ObAction {
62     guint ref;
63
64     ObActionDefinition *def;
65     ObActionIPreFunc i_pre;
66     ObActionIInputFunc i_input;
67     ObActionICancelFunc i_cancel;
68     ObActionIPostFunc i_post;
69     gpointer options;
70 };
71
72 static GSList *registered = NULL;
73
74 void action_startup(gboolean reconfig)
75 {
76     if (reconfig) return;
77
78     actions__all_startup();
79 }
80
81 void action_shutdown(gboolean reconfig)
82 {
83     action_interactive_cancel_act();
84
85     if (reconfig) return;
86
87     /* free all the registered actions */
88     while (registered) {
89         ObActionDefinition *d = registered->data;
90         if (d->shutdown) d->shutdown();
91         action_definition_unref(d);
92         registered = g_slist_delete_link(registered, registered);
93     }
94 }
95
96 ObActionDefinition* do_register(const gchar *name,
97                                 ObActionDefaultFilter def_filter,
98                                 ObActionDataFreeFunc free,
99                                 ObActionRunFunc run)
100 {
101     GSList *it;
102     ObActionDefinition *def;
103
104     g_return_val_if_fail(def_filter < OB_NUM_ACTION_DEFAULT_FILTERS, NULL);
105     g_return_val_if_fail(run != NULL, NULL);
106
107     for (it = registered; it; it = g_slist_next(it)) {
108         def = it->data;
109         if (!g_ascii_strcasecmp(name, def->name)) /* already registered */
110             return NULL;
111     }
112
113     def = g_slice_new(ObActionDefinition);
114     def->ref = 1;
115     def->name = g_strdup(name);
116     def->def_filter = def_filter;
117     def->free = free;
118     def->run = run;
119     def->shutdown = NULL;
120
121     registered = g_slist_prepend(registered, def);
122     return def;
123 }
124
125 gboolean action_register_i(const gchar *name,
126                            ObActionDefaultFilter def_filter,
127                            ObActionIDataSetupFunc setup,
128                            ObActionDataFreeFunc free,
129                            ObActionRunFunc run)
130 {
131     ObActionDefinition *def = do_register(name, def_filter, free, run);
132     if (def) {
133         def->canbeinteractive = TRUE;
134         def->setup.i = setup;
135     }
136     return def != NULL;
137 }
138
139 gboolean action_register(const gchar *name,
140                          ObActionDefaultFilter def_filter,
141                          ObActionDataSetupFunc setup,
142                          ObActionDataFreeFunc free,
143                          ObActionRunFunc run)
144 {
145     ObActionDefinition *def = do_register(name, def_filter, free, run);
146     if (def) {
147         def->canbeinteractive = FALSE;
148         def->setup.n = setup;
149     }
150     return def != NULL;
151 }
152
153 gboolean action_set_shutdown(const gchar *name,
154                              ObActionShutdownFunc shutdown)
155 {
156     GSList *it;
157     ObActionDefinition *def;
158
159     for (it = registered; it; it = g_slist_next(it)) {
160         def = it->data;
161         if (!g_ascii_strcasecmp(name, def->name)) {
162             def->shutdown = shutdown;
163             return TRUE;
164         }
165     }
166     return FALSE;
167 }
168
169 static void action_definition_ref(ObActionDefinition *def)
170 {
171     ++def->ref;
172 }
173
174 static void action_definition_unref(ObActionDefinition *def)
175 {
176     if (def && --def->ref == 0) {
177         g_free(def->name);
178         g_slice_free(ObActionDefinition, def);
179     }
180 }
181
182 static ObAction* action_find_by_name(const gchar *name)
183 {
184     GSList *it;
185     ObActionDefinition *def = NULL;
186     ObAction *act = NULL;
187
188     /* find the requested action */
189     for (it = registered; it; it = g_slist_next(it)) {
190         def = it->data;
191         if (!g_ascii_strcasecmp(name, def->name))
192             break;
193         def = NULL;
194     }
195
196     /* if we found the action */
197     if (def) {
198         act = g_slice_new(ObAction);
199         act->ref = 1;
200         act->def = def;
201         action_definition_ref(act->def);
202         act->i_pre = NULL;
203         act->i_input = NULL;
204         act->i_cancel = NULL;
205         act->i_post = NULL;
206         act->options = NULL;
207     } else
208         g_message(_("Invalid action \"%s\" requested. No such action exists."),
209                   name);
210
211     return act;
212 }
213
214 ObAction* action_new(const gchar *name, GHashTable *config)
215 {
216     ObAction *act = NULL;
217
218     act = action_find_by_name(name);
219     if (act) {
220         /* there is more stuff to parse here */
221         if (act->def->canbeinteractive) {
222             if (act->def->setup.i)
223                 act->options = act->def->setup.i(config,
224                                                  &act->i_pre,
225                                                  &act->i_input,
226                                                  &act->i_cancel,
227                                                  &act->i_post);
228         }
229         else {
230             if (act->def->setup.n)
231                 act->options = act->def->setup.n(config);
232         }
233     }
234
235     return act;
236 }
237
238 gboolean action_is_interactive(ObAction *act)
239 {
240     return act->i_input != NULL;
241 }
242
243 void action_ref(ObAction *act)
244 {
245     ++act->ref;
246 }
247
248 void action_unref(ObAction *act)
249 {
250     if (act && --act->ref == 0) {
251         /* free the action specific options */
252         if (act->def->free)
253             act->def->free(act->options);
254         /* unref the definition */
255         action_definition_unref(act->def);
256         g_slice_free(ObAction, act);
257     }
258 }
259
260 gboolean action_run(ObAction *act, const ObActionListRun *data,
261                     struct _ObClientSet *set)
262 {
263     gboolean ran_interactive;
264     gboolean update_user_time;
265     gboolean run, run_i;
266
267     ran_interactive = FALSE;
268     update_user_time = FALSE;
269
270     /* If we're starting an interactive action:
271        - if the current interactive action is the same, do nothing and
272          just use the run function.
273        - otherwise...
274        - cancel the current interactive action (if any)
275        - run the pre function. if it returns false then the action will
276          not be treated as interactive.
277        - set up for a new interactive action with action_interactive_begin_act.
278          this may fail in which case we don't run the action at all.
279        Then execute the action's run function.
280        If the action is doing something to the currently focused window,
281          then we want to update its user_time to indicate it was used by a
282          human now.
283     */
284
285     run_i = FALSE;
286     if (action_is_interactive(act)) {
287         ObActionRunFunc this_run = act->def->run;
288         ObActionRunFunc i_run = (current_i_act ?
289                                  current_i_act->def->run : NULL);
290
291         if (i_run && i_run != this_run)
292             action_interactive_cancel_act();
293         run_i = TRUE;
294         if (i_run != this_run && act->i_pre)
295             run_i = act->i_pre(data->mod_state, act->options);
296     }
297
298     run = TRUE;
299     if (run_i) {
300         run = action_interactive_begin_act(act, data->mod_state);
301         ran_interactive = TRUE;
302     }
303
304     if (run) {
305         gboolean end;
306
307         /* XXX pass the set here */
308         end = !act->def->run(data, act->options);
309         g_assert(end || action_is_interactive(act));
310
311         if (end) {
312             if (action_is_interactive(act))
313                 action_interactive_end_act();
314             /* XXX else if (client_set_contains(focus_client)) */
315             else if (data->target && data->target == focus_client)
316                 event_update_user_time();
317         }
318     }
319
320     return ran_interactive;
321 }
322
323 gboolean action_interactive_act_running(void)
324 {
325     return current_i_act != NULL;
326 }
327
328 void action_interactive_cancel_act(void)
329 {
330     if (current_i_act) {
331         if (current_i_act->i_cancel)
332             current_i_act->i_cancel(current_i_act->options);
333         action_interactive_end_act();
334     }
335 }
336
337 static gboolean action_interactive_begin_act(ObAction *act, guint state)
338 {
339     if (grab_keyboard()) {
340         current_i_act = act;
341         action_ref(current_i_act);
342
343         current_i_initial_state = obt_keyboard_only_modmasks(state);
344
345         /* if using focus_delay, stop the timer now so that focus doesn't go
346            moving on us, which would kill the action */
347         event_halt_focus_delay();
348
349         return TRUE;
350     }
351     else
352         return FALSE;
353 }
354
355 static void action_interactive_end_act(void)
356 {
357     if (current_i_act) {
358         ObAction *ia = current_i_act;
359
360         /* set this to NULL first so the i_post() function can't cause this to
361            get called again (if it decides it wants to cancel any ongoing
362            interactive action). */
363         current_i_act = NULL;
364
365         ungrab_keyboard();
366
367         if (ia->i_post)
368             ia->i_post(ia->options);
369
370         action_unref(ia);
371     }
372 }
373
374 gboolean action_interactive_input_event(XEvent *e)
375 {
376     gboolean used = FALSE;
377     if (current_i_act) {
378         if (!current_i_act->i_input(current_i_initial_state, e,
379                                     grab_input_context(),
380                                     current_i_act->options, &used))
381         {
382             used = TRUE; /* if it cancelled the action then it has to of
383                             been used */
384             action_interactive_end_act();
385         }
386     }
387     return used;
388 }
389
390 void action_client_move(const ObActionListRun *data, gboolean start)
391 {
392     static gulong ignore_start = 0;
393     if (start)
394         ignore_start = event_start_ignore_all_enters();
395     else if (config_focus_follow &&
396              data->pointer_context != OB_FRAME_CONTEXT_CLIENT)
397     {
398         if (data->user_act == OB_USER_ACTION_MOUSE_PRESS) {
399             /* usually this is sorta redundant, but with a press action
400                that moves windows our from under the cursor, the enter
401                event will come as a GrabNotify which is ignored, so this
402                makes a fake enter event
403
404                don't do this if there is a grab on the pointer.  enter events
405                are ignored during a grab, so don't force fake ones when they
406                should be ignored
407             */
408             if (!grab_on_pointer()) {
409                 struct _ObClient *under = client_under_pointer();
410                 if (under && under != data->pointer_over) {
411                     ob_debug_type(OB_DEBUG_FOCUS,
412                                   "Generating fake enter because we did a "
413                                   "mouse-event action");
414                     event_enter_client(under);
415                 }
416                 else if (!under && under != data->pointer_over) {
417                     ob_debug_type(OB_DEBUG_FOCUS,
418                                   "Generating fake leave because we did a "
419                                   "mouse-event action");
420                     event_enter_client(data->target);
421                 }
422             }
423         }
424         else if (!data->pointer_button && !config_focus_under_mouse)
425             event_end_ignore_all_enters(ignore_start);
426     }
427 }
428
429 ObActionDefaultFilter action_default_filter(ObAction *act)
430 {
431     return act->def->def_filter;
432 }