Add client.maximized (a shortcut for ~_horizontal and ~_vertical)
[awesome.git] / spawn.c
blob63b28752140ef5ad14c9b3550f348fef3ecb9c67
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 <unistd.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
27 #include <glib.h>
29 #include "spawn.h"
30 #include "screen.h"
31 #include "luaa.h"
32 #include "event.h"
34 /** 20 seconds timeout */
35 #define AWESOME_SPAWN_TIMEOUT 20.0
37 /** Wrapper for unrefing startup sequence.
39 static inline void
40 a_sn_startup_sequence_unref(SnStartupSequence **sss)
42 return sn_startup_sequence_unref(*sss);
45 DO_ARRAY(SnStartupSequence *, SnStartupSequence, a_sn_startup_sequence_unref)
47 /** The array of startup sequence running */
48 SnStartupSequence_array_t sn_waits;
50 /** Remove a SnStartupSequence pointer from an array and forget about it.
51 * \param s The startup sequence to found, remove and unref.
52 * \return True if found and removed.
54 static inline bool
55 spawn_sequence_remove(SnStartupSequence *s)
57 for(int i = 0; i < sn_waits.len; i++)
58 if(sn_waits.tab[i] == s)
60 SnStartupSequence_array_take(&sn_waits, i);
61 sn_startup_sequence_unref(s);
62 return true;
64 return false;
67 static gboolean
68 spawn_monitor_timeout(gpointer sequence)
70 if(spawn_sequence_remove(sequence))
72 signal_t *sig = signal_array_getbyid(&global_signals,
73 a_strhash((const unsigned char *) "spawn::timeout"));
74 if(sig)
76 /* send a timeout signal */
77 lua_createtable(globalconf.L, 0, 2);
78 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(sequence));
79 lua_setfield(globalconf.L, -2, "id");
80 foreach(func, sig->sigfuncs)
82 lua_pushvalue(globalconf.L, -1);
83 luaA_object_push(globalconf.L, (void *) *func);
84 luaA_dofunction(globalconf.L, 1, 0);
86 lua_pop(globalconf.L, 1);
88 else
89 warn("spawn::timeout signal is missing");
91 sn_startup_sequence_unref(sequence);
92 return FALSE;
95 static void
96 spawn_monitor_event(SnMonitorEvent *event, void *data)
98 SnStartupSequence *sequence = sn_monitor_event_get_startup_sequence(event);
99 SnMonitorEventType event_type = sn_monitor_event_get_type(event);
101 lua_createtable(globalconf.L, 0, 2);
102 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(sequence));
103 lua_setfield(globalconf.L, -2, "id");
105 const char *event_type_str = NULL;
107 switch(event_type)
109 case SN_MONITOR_EVENT_INITIATED:
110 /* ref the sequence for the array */
111 sn_startup_sequence_ref(sequence);
112 SnStartupSequence_array_append(&sn_waits, sequence);
113 event_type_str = "spawn::initiated";
115 /* Add a timeout function so we do not wait for this event to complete
116 * for ever */
117 g_timeout_add_seconds(AWESOME_SPAWN_TIMEOUT, spawn_monitor_timeout, sequence);
118 /* ref the sequence for the callback event */
119 sn_startup_sequence_ref(sequence);
120 break;
121 case SN_MONITOR_EVENT_CHANGED:
122 event_type_str = "spawn::change";
123 break;
124 case SN_MONITOR_EVENT_COMPLETED:
125 event_type_str = "spawn::completed";
126 break;
127 case SN_MONITOR_EVENT_CANCELED:
128 event_type_str = "spawn::canceled";
129 break;
132 /* common actions */
133 switch(event_type)
135 case SN_MONITOR_EVENT_INITIATED:
136 case SN_MONITOR_EVENT_CHANGED:
138 const char *s = sn_startup_sequence_get_name(sequence);
139 if(s)
141 lua_pushstring(globalconf.L, s);
142 lua_setfield(globalconf.L, -2, "name");
145 if((s = sn_startup_sequence_get_description(sequence)))
147 lua_pushstring(globalconf.L, s);
148 lua_setfield(globalconf.L, -2, "description");
151 lua_pushnumber(globalconf.L, sn_startup_sequence_get_workspace(sequence));
152 lua_setfield(globalconf.L, -2, "workspace");
154 if((s = sn_startup_sequence_get_binary_name(sequence)))
156 lua_pushstring(globalconf.L, s);
157 lua_setfield(globalconf.L, -2, "binary_name");
160 if((s = sn_startup_sequence_get_icon_name(sequence)))
162 lua_pushstring(globalconf.L, s);
163 lua_setfield(globalconf.L, -2, "icon_name");
166 if((s = sn_startup_sequence_get_wmclass(sequence)))
168 lua_pushstring(globalconf.L, s);
169 lua_setfield(globalconf.L, -2, "wmclass");
172 break;
173 case SN_MONITOR_EVENT_COMPLETED:
174 case SN_MONITOR_EVENT_CANCELED:
175 spawn_sequence_remove(sequence);
176 break;
179 /* send the signal */
180 signal_t *sig = signal_array_getbyid(&global_signals,
181 a_strhash((const unsigned char *) event_type_str));
183 if(sig)
185 foreach(func, sig->sigfuncs)
187 lua_pushvalue(globalconf.L, -1);
188 luaA_object_push(globalconf.L, (void *) *func);
189 luaA_dofunction(globalconf.L, 1, 0);
191 lua_pop(globalconf.L, 1);
193 else
194 warn("%s signal is missing", event_type_str);
197 /** Tell the spawn module that an app has been started.
198 * \param c The client that just started.
199 * \param startup_id The startup id of the started application.
201 void
202 spawn_start_notify(client_t *c, const char * startup_id)
204 foreach(_seq, sn_waits)
206 SnStartupSequence *seq = *_seq;
207 bool found = false;
208 const char *seqid = sn_startup_sequence_get_id(seq);
210 if (A_STRNEQ(seqid, startup_id))
211 found = true;
212 else
214 const char *seqclass = sn_startup_sequence_get_wmclass(seq);
215 if (A_STREQ(seqclass, c->class) || A_STREQ(seqclass, c->instance))
216 found = true;
217 else
219 const char *seqbin = sn_startup_sequence_get_binary_name(seq);
220 if (A_STREQ_CASE(seqbin, c->class) || A_STREQ_CASE(seqbin, c->instance))
221 found = true;
225 if(found)
227 sn_startup_sequence_complete(seq);
228 break;
233 /** Initialize program spawner.
235 void
236 spawn_init(void)
238 globalconf.sndisplay = sn_xcb_display_new(globalconf.connection, NULL, NULL);
240 globalconf.snmonitor = sn_monitor_context_new(globalconf.sndisplay,
241 globalconf.default_screen,
242 spawn_monitor_event,
243 NULL, NULL);
245 signal_add(&global_signals, "spawn::canceled");
246 signal_add(&global_signals, "spawn::change");
247 signal_add(&global_signals, "spawn::completed");
248 signal_add(&global_signals, "spawn::initiated");
249 signal_add(&global_signals, "spawn::timeout");
252 static gboolean
253 spawn_launchee_timeout(gpointer context)
255 sn_launcher_context_complete(context);
256 sn_launcher_context_unref(context);
257 return FALSE;
260 static void
261 spawn_callback(gpointer user_data)
263 setsid();
266 /** Parse a command line.
267 * \param L The Lua VM state.
268 * \param idx The index of the argument that we should parse
269 * \return The argv array for the new process.
271 static gchar **
272 parse_command(lua_State *L, int idx)
274 gchar **argv = NULL;
275 idx = luaA_absindex(L, idx);
277 if (lua_isstring(L, idx))
279 const char *cmd = luaL_checkstring(L, idx);
280 if(!g_shell_parse_argv(cmd, NULL, &argv, NULL))
281 return NULL;
283 else if (lua_istable(L, idx))
285 size_t i, len = luaA_rawlen(L, idx);
287 /* First verify that the table is sane: All integer keys must contain
288 * strings. Do this by pushing them all onto the stack.
290 for (i = 0; i < len; i++)
292 lua_rawgeti(L, idx, i+1);
293 if (lua_type(L, -1) != LUA_TSTRING)
294 luaL_error(L, "Non-string argument at table index %d", i+1);
297 /* From this point on nothing can go wrong and so we can safely allocate
298 * memory.
300 argv = g_new0(gchar *, len + 1);
301 for (i = 0; i < len; i++)
303 argv[len - i - 1] = g_strdup(lua_tostring(L, -1));
304 lua_pop(L, 1);
307 else
309 luaL_error(L, "Invalid argument to spawn(), expect string or table");
312 return argv;
315 /** Spawn a program.
316 * This function is multi-head (Zaphod) aware and will set display to
317 * the right screen according to mouse position.
318 * \param L The Lua VM state.
319 * \return The number of elements pushed on stack
320 * \luastack
321 * \lparam The command to launch.
322 * \lparam Use startup-notification, true or false, default to true.
323 * \lreturn Process ID if everything is OK, or an error string if an error occured.
326 luaA_spawn(lua_State *L)
328 gchar **argv = NULL;
329 bool use_sn = true;
330 gboolean retval;
331 GPid pid;
333 if(lua_gettop(L) >= 2)
334 use_sn = luaA_checkboolean(L, 2);
336 argv = parse_command(L, 1);
337 if(!argv || !argv[0])
339 g_strfreev(argv);
340 return 0;
343 SnLauncherContext *context = NULL;
344 if(use_sn)
346 context = sn_launcher_context_new(globalconf.sndisplay, globalconf.default_screen);
347 sn_launcher_context_set_name(context, "awesome");
348 sn_launcher_context_set_description(context, "awesome spawn");
349 sn_launcher_context_set_binary_name(context, argv[0]);
350 sn_launcher_context_initiate(context, "awesome", argv[0], globalconf.timestamp);
352 /* app will have AWESOME_SPAWN_TIMEOUT seconds to complete,
353 * or the timeout function will terminate the launch sequence anyway */
354 g_timeout_add_seconds(AWESOME_SPAWN_TIMEOUT, spawn_launchee_timeout, context);
355 sn_launcher_context_setup_child_process(context);
358 GError *error = NULL;
359 retval = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
360 spawn_callback, NULL, &pid, &error);
361 g_strfreev(argv);
362 if(!retval)
364 /* push error on stack */
365 lua_pushstring(L, error->message);
366 g_error_free(error);
367 if(context)
368 sn_launcher_context_complete(context);
369 return 1;
372 /* push pid on stack */
373 lua_pushnumber(L, pid);
375 return 1;
378 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80