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.
24 #include <sys/types.h>
27 #include <glib/gspawn.h>
34 /** 20 seconds timeout */
35 #define AWESOME_SPAWN_TIMEOUT 20.0
37 /** Wrapper for unrefing startup sequence.
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.
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
);
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_from_registry(globalconf
.L
, globalconf
.hooks
.startup_notification
, 1, 0);
82 sn_startup_sequence_unref(w
->data
);
87 spawn_monitor_event(SnMonitorEvent
*event
, void *data
)
89 if(globalconf
.hooks
.startup_notification
== LUA_REFNIL
)
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");
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
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
);
117 case SN_MONITOR_EVENT_CHANGED
:
118 lua_pushliteral(globalconf
.L
, "change");
119 lua_setfield(globalconf
.L
, -2, "type");
121 case SN_MONITOR_EVENT_COMPLETED
:
122 lua_pushliteral(globalconf
.L
, "completed");
123 lua_setfield(globalconf
.L
, -2, "type");
125 case SN_MONITOR_EVENT_CANCELED
:
126 lua_pushliteral(globalconf
.L
, "canceled");
127 lua_setfield(globalconf
.L
, -2, "type");
134 case SN_MONITOR_EVENT_INITIATED
:
135 case SN_MONITOR_EVENT_CHANGED
:
137 const char *s
= sn_startup_sequence_get_name(sequence
);
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");
172 case SN_MONITOR_EVENT_COMPLETED
:
173 case SN_MONITOR_EVENT_CANCELED
:
174 spawn_sequence_remove(sequence
);
178 luaA_dofunction_from_registry(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.
185 spawn_start_notify(client_t
*c
)
187 foreach(_seq
, sn_waits
)
189 SnStartupSequence
*seq
= *_seq
;
191 const char *seqid
= sn_startup_sequence_get_id(seq
);
193 if(!a_strcmp(seqid
, c
->startup_id
))
197 const char *seqclass
= sn_startup_sequence_get_wmclass(seq
);
198 if(!a_strcmp(seqclass
, c
->class) || !a_strcmp(seqclass
, c
->instance
))
202 const char *seqbin
= sn_startup_sequence_get_binary_name(seq
);
203 if(!a_strcasecmp(seqbin
, c
->class) || !a_strcasecmp(seqbin
, c
->instance
))
210 sn_startup_sequence_complete(seq
);
216 /** Initialize program spawner.
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
,
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
);
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
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];
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);
278 SnLauncherContext
*context
= NULL
;
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
);
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
);
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
);
315 sn_launcher_context_complete(context
);
322 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80