]> icculus.org git repositories - dana/openbox.git/blob - openbox/actions/execute.c
Rename ObActionValue to ObConfigValue. This holds one of three types.
[dana/openbox.git] / openbox / actions / execute.c
1 #include "openbox/action.h"
2 #include "openbox/action_list_run.h"
3 #include "openbox/config_value.h"
4 #include "openbox/client.h"
5 #include "openbox/client_set.h"
6 #include "openbox/event.h"
7 #include "openbox/prompt.h"
8 #include "openbox/screen.h"
9 #include "openbox/startupnotify.h"
10 #include "obt/paths.h"
11 #include "gettext.h"
12
13 #ifdef HAVE_STDLIB_H
14 #  include <stdlib.h>
15 #endif
16
17 typedef struct {
18     gchar   *cmd;
19     gboolean sn;
20     gchar   *sn_name;
21     gchar   *sn_icon;
22     gchar   *sn_wmclass;
23     gchar   *prompt;
24
25     /* for the prompt callback */
26     ObActionListRun *data;
27     ObClientSet *set;
28 } Options;
29
30 static gpointer setup_func(GHashTable *config);
31 static void     free_func(gpointer options);
32 static gboolean run_func(const ObClientSet *set,
33                          const ObActionListRun *data, gpointer options);
34 static void shutdown_func(void);
35 static void client_dest(ObClient *client, gpointer data);
36
37 static GSList *prompt_opts = NULL;
38
39 void action_execute_startup(void)
40 {
41     action_register("Execute", OB_ACTION_DEFAULT_FILTER_SINGLE,
42                     setup_func, free_func, run_func);
43     action_set_shutdown("Execute", shutdown_func);
44
45     client_add_destroy_notify(client_dest, NULL);
46 }
47
48 static void client_dest(ObClient *client, gpointer data)
49 {
50     GSList *it;
51
52     for (it = prompt_opts; it; it = g_slist_next(it)) {
53         Options *o = it->data;
54         if (o->data->target == client)
55             o->data->target = NULL;
56     }
57 }
58
59 static gpointer setup_func(GHashTable *config)
60 {
61     ObConfigValue *v;
62     Options *o;
63
64     o = g_slice_new0(Options);
65
66     v = g_hash_table_lookup(config, "command");
67     if (v && config_value_is_string(v))
68         o->cmd = obt_paths_expand_tilde(config_value_string(v));
69
70     v = g_hash_table_lookup(config, "prompt");
71     if (v && config_value_is_string(v))
72         o->prompt = g_strdup(config_value_string(v));
73
74     v = g_hash_table_lookup(config, "startupnotify");
75     if (v && config_value_is_string(v) && config_value_bool(v)) {
76         o->sn = TRUE;
77         v = g_hash_table_lookup(config, "name");
78         if (v && config_value_is_string(v))
79             o->sn_name = g_strdup(config_value_string(v));
80         v = g_hash_table_lookup(config, "icon");
81         if (v && config_value_is_string(v))
82             o->sn_icon = g_strdup(config_value_string(v));
83         v = g_hash_table_lookup(config, "wmclass");
84         if (v && config_value_is_string(v))
85             o->sn_wmclass = g_strdup(config_value_string(v));
86     }
87     return o;
88 }
89
90 static void shutdown_func(void)
91 {
92     client_remove_destroy_notify(client_dest);
93 }
94
95 static void free_func(gpointer options)
96 {
97     Options *o = options;
98
99     if (o) {
100         prompt_opts = g_slist_remove(prompt_opts, o);
101
102         g_free(o->cmd);
103         g_free(o->sn_name);
104         g_free(o->sn_icon);
105         g_free(o->sn_wmclass);
106         g_free(o->prompt);
107         if (o->set) client_set_destroy(o->set);
108         if (o->data) g_slice_free(ObActionListRun, o->data);
109         g_slice_free(Options, o);
110     }
111 }
112
113 static Options* dup_options(Options *in, const ObClientSet *set,
114                             const ObActionListRun *data)
115 {
116     Options *o = g_slice_new(Options);
117     o->cmd = g_strdup(in->cmd);
118     o->sn = in->sn;
119     o->sn_name = g_strdup(in->sn_name);
120     o->sn_icon = g_strdup(in->sn_icon);
121     o->sn_wmclass = g_strdup(in->sn_wmclass);
122     o->prompt = NULL;
123     o->set = client_set_clone(set);
124     o->data = g_slice_new(ObActionListRun);
125     memcpy(o->data, data, sizeof(ObActionListRun));
126     return o;
127 }
128
129 static gboolean do_execute_each(ObClient *client,
130                                 const ObActionListRun *data, gpointer options)
131 {
132     Options *o = options;
133     GError *e;
134     gchar **argv = NULL;
135     gchar *cmd;
136
137     cmd = g_filename_from_utf8(o->cmd, -1, NULL, NULL, NULL);
138     if (!cmd) {
139         g_message(_("Failed to convert the path \"%s\" from utf8"), o->cmd);
140         return FALSE;
141     }
142
143     if (client) {
144         gchar *c, *before, *expand;
145
146         /* replace occurrences of $pid and $wid */
147
148         expand = NULL;
149         before = cmd;
150
151         while ((c = strchr(before, '$'))) {
152             if ((c[1] == 'p' || c[1] == 'P') &&
153                 (c[2] == 'i' || c[2] == 'I') &&
154                 (c[3] == 'd' || c[3] == 'D') &&
155                 !g_ascii_isalnum(c[4]))
156             {
157                 /* found $pid */
158                 gchar *tmp;
159
160                 *c = '\0';
161                 tmp = expand;
162                 expand = g_strdup_printf("%s%s%u",
163                                          (expand ? expand : ""),
164                                          before,
165                                          client->pid);
166                 g_free(tmp);
167
168                 before = c + 4; /* 4 = strlen("$pid") */
169             }
170             else if ((c[1] == 'w' || c[1] == 'W') &&
171                      (c[2] == 'i' || c[2] == 'I') &&
172                      (c[3] == 'd' || c[3] == 'D') &&
173                      !g_ascii_isalnum(c[4]))
174             {
175                 /* found $wid */
176                 gchar *tmp;
177
178                 *c = '\0';
179                 tmp = expand;
180                 expand = g_strdup_printf("%s%s%lu",
181                                          (expand ? expand : ""),
182                                          before,
183                                          client->window);
184                 g_free(tmp);
185
186                 before = c + 4; /* 4 = strlen("$wid") */
187             }
188             else
189                 before = c + 1; /* no infinite loops plz */
190         }
191
192         if (expand) {
193             gchar *tmp;
194
195             /* add on the end of the string after the last replacement */
196             tmp = expand;
197             expand = g_strconcat(expand, before, NULL);
198             g_free(tmp);
199
200             /* replace the command with the expanded one */
201             g_free(cmd);
202             cmd = expand;
203         }
204     }
205
206     /* If there is a keyboard grab going on then we need to cancel
207        it so the application can grab things */
208     if (data->user_act != OB_USER_ACTION_MENU_SELECTION)
209         event_cancel_all_key_grabs();
210
211     e = NULL;
212     if (!g_shell_parse_argv(cmd, NULL, &argv, &e)) {
213         g_message("%s", e->message);
214         g_error_free(e);
215     }
216     else {
217         gchar *program = NULL;
218         gboolean ok;
219
220         if (o->sn) {
221             program = g_path_get_basename(argv[0]);
222             /* sets up the environment */
223             sn_setup_spawn_environment(program, o->sn_name, o->sn_icon,
224                                        o->sn_wmclass,
225                                        /* launch it on the current desktop */
226                                        screen_desktop);
227         }
228
229         e = NULL;
230         ok = g_spawn_async(NULL, argv, NULL,
231                            G_SPAWN_SEARCH_PATH |
232                            G_SPAWN_DO_NOT_REAP_CHILD,
233                            NULL, NULL, NULL, &e);
234         if (!ok) {
235             g_message("%s", e->message);
236             g_error_free(e);
237         }
238
239         if (o->sn) {
240             if (!ok) sn_spawn_cancel();
241             g_unsetenv("DESKTOP_STARTUP_ID");
242         }
243
244         g_free(program);
245         g_strfreev(argv);
246     }
247
248     g_free(cmd);
249
250     return TRUE;
251 }
252
253 /* Always return FALSE because its not interactive */
254 static gboolean do_execute(const ObClientSet *set,
255                            const ObActionListRun *data, gpointer options)
256 {
257     if (client_set_is_empty(set))
258         do_execute_each(NULL, data, options);
259     else
260         client_set_run(set, data, do_execute_each, options);
261     return FALSE;
262 }
263
264 static gboolean prompt_cb(ObPrompt *p, gint result, gpointer options)
265 {
266     Options *o = options;
267     if (result)
268         run_func(o->set, o->data, o);
269     return TRUE; /* call the cleanup func */
270 }
271
272 static void prompt_cleanup(ObPrompt *p, gpointer options)
273 {
274     prompt_unref(p);
275     free_func(options);
276 }
277
278 /* Always return FALSE because its not interactive */
279 static gboolean run_func(const ObClientSet *set,
280                          const ObActionListRun *data, gpointer options)
281 {
282     Options *o = options;
283
284     if (!o->cmd) return FALSE;
285
286     if (o->prompt) {
287         ObPrompt *p;
288         Options *ocp;
289         ObPromptAnswer answers[] = {
290             { _("No"), 0 },
291             { _("Yes"), 1 }
292         };
293
294         ocp = dup_options(options, set, data);
295         p = prompt_new(o->prompt, _("Execute"), answers, 2, 0, 0,
296                        prompt_cb, prompt_cleanup, ocp);
297         prompt_show(p, NULL, FALSE);
298
299         return FALSE;
300     }
301
302     return do_execute(set, data, options);
303 }