change codename
[awesome.git] / spawn.c
blobe3a31a4dee7e9e6e9e171a6b515bd30458685ff8
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/gspawn.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 array The startup sequence array.
52 * \param s The startup sequence to found, remove and unref.
53 * \return True if found and removed.
55 static inline bool
56 spawn_sequence_remove(SnStartupSequence *s)
58 for(int i = 0; i < sn_waits.len; i++)
59 if(sn_waits.tab[i] == s)
61 SnStartupSequence_array_take(&sn_waits, i);
62 sn_startup_sequence_unref(s);
63 return true;
65 return false;
68 static void
69 spawn_monitor_timeout(struct ev_loop *loop, ev_timer *w, int revents)
71 if(spawn_sequence_remove(w->data)
72 && globalconf.hooks.startup_notification != LUA_REFNIL)
74 /* send a timeout event to hook function */
75 lua_createtable(globalconf.L, 0, 2);
76 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(w->data));
77 lua_setfield(globalconf.L, -2, "id");
78 lua_pushliteral(globalconf.L, "timedout");
79 lua_setfield(globalconf.L, -2, "type");
80 luaA_dofunction(globalconf.L, globalconf.hooks.startup_notification, 1, 0);
82 sn_startup_sequence_unref(w->data);
83 p_delete(&w);
86 static void
87 spawn_monitor_event(SnMonitorEvent *event, void *data)
89 if(globalconf.hooks.startup_notification == LUA_REFNIL)
90 return;
92 SnStartupSequence *sequence = sn_monitor_event_get_startup_sequence(event);
93 SnMonitorEventType event_type = sn_monitor_event_get_type(event);
95 lua_createtable(globalconf.L, 0, 2);
96 lua_pushstring(globalconf.L, sn_startup_sequence_get_id(sequence));
97 lua_setfield(globalconf.L, -2, "id");
99 switch(event_type)
101 case SN_MONITOR_EVENT_INITIATED:
102 /* ref the sequence for the array */
103 sn_startup_sequence_ref(sequence);
104 SnStartupSequence_array_append(&sn_waits, sequence);
105 lua_pushliteral(globalconf.L, "initiated");
106 lua_setfield(globalconf.L, -2, "type");
108 /* Add a timeout function so we do not wait for this event to complete
109 * for ever */
110 struct ev_timer *ev_timeout = p_new(struct ev_timer, 1);
111 ev_timer_init(ev_timeout, spawn_monitor_timeout, AWESOME_SPAWN_TIMEOUT, 0.);
112 /* ref the sequence for the callback event */
113 sn_startup_sequence_ref(sequence);
114 ev_timeout->data = sequence;
115 ev_timer_start(globalconf.loop, ev_timeout);
116 break;
117 case SN_MONITOR_EVENT_CHANGED:
118 lua_pushliteral(globalconf.L, "change");
119 lua_setfield(globalconf.L, -2, "type");
120 break;
121 case SN_MONITOR_EVENT_COMPLETED:
122 lua_pushliteral(globalconf.L, "completed");
123 lua_setfield(globalconf.L, -2, "type");
124 break;
125 case SN_MONITOR_EVENT_CANCELED:
126 lua_pushliteral(globalconf.L, "canceled");
127 lua_setfield(globalconf.L, -2, "type");
128 break;
131 /* common actions */
132 switch(event_type)
134 case SN_MONITOR_EVENT_INITIATED:
135 case SN_MONITOR_EVENT_CHANGED:
137 const char *s = sn_startup_sequence_get_name(sequence);
138 if(s)
140 lua_pushstring(globalconf.L, s);
141 lua_setfield(globalconf.L, -2, "name");
144 if((s = sn_startup_sequence_get_description(sequence)))
146 lua_pushstring(globalconf.L, s);
147 lua_setfield(globalconf.L, -2, "description");
150 lua_pushnumber(globalconf.L, sn_startup_sequence_get_workspace(sequence));
151 lua_setfield(globalconf.L, -2, "workspace");
153 if((s = sn_startup_sequence_get_binary_name(sequence)))
155 lua_pushstring(globalconf.L, s);
156 lua_setfield(globalconf.L, -2, "binary_name");
159 if((s = sn_startup_sequence_get_icon_name(sequence)))
161 lua_pushstring(globalconf.L, s);
162 lua_setfield(globalconf.L, -2, "icon_name");
165 if((s = sn_startup_sequence_get_wmclass(sequence)))
167 lua_pushstring(globalconf.L, s);
168 lua_setfield(globalconf.L, -2, "wmclass");
171 break;
172 case SN_MONITOR_EVENT_COMPLETED:
173 case SN_MONITOR_EVENT_CANCELED:
174 spawn_sequence_remove(sequence);
175 break;
178 luaA_dofunction(globalconf.L, globalconf.hooks.startup_notification, 1, 0);
181 /** Tell the spawn module that an app has been started.
182 * \param c The client that just started.
184 void
185 spawn_start_notify(client_t *c)
187 foreach(_seq, sn_waits)
189 SnStartupSequence *seq = *_seq;
190 bool found = false;
191 const char *seqid = sn_startup_sequence_get_id(seq);
193 if(!a_strcmp(seqid, c->startup_id))
194 found = true;
195 else
197 const char *seqclass = sn_startup_sequence_get_wmclass(seq);
198 if(!a_strcmp(seqclass, c->class) || !a_strcmp(seqclass, c->instance))
199 found = true;
200 else
202 const char *seqbin = sn_startup_sequence_get_binary_name(seq);
203 if(!a_strcasecmp(seqbin, c->class) || !a_strcasecmp(seqbin, c->instance))
204 found = true;
208 if(found)
210 sn_startup_sequence_complete(seq);
211 break;
216 /** Initialize program spawner.
218 void
219 spawn_init(void)
221 globalconf.sndisplay = sn_xcb_display_new(globalconf.connection, NULL, NULL);
223 const int screen_max = xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
225 for(int screen = 0; screen < screen_max; screen++)
226 globalconf.screens.tab[screen].snmonitor = sn_monitor_context_new(globalconf.sndisplay,
227 screen,
228 spawn_monitor_event,
229 NULL, NULL);
232 static void
233 spawn_launchee_timeout(struct ev_loop *loop, ev_timer *w, int revents)
235 sn_launcher_context_complete(w->data);
236 sn_launcher_context_unref(w->data);
237 p_delete(&w);
240 /** Spawn a program.
241 * This function is multi-head (Zaphod) aware and will set display to
242 * the right screen according to mouse position.
243 * \param L The Lua VM state.
244 * \return The number of elements pushed on stack
245 * \luastack
246 * \lparam The command to launch.
247 * \lparam Use startup-notification, true or false, default to true.
248 * \lparam The optional screen number to spawn the command on.
249 * \lreturn Nothing if launch was successful, an error string otherwise.
252 luaA_spawn(lua_State *L)
254 char *host, newdisplay[128];
255 const char *cmd;
256 bool use_sn = true;
257 int screen = 0, screenp, displayp;
259 if(lua_gettop(L) >= 2)
260 use_sn = luaA_checkboolean(L, 2);
262 if(lua_gettop(L) == 3)
264 screen = luaL_checknumber(L, 3) - 1;
265 luaA_checkscreen(screen);
268 cmd = luaL_checkstring(L, 1);
270 if(!globalconf.xinerama_is_active)
272 xcb_parse_display(NULL, &host, &displayp, &screenp);
273 snprintf(newdisplay, sizeof(newdisplay), "%s:%d.%d", host, displayp, screen);
274 setenv("DISPLAY", newdisplay, 1);
275 p_delete(&host);
278 SnLauncherContext *context = NULL;
279 if(use_sn)
281 char *cmdname, *space;
282 const char *first_no_space_char = cmd;
283 /* Look for the first char which is not space */
284 while(*first_no_space_char && *first_no_space_char == ' ')
285 first_no_space_char++;
286 /* Look for space in the string to get the command name. */
287 if((space = strchr(first_no_space_char, ' ')))
288 cmdname = a_strndup(cmd, space - cmd);
289 else
290 cmdname = a_strdup(cmd);
292 context = sn_launcher_context_new(globalconf.sndisplay, screen_virttophys(screen));
293 sn_launcher_context_set_name(context, "awesome");
294 sn_launcher_context_set_description(context, "awesome spawn");
295 sn_launcher_context_set_binary_name(context, cmdname);
296 sn_launcher_context_initiate(context, "awesome", cmdname, XCB_CURRENT_TIME);
297 p_delete(&cmdname);
299 /* app will have AWESOME_SPAWN_TIMEOUT seconds to complete,
300 * or the timeout function will terminate the launch sequence anyway */
301 struct ev_timer *ev_timeout = p_new(struct ev_timer, 1);
302 ev_timer_init(ev_timeout, spawn_launchee_timeout, AWESOME_SPAWN_TIMEOUT, 0.);
303 ev_timeout->data = context;
304 ev_timer_start(globalconf.loop, ev_timeout);
305 sn_launcher_context_setup_child_process(context);
308 GError *error = NULL;
309 if(!g_spawn_command_line_async(cmd, &error))
311 /* push error on stack */
312 lua_pushstring(L, error->message);
313 g_error_free(error);
314 if(context)
315 sn_launcher_context_complete(context);
316 return 1;
319 return 0;
322 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80