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>
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 s The startup sequence to found, remove and unref.
52 * \return True if found and removed.
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
);
68 spawn_monitor_timeout(struct ev_loop
*loop
, ev_timer
*w
, int revents
)
70 if(spawn_sequence_remove(w
->data
))
72 signal_t
*sig
= signal_array_getbyid(&global_signals
,
73 a_strhash((const unsigned char *) "spawn::timeout"));
76 /* send a timeout signal */
77 lua_createtable(globalconf
.L
, 0, 2);
78 lua_pushstring(globalconf
.L
, sn_startup_sequence_get_id(w
->data
));
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);
89 warn("spawn::timeout signal is missing");
91 sn_startup_sequence_unref(w
->data
);
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
;
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
117 struct ev_timer
*ev_timeout
= p_new(struct ev_timer
, 1);
118 ev_timer_init(ev_timeout
, spawn_monitor_timeout
, AWESOME_SPAWN_TIMEOUT
, 0.);
119 /* ref the sequence for the callback event */
120 sn_startup_sequence_ref(sequence
);
121 ev_timeout
->data
= sequence
;
122 ev_timer_start(globalconf
.loop
, ev_timeout
);
124 case SN_MONITOR_EVENT_CHANGED
:
125 event_type_str
= "spawn::change";
127 case SN_MONITOR_EVENT_COMPLETED
:
128 event_type_str
= "spawn::completed";
130 case SN_MONITOR_EVENT_CANCELED
:
131 event_type_str
= "spawn::canceled";
138 case SN_MONITOR_EVENT_INITIATED
:
139 case SN_MONITOR_EVENT_CHANGED
:
141 const char *s
= sn_startup_sequence_get_name(sequence
);
144 lua_pushstring(globalconf
.L
, s
);
145 lua_setfield(globalconf
.L
, -2, "name");
148 if((s
= sn_startup_sequence_get_description(sequence
)))
150 lua_pushstring(globalconf
.L
, s
);
151 lua_setfield(globalconf
.L
, -2, "description");
154 lua_pushnumber(globalconf
.L
, sn_startup_sequence_get_workspace(sequence
));
155 lua_setfield(globalconf
.L
, -2, "workspace");
157 if((s
= sn_startup_sequence_get_binary_name(sequence
)))
159 lua_pushstring(globalconf
.L
, s
);
160 lua_setfield(globalconf
.L
, -2, "binary_name");
163 if((s
= sn_startup_sequence_get_icon_name(sequence
)))
165 lua_pushstring(globalconf
.L
, s
);
166 lua_setfield(globalconf
.L
, -2, "icon_name");
169 if((s
= sn_startup_sequence_get_wmclass(sequence
)))
171 lua_pushstring(globalconf
.L
, s
);
172 lua_setfield(globalconf
.L
, -2, "wmclass");
176 case SN_MONITOR_EVENT_COMPLETED
:
177 case SN_MONITOR_EVENT_CANCELED
:
178 spawn_sequence_remove(sequence
);
182 /* send the signal */
183 signal_t
*sig
= signal_array_getbyid(&global_signals
,
184 a_strhash((const unsigned char *) event_type_str
));
188 foreach(func
, sig
->sigfuncs
)
190 lua_pushvalue(globalconf
.L
, -1);
191 luaA_object_push(globalconf
.L
, (void *) *func
);
192 luaA_dofunction(globalconf
.L
, 1, 0);
194 lua_pop(globalconf
.L
, 1);
197 warn("%s signal is missing", event_type_str
);
200 /** Tell the spawn module that an app has been started.
201 * \param c The client that just started.
202 * \param startup_id The startup id of the started application.
205 spawn_start_notify(client_t
*c
, const char * startup_id
)
207 foreach(_seq
, sn_waits
)
209 SnStartupSequence
*seq
= *_seq
;
211 const char *seqid
= sn_startup_sequence_get_id(seq
);
213 if (A_STRNEQ(seqid
, startup_id
))
217 const char *seqclass
= sn_startup_sequence_get_wmclass(seq
);
218 if (A_STREQ(seqclass
, c
->class) || A_STREQ(seqclass
, c
->instance
))
222 const char *seqbin
= sn_startup_sequence_get_binary_name(seq
);
223 if (A_STREQ_CASE(seqbin
, c
->class) || A_STREQ_CASE(seqbin
, c
->instance
))
230 sn_startup_sequence_complete(seq
);
236 /** Initialize program spawner.
241 globalconf
.sndisplay
= sn_xcb_display_new(globalconf
.connection
, NULL
, NULL
);
243 globalconf
.snmonitor
= sn_monitor_context_new(globalconf
.sndisplay
,
244 globalconf
.default_screen
,
248 signal_add(&global_signals
, "spawn::canceled");
249 signal_add(&global_signals
, "spawn::change");
250 signal_add(&global_signals
, "spawn::completed");
251 signal_add(&global_signals
, "spawn::initiated");
252 signal_add(&global_signals
, "spawn::timeout");
256 spawn_launchee_timeout(struct ev_loop
*loop
, ev_timer
*w
, int revents
)
258 sn_launcher_context_complete(w
->data
);
259 sn_launcher_context_unref(w
->data
);
264 spawn_callback(gpointer user_data
)
270 * \param command_line The command line to launch.
271 * \param error A error pointer to fill with the possible error from
273 * \return g_spawn_async value.
276 spawn_command(const gchar
*command_line
, GError
**error
)
282 if(!g_shell_parse_argv(command_line
, NULL
, &argv
, error
))
285 retval
= g_spawn_async(NULL
, argv
, NULL
, G_SPAWN_SEARCH_PATH
,
286 spawn_callback
, NULL
, &pid
, error
);
295 * This function is multi-head (Zaphod) aware and will set display to
296 * the right screen according to mouse position.
297 * \param L The Lua VM state.
298 * \return The number of elements pushed on stack
300 * \lparam The command to launch.
301 * \lparam Use startup-notification, true or false, default to true.
302 * \lreturn Process ID if everything is OK, or an error string if an error occured.
305 luaA_spawn(lua_State
*L
)
310 if(lua_gettop(L
) >= 2)
311 use_sn
= luaA_checkboolean(L
, 2);
313 cmd
= luaL_checkstring(L
, 1);
315 SnLauncherContext
*context
= NULL
;
318 char *cmdname
, *space
;
319 const char *first_no_space_char
= cmd
;
320 /* Look for the first char which is not space */
321 while(*first_no_space_char
&& *first_no_space_char
== ' ')
322 first_no_space_char
++;
323 /* Look for space in the string to get the command name. */
324 if((space
= strchr(first_no_space_char
, ' ')))
325 cmdname
= a_strndup(cmd
, space
- cmd
);
327 cmdname
= a_strdup(cmd
);
329 context
= sn_launcher_context_new(globalconf
.sndisplay
, globalconf
.default_screen
);
330 sn_launcher_context_set_name(context
, "awesome");
331 sn_launcher_context_set_description(context
, "awesome spawn");
332 sn_launcher_context_set_binary_name(context
, cmdname
);
333 sn_launcher_context_initiate(context
, "awesome", cmdname
, globalconf
.timestamp
);
336 /* app will have AWESOME_SPAWN_TIMEOUT seconds to complete,
337 * or the timeout function will terminate the launch sequence anyway */
338 struct ev_timer
*ev_timeout
= p_new(struct ev_timer
, 1);
339 ev_timer_init(ev_timeout
, spawn_launchee_timeout
, AWESOME_SPAWN_TIMEOUT
, 0.);
340 ev_timeout
->data
= context
;
341 ev_timer_start(globalconf
.loop
, ev_timeout
);
342 sn_launcher_context_setup_child_process(context
);
345 GError
*error
= NULL
;
346 GPid pid
= spawn_command(cmd
, &error
);
349 /* push error on stack */
350 lua_pushstring(L
, error
->message
);
353 sn_launcher_context_complete(context
);
357 /* push pid on stack */
358 lua_pushnumber(L
, pid
);
363 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80