]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
Pass a client set to all actions, and make focus cycling use a client set.
[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          - However, we only do this for non-interactive actions, as we expect
284            them to do their "thing" on every window in the set.  Interactive
285            actions generally let you choose one from a set to do stuff to.
286     */
287
288     run_i = FALSE;
289     if (action_is_interactive(act)) {
290         ObActionRunFunc this_run = act->def->run;
291         ObActionRunFunc i_run = (current_i_act ?
292                                  current_i_act->def->run : NULL);
293
294         if (i_run && i_run != this_run)
295             action_interactive_cancel_act();
296         run_i = TRUE;
297         if (i_run != this_run && act->i_pre)
298             run_i = act->i_pre(data->mod_state, act->options);
299     }
300
301     run = TRUE;
302     if (run_i) {
303         run = action_interactive_begin_act(act, data->mod_state);
304         ran_interactive = TRUE;
305     }
306
307     if (run) {
308         gboolean end;
309
310         end = !act->def->run(set, data, act->options);
311         g_assert(end || action_is_interactive(act));
312
313         if (end) {
314             if (action_is_interactive(act))
315                 action_interactive_end_act();
316             else if (client_set_contains(set, focus_client))
317                 event_update_user_time();
318         }
319     }
320
321     return ran_interactive;
322 }
323
324 gboolean action_interactive_act_running(void)
325 {
326     return current_i_act != NULL;
327 }
328
329 void action_interactive_cancel_act(void)
330 {
331     if (current_i_act) {
332         if (current_i_act->i_cancel)
333             current_i_act->i_cancel(current_i_act->options);
334         action_interactive_end_act();
335     }
336 }
337
338 static gboolean action_interactive_begin_act(ObAction *act, guint state)
339 {
340     if (grab_keyboard()) {
341         current_i_act = act;
342         action_ref(current_i_act);
343
344         current_i_initial_state = obt_keyboard_only_modmasks(state);
345
346         /* if using focus_delay, stop the timer now so that focus doesn't go
347            moving on us, which would kill the action */
348         event_halt_focus_delay();
349
350         return TRUE;
351     }
352     else
353         return FALSE;
354 }
355
356 static void action_interactive_end_act(void)
357 {
358     if (current_i_act) {
359         ObAction *ia = current_i_act;
360
361         /* set this to NULL first so the i_post() function can't cause this to
362            get called again (if it decides it wants to cancel any ongoing
363            interactive action). */
364         current_i_act = NULL;
365
366         ungrab_keyboard();
367
368         if (ia->i_post)
369             ia->i_post(ia->options);
370
371         action_unref(ia);
372     }
373 }
374
375 gboolean action_interactive_input_event(XEvent *e)
376 {
377     gboolean used = FALSE;
378     if (current_i_act) {
379         if (!current_i_act->i_input(current_i_initial_state, e,
380                                     grab_input_context(),
381                                     current_i_act->options, &used))
382         {
383             used = TRUE; /* if it cancelled the action then it has to of
384                             been used */
385             action_interactive_end_act();
386         }
387     }
388     return used;
389 }
390
391 void action_client_move(const ObActionListRun *data, gboolean start)
392 {
393     static gulong ignore_start = 0;
394     if (start)
395         ignore_start = event_start_ignore_all_enters();
396     else if (config_focus_follow &&
397              data->pointer_context != OB_FRAME_CONTEXT_CLIENT)
398     {
399         if (data->user_act == OB_USER_ACTION_MOUSE_PRESS) {
400             /* usually this is sorta redundant, but with a press action
401                that moves windows our from under the cursor, the enter
402                event will come as a GrabNotify which is ignored, so this
403                makes a fake enter event
404
405                don't do this if there is a grab on the pointer.  enter events
406                are ignored during a grab, so don't force fake ones when they
407                should be ignored
408             */
409             if (!grab_on_pointer()) {
410                 struct _ObClient *under = client_under_pointer();
411                 if (under && under != data->pointer_over) {
412                     ob_debug_type(OB_DEBUG_FOCUS,
413                                   "Generating fake enter because we did a "
414                                   "mouse-event action");
415                     event_enter_client(under);
416                 }
417                 else if (!under && under != data->pointer_over) {
418                     ob_debug_type(OB_DEBUG_FOCUS,
419                                   "Generating fake leave because we did a "
420                                   "mouse-event action");
421                     event_leave_client(data->pointer_over);
422                 }
423             }
424         }
425         else if (!data->pointer_button && !config_focus_under_mouse)
426             event_end_ignore_all_enters(ignore_start);
427     }
428 }
429
430 ObActionDefaultFilter action_default_filter(ObAction *act)
431 {
432     return act->def->def_filter;
433 }