1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 action.c for the Openbox window manager
4 Copyright (c) 2007-2011 Dana Jansens
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.
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.
16 See the COPYING file for a copy of the GNU General Public License.
20 #include "action_list.h"
21 #include "action_list_run.h"
22 #include "action_filter.h"
29 #include "client_set.h"
34 #include "actions/_all.h"
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);
42 static ObAction *current_i_act = NULL;
43 static guint current_i_initial_state = 0;
45 struct _ObActionDefinition {
50 gboolean canbeinteractive;
51 ObActionDefaultFilter def_filter;
53 ObActionIDataSetupFunc i;
54 ObActionDataSetupFunc n;
56 ObActionDataFreeFunc free;
58 ObActionShutdownFunc shutdown;
64 ObActionDefinition *def;
65 ObActionIPreFunc i_pre;
66 ObActionIInputFunc i_input;
67 ObActionICancelFunc i_cancel;
68 ObActionIPostFunc i_post;
72 static GSList *registered = NULL;
74 void action_startup(gboolean reconfig)
78 actions__all_startup();
81 void action_shutdown(gboolean reconfig)
83 action_interactive_cancel_act();
87 /* free all the registered actions */
89 ObActionDefinition *d = registered->data;
90 if (d->shutdown) d->shutdown();
91 action_definition_unref(d);
92 registered = g_slist_delete_link(registered, registered);
96 ObActionDefinition* do_register(const gchar *name,
97 ObActionDefaultFilter def_filter,
98 ObActionDataFreeFunc free,
102 ObActionDefinition *def;
104 g_return_val_if_fail(def_filter < OB_NUM_ACTION_DEFAULT_FILTERS, NULL);
105 g_return_val_if_fail(run != NULL, NULL);
107 for (it = registered; it; it = g_slist_next(it)) {
109 if (!g_ascii_strcasecmp(name, def->name)) /* already registered */
113 def = g_slice_new(ObActionDefinition);
115 def->name = g_strdup(name);
116 def->def_filter = def_filter;
119 def->shutdown = NULL;
121 registered = g_slist_prepend(registered, def);
125 gboolean action_register_i(const gchar *name,
126 ObActionDefaultFilter def_filter,
127 ObActionIDataSetupFunc setup,
128 ObActionDataFreeFunc free,
131 ObActionDefinition *def = do_register(name, def_filter, free, run);
133 def->canbeinteractive = TRUE;
134 def->setup.i = setup;
139 gboolean action_register(const gchar *name,
140 ObActionDefaultFilter def_filter,
141 ObActionDataSetupFunc setup,
142 ObActionDataFreeFunc free,
145 ObActionDefinition *def = do_register(name, def_filter, free, run);
147 def->canbeinteractive = FALSE;
148 def->setup.n = setup;
153 gboolean action_set_shutdown(const gchar *name,
154 ObActionShutdownFunc shutdown)
157 ObActionDefinition *def;
159 for (it = registered; it; it = g_slist_next(it)) {
161 if (!g_ascii_strcasecmp(name, def->name)) {
162 def->shutdown = shutdown;
169 static void action_definition_ref(ObActionDefinition *def)
174 static void action_definition_unref(ObActionDefinition *def)
176 if (def && --def->ref == 0) {
178 g_slice_free(ObActionDefinition, def);
182 static ObAction* action_find_by_name(const gchar *name, gchar **error)
185 ObActionDefinition *def = NULL;
186 ObAction *act = NULL;
188 /* find the requested action */
189 for (it = registered; it; it = g_slist_next(it)) {
191 if (!g_ascii_strcasecmp(name, def->name))
196 /* if we found the action */
198 act = g_slice_new(ObAction);
201 action_definition_ref(act->def);
204 act->i_cancel = NULL;
209 *error = g_strdup_printf(
210 _("Invalid action \"%s\" requested. No such action exists."),
216 ObAction* action_new(const gchar *name, GHashTable *config, gchar **error)
218 ObAction *act = NULL;
220 act = action_find_by_name(name, error);
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,
232 if (act->def->setup.n)
233 act->options = act->def->setup.n(config);
240 gboolean action_is_interactive(ObAction *act)
242 return act->i_input != NULL;
245 void action_ref(ObAction *act)
250 void action_unref(ObAction *act)
252 if (act && --act->ref == 0) {
253 /* free the action specific options */
255 act->def->free(act->options);
256 /* unref the definition */
257 action_definition_unref(act->def);
258 g_slice_free(ObAction, act);
262 gboolean action_run(ObAction *act, const ObActionListRun *data,
263 struct _ObClientSet *set)
265 gboolean ran_interactive;
268 ran_interactive = FALSE;
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.
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
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.
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);
294 if (current_i_run != this_run) {
296 action_interactive_cancel_act();
299 run_i = act->i_pre(data->mod_state, act->options);
305 run = action_interactive_begin_act(act, data->mod_state);
306 ran_interactive = TRUE;
312 end = !act->def->run(set, data, act->options);
313 g_assert(end || action_is_interactive(act));
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();
323 return ran_interactive;
326 gboolean action_interactive_act_running(void)
328 return current_i_act != NULL;
331 void action_interactive_cancel_act(void)
334 if (current_i_act->i_cancel)
335 current_i_act->i_cancel(current_i_act->options);
336 action_interactive_end_act();
340 static gboolean action_interactive_begin_act(ObAction *act, guint state)
342 if (grab_keyboard()) {
344 action_ref(current_i_act);
346 interactive_initial_state = state;
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();
358 static void action_interactive_end_act(void)
361 ObAction *ia = current_i_act;
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;
371 ia->i_post(ia->options);
377 gboolean action_interactive_input_event(XEvent *e)
379 gboolean used = FALSE;
381 if (!current_i_act->i_input(current_i_initial_state, e,
382 grab_input_context(),
383 current_i_act->options, &used))
385 used = TRUE; /* if it cancelled the action then it has to of
387 action_interactive_end_act();
393 void action_client_move(const ObActionListRun *data, gboolean start)
395 static gulong ignore_start = 0;
397 ignore_start = event_start_ignore_all_enters();
398 else if (config_focus_follow &&
399 data->pointer_context != OB_FRAME_CONTEXT_CLIENT)
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
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
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);
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);
427 else if (!data->pointer_button && !config_focus_under_mouse)
428 event_end_ignore_all_enters(ignore_start);
432 ObActionDefaultFilter action_default_filter(ObAction *act)
434 return act->def->def_filter;