]> icculus.org git repositories - dana/openbox.git/blob - openbox/action.c
Make warnings about parse problems in .desktop files "debug" messages. Most people...
[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, gchar **error);
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, gchar **error)
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     }
208     else if (error)
209         *error = g_strdup_printf(
210             _("Invalid action \"%s\" requested. No such action exists."),
211             name);
212
213     return act;
214 }
215
216 ObAction* action_new(const gchar *name, GHashTable *config, gchar **error)
217 {
218     ObAction *act = NULL;
219
220     act = action_find_by_name(name, error);
221     if (act) {
222         /* there is more stuff to parse here */
223         if (act->def->canbeinteractive) {
224             if (act->def->setup.i)
225                 act->options = act->def->setup.i(config,
226                                                  &act->i_pre,
227                                                  &act->i_input,
228                                                  &act->i_cancel,
229                                                  &act->i_post);
230         }
231         else {
232             if (act->def->setup.n)
233                 act->options = act->def->setup.n(config);
234         }
235     }
236
237     return act;
238 }
239
240 gboolean action_is_interactive(ObAction *act)
241 {
242     return act->i_input != NULL;
243 }
244
245 void action_ref(ObAction *act)
246 {
247     ++act->ref;
248 }
249
250 void action_unref(ObAction *act)
251 {
252     if (act && --act->ref == 0) {
253         /* free the action specific options */
254         if (act->def->free)
255             act->def->free(act->options);
256         /* unref the definition */
257         action_definition_unref(act->def);
258         g_slice_free(ObAction, act);
259     }
260 }
261
262 gboolean action_run(ObAction *act, const ObActionListRun *data,
263                     struct _ObClientSet *set)
264 {
265     gboolean ran_interactive;
266     gboolean run, run_i;
267
268     ran_interactive = 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 current_i_run = (current_i_act ?
292                                          current_i_act->def->run : NULL);
293
294         if (current_i_run != this_run) {
295             if (current_i_run)
296                 action_interactive_cancel_act();
297             run_i = TRUE;
298             if (act->i_pre)
299                 run_i = act->i_pre(data->mod_state, act->options);
300         }
301     }
302
303     run = TRUE;
304     if (run_i) {
305         run = action_interactive_begin_act(act, data->mod_state);
306         ran_interactive = TRUE;
307     }
308
309     if (run) {
310         gboolean end;
311
312         end = !act->def->run(set, data, act->options);
313         g_assert(end || action_is_interactive(act));
314
315         if (end) {
316             if (action_is_interactive(act))
317                 action_interactive_end_act();
318             else if (client_set_contains(set, focus_client))
319                 event_update_user_time();
320         }
321     }
322
323     return ran_interactive;
324 }
325
326 gboolean action_interactive_act_running(void)
327 {
328     return current_i_act != NULL;
329 }
330
331 void action_interactive_cancel_act(void)
332 {
333     if (current_i_act) {
334         if (current_i_act->i_cancel)
335             current_i_act->i_cancel(current_i_act->options);
336         action_interactive_end_act();
337     }
338 }
339
340 static gboolean action_interactive_begin_act(ObAction *act, guint state)
341 {
342     if (grab_keyboard()) {
343         current_i_act = act;
344         action_ref(current_i_act);
345
346         interactive_initial_state = state;
347
348         /* if using focus_delay, stop the timer now so that focus doesn't go
349            moving on us, which would kill the action */
350         event_halt_focus_delay();
351
352         return TRUE;
353     }
354     else
355         return FALSE;
356 }
357
358 static void action_interactive_end_act(void)
359 {
360     if (current_i_act) {
361         ObAction *ia = current_i_act;
362
363         /* set this to NULL first so the i_post() function can't cause this to
364            get called again (if it decides it wants to cancel any ongoing
365            interactive action). */
366         current_i_act = NULL;
367
368         ungrab_keyboard();
369
370         if (ia->i_post)
371             ia->i_post(ia->options);
372
373         action_unref(ia);
374     }
375 }
376
377 gboolean action_interactive_input_event(XEvent *e)
378 {
379     gboolean used = FALSE;
380     if (current_i_act) {
381         if (!current_i_act->i_input(current_i_initial_state, e,
382                                     grab_input_context(),
383                                     current_i_act->options, &used))
384         {
385             used = TRUE; /* if it cancelled the action then it has to of
386                             been used */
387             action_interactive_end_act();
388         }
389     }
390     return used;
391 }
392
393 void action_client_move(const ObActionListRun *data, gboolean start)
394 {
395     static gulong ignore_start = 0;
396     if (start)
397         ignore_start = event_start_ignore_all_enters();
398     else if (config_focus_follow &&
399              data->pointer_context != OB_FRAME_CONTEXT_CLIENT)
400     {
401         if (data->user_act == OB_USER_ACTION_MOUSE_PRESS) {
402             /* usually this is sorta redundant, but with a press action
403                that moves windows our from under the cursor, the enter
404                event will come as a GrabNotify which is ignored, so this
405                makes a fake enter event
406
407                don't do this if there is a grab on the pointer.  enter events
408                are ignored during a grab, so don't force fake ones when they
409                should be ignored
410             */
411             if (!grab_on_pointer()) {
412                 struct _ObClient *under = client_under_pointer();
413                 if (under && under != data->pointer_over) {
414                     ob_debug_type(OB_DEBUG_FOCUS,
415                                   "Generating fake enter because we did a "
416                                   "mouse-event action");
417                     event_enter_client(under);
418                 }
419                 else if (!under && under != data->pointer_over) {
420                     ob_debug_type(OB_DEBUG_FOCUS,
421                                   "Generating fake leave because we did a "
422                                   "mouse-event action");
423                     event_leave_client(data->pointer_over);
424                 }
425             }
426         }
427         else if (!data->pointer_button && !config_focus_under_mouse)
428             event_end_ignore_all_enters(ignore_start);
429     }
430 }
431
432 ObActionDefaultFilter action_default_filter(ObAction *act)
433 {
434     return act->def->def_filter;
435 }