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);
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)
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;
208 g_message(_("Invalid action \"%s\" requested. No such action exists."),
214 ObAction* action_new(const gchar *name, GHashTable *config)
216 ObAction *act = NULL;
218 act = action_find_by_name(name);
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,
230 if (act->def->setup.n)
231 act->options = act->def->setup.n(config);
238 gboolean action_is_interactive(ObAction *act)
240 return act->i_input != NULL;
243 void action_ref(ObAction *act)
248 void action_unref(ObAction *act)
250 if (act && --act->ref == 0) {
251 /* free the action specific options */
253 act->def->free(act->options);
254 /* unref the definition */
255 action_definition_unref(act->def);
256 g_slice_free(ObAction, act);
260 gboolean action_run(ObAction *act, const ObActionListRun *data,
261 struct _ObClientSet *set)
263 gboolean ran_interactive;
264 gboolean update_user_time;
267 ran_interactive = FALSE;
268 update_user_time = 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 i_run = (current_i_act ?
292 current_i_act->def->run : NULL);
294 if (i_run && i_run != this_run)
295 action_interactive_cancel_act();
297 if (i_run != this_run && act->i_pre)
298 run_i = act->i_pre(data->mod_state, act->options);
303 run = action_interactive_begin_act(act, data->mod_state);
304 ran_interactive = TRUE;
310 end = !act->def->run(set, data, act->options);
311 g_assert(end || action_is_interactive(act));
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();
321 return ran_interactive;
324 gboolean action_interactive_act_running(void)
326 return current_i_act != NULL;
329 void action_interactive_cancel_act(void)
332 if (current_i_act->i_cancel)
333 current_i_act->i_cancel(current_i_act->options);
334 action_interactive_end_act();
338 static gboolean action_interactive_begin_act(ObAction *act, guint state)
340 if (grab_keyboard()) {
342 action_ref(current_i_act);
344 current_i_initial_state = obt_keyboard_only_modmasks(state);
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();
356 static void action_interactive_end_act(void)
359 ObAction *ia = current_i_act;
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;
369 ia->i_post(ia->options);
375 gboolean action_interactive_input_event(XEvent *e)
377 gboolean used = FALSE;
379 if (!current_i_act->i_input(current_i_initial_state, e,
380 grab_input_context(),
381 current_i_act->options, &used))
383 used = TRUE; /* if it cancelled the action then it has to of
385 action_interactive_end_act();
391 void action_client_move(const ObActionListRun *data, gboolean start)
393 static gulong ignore_start = 0;
395 ignore_start = event_start_ignore_all_enters();
396 else if (config_focus_follow &&
397 data->pointer_context != OB_FRAME_CONTEXT_CLIENT)
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
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
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);
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);
425 else if (!data->pointer_button && !config_focus_under_mouse)
426 event_end_ignore_all_enters(ignore_start);
430 ObActionDefaultFilter action_default_filter(ObAction *act)
432 return act->def->def_filter;