tag: Improve tag property::index support (FS#1229)
[awesome.git] / spawn.c
bloba2577214a77883b4142c2d402c8aab99aa0c0880
1 /*
2 * spawn.c - Lua configuration management
4 * Copyright © 2009 Julien Danjou <julien@danjou.info>
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 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "spawn.h"
24 #include <unistd.h>
25 #include <glib.h>
27 /** 20 seconds timeout */
28 #define AWESOME_SPAWN_TIMEOUT 20.0
30 /** Wrapper for unrefing startup sequence.
32 static inline void
33 a_sn_startup_sequence_unref(SnStartupSequence **sss)
35 return sn_startup_sequence_unref(*sss);
38 DO_ARRAY(SnStartupSequence *, SnStartupSequence, a_sn_startup_sequence_unref)
40 /** The array of startup sequence running */
41 SnStartupSequence_array_t sn_waits;
43 /** Remove a SnStartupSequence pointer from an array and forget about it.
44 * \param s The startup sequence to found, remove and unref.
45 * \return True if found and removed.
47 static inline bool
48 spawn_sequence_remove(SnStartupSequence *s)
50 for(int i = 0; i < sn_waits.len; i++)
51 if(sn_waits.tab[i] == s)
53 SnStartupSequence_array_take(&sn_waits, i);
54 sn_startup_sequence_unref(s);
55 return true;
57 return false;
60 static gboolean
61 spawn_monitor_timeout(gpointer sequence)
63 if(spawn_sequence_remove(sequence))
65 signal_t *sig = signal_array_getbyid(&global_signals,
66 a_strhash((const unsigned char *) "spawn::timeout"));
67 if(sig)
69 /* send a timeout signal */
70 lua_createtable(globalconf.L, 0, 2);
71 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(sequence));
72 lua_setfield(globalconf.L, -2, "id");
73 foreach(func, sig->sigfuncs)
75 lua_pushvalue(globalconf.L, -1);
76 luaA_object_push(globalconf.L, (void *) *func);
77 luaA_dofunction(globalconf.L, 1, 0);
79 lua_pop(globalconf.L, 1);
81 else
82 warn("spawn::timeout signal is missing");
84 sn_startup_sequence_unref(sequence);
85 return FALSE;
88 static void
89 spawn_monitor_event(SnMonitorEvent *event, void *data)
91 SnStartupSequence *sequence = sn_monitor_event_get_startup_sequence(event);
92 SnMonitorEventType event_type = sn_monitor_event_get_type(event);
94 lua_createtable(globalconf.L, 0, 2);
95 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(sequence));
96 lua_setfield(globalconf.L, -2, "id");
98 const char *event_type_str = NULL;
100 switch(event_type)
102 case SN_MONITOR_EVENT_INITIATED:
103 /* ref the sequence for the array */
104 sn_startup_sequence_ref(sequence);
105 SnStartupSequence_array_append(&sn_waits, sequence);
106 event_type_str = "spawn::initiated";
108 /* Add a timeout function so we do not wait for this event to complete
109 * for ever */
110 g_timeout_add_seconds(AWESOME_SPAWN_TIMEOUT, spawn_monitor_timeout, sequence);
111 /* ref the sequence for the callback event */
112 sn_startup_sequence_ref(sequence);
113 break;
114 case SN_MONITOR_EVENT_CHANGED:
115 event_type_str = "spawn::change";
116 break;
117 case SN_MONITOR_EVENT_COMPLETED:
118 event_type_str = "spawn::completed";
119 break;
120 case SN_MONITOR_EVENT_CANCELED:
121 event_type_str = "spawn::canceled";
122 break;
125 /* common actions */
126 switch(event_type)
128 case SN_MONITOR_EVENT_INITIATED:
129 case SN_MONITOR_EVENT_CHANGED:
131 const char *s = sn_startup_sequence_get_name(sequence);
132 if(s)
134 lua_pushstring(globalconf.L, s);
135 lua_setfield(globalconf.L, -2, "name");
138 if((s = sn_startup_sequence_get_description(sequence)))
140 lua_pushstring(globalconf.L, s);
141 lua_setfield(globalconf.L, -2, "description");
144 lua_pushnumber(globalconf.L, sn_startup_sequence_get_workspace(sequence));
145 lua_setfield(globalconf.L, -2, "workspace");
147 if((s = sn_startup_sequence_get_binary_name(sequence)))
149 lua_pushstring(globalconf.L, s);
150 lua_setfield(globalconf.L, -2, "binary_name");
153 if((s = sn_startup_sequence_get_icon_name(sequence)))
155 lua_pushstring(globalconf.L, s);
156 lua_setfield(globalconf.L, -2, "icon_name");
159 if((s = sn_startup_sequence_get_wmclass(sequence)))
161 lua_pushstring(globalconf.L, s);
162 lua_setfield(globalconf.L, -2, "wmclass");
165 break;
166 case SN_MONITOR_EVENT_COMPLETED:
167 case SN_MONITOR_EVENT_CANCELED:
168 spawn_sequence_remove(sequence);
169 break;
172 /* send the signal */
173 signal_t *sig = signal_array_getbyid(&global_signals,
174 a_strhash((const unsigned char *) event_type_str));
176 if(sig)
178 foreach(func, sig->sigfuncs)
180 lua_pushvalue(globalconf.L, -1);
181 luaA_object_push(globalconf.L, (void *) *func);
182 luaA_dofunction(globalconf.L, 1, 0);
184 lua_pop(globalconf.L, 1);
186 else
187 warn("%s signal is missing", event_type_str);
190 /** Tell the spawn module that an app has been started.
191 * \param c The client that just started.
192 * \param startup_id The startup id of the started application.
194 void
195 spawn_start_notify(client_t *c, const char * startup_id)
197 foreach(_seq, sn_waits)
199 SnStartupSequence *seq = *_seq;
200 bool found = false;
201 const char *seqid = sn_startup_sequence_get_id(seq);
203 if (A_STRNEQ(seqid, startup_id))
204 found = true;
205 else
207 const char *seqclass = sn_startup_sequence_get_wmclass(seq);
208 if (A_STREQ(seqclass, c->class) || A_STREQ(seqclass, c->instance))
209 found = true;
210 else
212 const char *seqbin = sn_startup_sequence_get_binary_name(seq);
213 if (A_STREQ_CASE(seqbin, c->class) || A_STREQ_CASE(seqbin, c->instance))
214 found = true;
218 if(found)
220 sn_startup_sequence_complete(seq);
221 break;
226 /** Initialize program spawner.
228 void
229 spawn_init(void)
231 globalconf.sndisplay = sn_xcb_display_new(globalconf.connection, NULL, NULL);
233 globalconf.snmonitor = sn_monitor_context_new(globalconf.sndisplay,
234 globalconf.default_screen,
235 spawn_monitor_event,
236 NULL, NULL);
238 signal_add(&global_signals, "spawn::canceled");
239 signal_add(&global_signals, "spawn::change");
240 signal_add(&global_signals, "spawn::completed");
241 signal_add(&global_signals, "spawn::initiated");
242 signal_add(&global_signals, "spawn::timeout");
245 static gboolean
246 spawn_launchee_timeout(gpointer context)
248 sn_launcher_context_complete(context);
249 sn_launcher_context_unref(context);
250 return FALSE;
253 static void
254 spawn_callback(gpointer user_data)
256 setsid();
259 /** Parse a command line.
260 * \param L The Lua VM state.
261 * \param idx The index of the argument that we should parse
262 * \return The argv array for the new process.
264 static gchar **
265 parse_command(lua_State *L, int idx)
267 gchar **argv = NULL;
268 idx = luaA_absindex(L, idx);
270 if (lua_isstring(L, idx))
272 const char *cmd = luaL_checkstring(L, idx);
273 if(!g_shell_parse_argv(cmd, NULL, &argv, NULL))
274 return NULL;
276 else if (lua_istable(L, idx))
278 size_t i, len = luaA_rawlen(L, idx);
280 /* First verify that the table is sane: All integer keys must contain
281 * strings. Do this by pushing them all onto the stack.
283 for (i = 0; i < len; i++)
285 lua_rawgeti(L, idx, i+1);
286 if (lua_type(L, -1) != LUA_TSTRING)
287 luaL_error(L, "Non-string argument at table index %d", i+1);
290 /* From this point on nothing can go wrong and so we can safely allocate
291 * memory.
293 argv = g_new0(gchar *, len + 1);
294 for (i = 0; i < len; i++)
296 argv[len - i - 1] = g_strdup(lua_tostring(L, -1));
297 lua_pop(L, 1);
300 else
302 luaL_error(L, "Invalid argument to spawn(), expect string or table");
305 return argv;
308 /** Spawn a program.
309 * This function is multi-head (Zaphod) aware and will set display to
310 * the right screen according to mouse position.
311 * \param L The Lua VM state.
312 * \return The number of elements pushed on stack
313 * \luastack
314 * \lparam The command to launch.
315 * \lparam Use startup-notification, true or false, default to true.
316 * \lreturn Process ID if everything is OK, or an error string if an error occured.
319 luaA_spawn(lua_State *L)
321 gchar **argv = NULL;
322 bool use_sn = true;
323 gboolean retval;
324 GPid pid;
326 if(lua_gettop(L) >= 2)
327 use_sn = luaA_checkboolean(L, 2);
329 argv = parse_command(L, 1);
330 if(!argv || !argv[0])
332 g_strfreev(argv);
333 return 0;
336 SnLauncherContext *context = NULL;
337 if(use_sn)
339 context = sn_launcher_context_new(globalconf.sndisplay, globalconf.default_screen);
340 sn_launcher_context_set_name(context, "awesome");
341 sn_launcher_context_set_description(context, "awesome spawn");
342 sn_launcher_context_set_binary_name(context, argv[0]);
343 sn_launcher_context_initiate(context, "awesome", argv[0], globalconf.timestamp);
345 /* app will have AWESOME_SPAWN_TIMEOUT seconds to complete,
346 * or the timeout function will terminate the launch sequence anyway */
347 g_timeout_add_seconds(AWESOME_SPAWN_TIMEOUT, spawn_launchee_timeout, context);
348 sn_launcher_context_setup_child_process(context);
351 GError *error = NULL;
352 retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
353 spawn_callback, NULL, &pid, &error);
354 g_strfreev(argv);
355 if(!retval)
357 /* push error on stack */
358 lua_pushstring(L, error->message);
359 g_error_free(error);
360 if(context)
361 sn_launcher_context_complete(context);
362 return 1;
365 /* push pid on stack */
366 lua_pushnumber(L, pid);
368 /* push sn on stack */
369 if (context)
370 lua_pushstring(L,sn_launcher_context_get_startup_id(context));
372 return (context)?2:1;
375 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80