spawn: add support for startup-notification

Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Julien Danjou 2009-04-03 16:30:18 +02:00
parent e4f7e99c3f
commit f4221a1fdf
11 changed files with 269 additions and 7 deletions

View File

@ -32,6 +32,7 @@
#include <xcb/xcb_event.h>
#include "awesome.h"
#include "spawn.h"
#include "client.h"
#include "window.h"
#include "ewmh.h"
@ -523,6 +524,9 @@ main(int argc, char **argv)
systray_init(screen_nbr);
}
/* init spawn (sn) */
spawn_init();
/* Parse and run configuration file */
luaA_parserc(xdg, confpath, true);

View File

@ -145,6 +145,7 @@ pkg_check_modules(AWESOME_REQUIRED REQUIRED
xcb-image>=0.3.0
xcb-property>=0.3.0
cairo-xcb
libstartup-notification-1.0>=0.10
xproto>=7.0.11
imlib2
libxdg-basedir)

View File

@ -31,6 +31,7 @@
#include "systray.h"
#include "property.h"
#include "wibox.h"
#include "spawn.h"
#include "common/atoms.h"
DO_LUA_TOSTRING(client_t, client, "client")
@ -545,6 +546,9 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, int phys_screen,
client_ban(c);
xcb_map_window(globalconf.connection, c->win);
if(!startup)
spawn_start_notify(c);
/* Call hook to notify list change */
if(globalconf.hooks.clients != LUA_REFNIL)
luaA_dofunction(globalconf.L, globalconf.hooks.clients, 0, 0);

View File

@ -823,10 +823,13 @@ event_handle_clientmessage(void *data __attribute__ ((unused)),
xcb_connection_t *connection,
xcb_client_message_event_t *ev)
{
client_t *c;
/* check for startup notification messages */
if(sn_xcb_display_process_event(globalconf.sndisplay, (xcb_generic_event_t *) ev))
return 0;
if(ev->type == WM_CHANGE_STATE)
{
client_t *c;
if((c = client_getbywin(ev->window))
&& ev->format == 32
&& ev->data.data32[0] == XCB_WM_STATE_ICONIC)

15
hooks.c
View File

@ -175,6 +175,20 @@ luaA_hooks_property(lua_State *L)
HANDLE_HOOK(L, globalconf.hooks.property);
}
/** Set the function called on each startup-notification events
* This function is called with a table and various fields set to describe the
* vents.
* \param L The Lua VM state.
* \return The number of elements pushed on stack.
* \luastack
* \lparam A function to call on each startup-notification event.
*/
static int
luaA_hooks_startup_notification(lua_State *L)
{
HANDLE_HOOK(L, globalconf.hooks.startup_notification);
}
/** Set the function to be called every N seconds.
* \param L The Lua VM state.
* \return The number of elements pushed on stack.
@ -233,6 +247,7 @@ const struct luaL_reg awesome_hooks_lib[] =
{ "clients", luaA_hooks_clients },
{ "tags", luaA_hooks_tags },
{ "tagged", luaA_hooks_tagged },
{ "startup_notification", luaA_hooks_startup_notification },
{ "timer", luaA_hooks_timer },
#ifdef WITH_DBUS
{ "dbus", luaA_hooks_dbus },

View File

@ -60,11 +60,13 @@ end
--- Spawn a program.
-- @param cmd The command.
-- @paran sn Enable startup-notification.
-- @param screen The screen where to spawn window.
-- @return The awesome.spawn return value.
function spawn(cmd, screen)
function spawn(cmd, sn, screen)
if cmd and cmd ~= "" then
return capi.awesome.spawn(cmd, screen or capi.mouse.screen)
if sn == nil then sn = true end
return capi.awesome.spawn(cmd, sn, screen or capi.mouse.screen)
end
end

View File

@ -44,7 +44,7 @@ function init(path)
for key, value in f:read("*all"):gsub("^","\n"):gmatch("\n[\t ]*([a-z_]+)[\t ]*=[\t ]*([^\n\t]+)") do
if key == "wallpaper_cmd" then
for s = 1, capi.screen.count() do
util.spawn(value, s)
util.spawn(value, false, s)
end
elseif key == "font" then
capi.awesome.font = value

1
luaa.c
View File

@ -882,6 +882,7 @@ luaA_init(xdgHandle xdg)
globalconf.hooks.tags = LUA_REFNIL;
globalconf.hooks.tagged = LUA_REFNIL;
globalconf.hooks.property = LUA_REFNIL;
globalconf.hooks.startup_notification = LUA_REFNIL;
globalconf.hooks.timer = LUA_REFNIL;
#ifdef WITH_DBUS
globalconf.hooks.dbus = LUA_REFNIL;

226
spawn.c
View File

@ -24,9 +24,200 @@
#include <sys/types.h>
#include <sys/wait.h>
#include "structs.h"
#include "spawn.h"
#include "screen.h"
#include "luaa.h"
#include "event.h"
/** 20 seconds timeout */
#define AWESOME_SPAWN_TIMEOUT 20.0
/** Wrapper for unrefing startup sequence.
*/
static inline void
a_sn_startup_sequence_unref(SnStartupSequence **sss)
{
return sn_startup_sequence_unref(*sss);
}
DO_ARRAY(SnStartupSequence *, SnStartupSequence, a_sn_startup_sequence_unref)
/** The array of startup sequence running */
SnStartupSequence_array_t sn_waits;
/** Remove a SnStartupSequence pointer from an array and forget about it.
* \param array The startup sequence array.
* \param s The startup sequence to found, remove and unref.
*/
static inline void
spawn_sequence_remove(SnStartupSequence *s)
{
for(int i = 0; i < sn_waits.len; i++)
if(sn_waits.tab[i] == s)
{
SnStartupSequence_array_take(&sn_waits, i);
sn_startup_sequence_unref(s);
break;
}
}
static void
spawn_monitor_timeout(struct ev_loop *loop, ev_timer *w, int revents)
{
spawn_sequence_remove(w->data);
p_delete(&w);
}
static void
spawn_monitor_event(SnMonitorEvent *event, void *data)
{
if(globalconf.hooks.startup_notification == LUA_REFNIL)
return;
SnStartupSequence *sequence = sn_monitor_event_get_startup_sequence(event);
SnMonitorEventType event_type = sn_monitor_event_get_type(event);
lua_newtable(globalconf.L);
lua_pushstring(globalconf.L, sn_startup_sequence_get_id(sequence));
lua_setfield(globalconf.L, -2, "id");
switch(event_type)
{
case SN_MONITOR_EVENT_INITIATED:
sn_startup_sequence_ref(sequence);
SnStartupSequence_array_append(&sn_waits, sequence);
lua_pushliteral(globalconf.L, "initiated");
lua_setfield(globalconf.L, -2, "type");
/* Add a timeout function so we do not wait for this event to complete
* for ever */
struct ev_timer *ev_timeout = p_new(struct ev_timer, 1);
ev_timer_init(ev_timeout, spawn_monitor_timeout, AWESOME_SPAWN_TIMEOUT, 0.);
ev_timeout->data = sequence;
ev_timer_start(globalconf.loop, ev_timeout);
break;
case SN_MONITOR_EVENT_CHANGED:
lua_pushliteral(globalconf.L, "change");
lua_setfield(globalconf.L, -2, "type");
break;
case SN_MONITOR_EVENT_COMPLETED:
lua_pushliteral(globalconf.L, "completed");
lua_setfield(globalconf.L, -2, "type");
break;
case SN_MONITOR_EVENT_CANCELED:
lua_pushliteral(globalconf.L, "canceled");
lua_setfield(globalconf.L, -2, "type");
break;
}
/* common actions */
switch(event_type)
{
case SN_MONITOR_EVENT_INITIATED:
case SN_MONITOR_EVENT_CHANGED:
{
const char *s = sn_startup_sequence_get_name(sequence);
if(s)
{
lua_pushstring(globalconf.L, s);
lua_setfield(globalconf.L, -2, "name");
}
if((s = sn_startup_sequence_get_description(sequence)))
{
lua_pushstring(globalconf.L, s);
lua_setfield(globalconf.L, -2, "description");
}
lua_pushnumber(globalconf.L, sn_startup_sequence_get_workspace(sequence));
lua_setfield(globalconf.L, -2, "workspace");
if((s = sn_startup_sequence_get_binary_name(sequence)))
{
lua_pushstring(globalconf.L, s);
lua_setfield(globalconf.L, -2, "binary_name");
}
if((s = sn_startup_sequence_get_icon_name(sequence)))
{
lua_pushstring(globalconf.L, s);
lua_setfield(globalconf.L, -2, "icon_name");
}
if((s = sn_startup_sequence_get_wmclass(sequence)))
{
lua_pushstring(globalconf.L, s);
lua_setfield(globalconf.L, -2, "wmclass");
}
}
break;
case SN_MONITOR_EVENT_COMPLETED:
case SN_MONITOR_EVENT_CANCELED:
spawn_sequence_remove(sequence);
break;
}
luaA_dofunction(globalconf.L, globalconf.hooks.startup_notification, 1, 0);
}
/** Tell the spawn module that an app has been started.
* \param c The client that just started.
*/
void
spawn_start_notify(client_t *c)
{
foreach(_seq, sn_waits)
{
SnStartupSequence *seq = *_seq;
bool found = false;
const char *seqid = sn_startup_sequence_get_id(seq);
if(!a_strcmp(seqid, c->startup_id))
found = true;
else
{
const char *seqclass = sn_startup_sequence_get_wmclass(seq);
if(!a_strcmp(seqclass, c->class) || !a_strcmp(seqclass, c->instance))
found = true;
else
{
const char *seqbin = sn_startup_sequence_get_binary_name(seq);
if(!a_strcasecmp(seqbin, c->class) || !a_strcasecmp(seqbin, c->instance))
found = true;
}
}
if(found)
{
sn_startup_sequence_complete(seq);
break;
}
}
}
/** Initialize program spawner.
*/
void
spawn_init(void)
{
globalconf.sndisplay = sn_xcb_display_new(globalconf.connection, NULL, NULL);
const int screen_max = xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
for(int screen = 0; screen < screen_max; screen++)
globalconf.screens[screen].snmonitor = sn_monitor_context_new(globalconf.sndisplay,
screen,
spawn_monitor_event,
NULL, NULL);
}
static void
spawn_launchee_timeout(struct ev_loop *loop, ev_timer *w, int revents)
{
sn_launcher_context_complete(w->data);
sn_launcher_context_unref(w->data);
p_delete(&w);
}
/** Spawn a program.
* This function is multi-head (Zaphod) aware and will set display to
@ -35,6 +226,7 @@
* \return The number of elements pushed on stack
* \luastack
* \lparam The command to launch.
* \lparam Use startup-notification, true or false, default to true.
* \lparam The optional screen number to spawn the command on.
*/
int
@ -42,11 +234,15 @@ luaA_spawn(lua_State *L)
{
char *host, newdisplay[128];
const char *cmd;
bool use_sn = true;
int screen = 0, screenp, displayp;
if(lua_gettop(L) == 2)
if(lua_gettop(L) >= 2)
use_sn = luaA_checkboolean(L, 2);
if(lua_gettop(L) == 3)
{
screen = luaL_checknumber(L, 2) - 1;
screen = luaL_checknumber(L, 3) - 1;
luaA_checkscreen(screen);
}
@ -60,6 +256,30 @@ luaA_spawn(lua_State *L)
p_delete(&host);
}
if(use_sn)
{
char *cmdname, *space;
if((space = strchr(cmd, ' ')))
cmdname = a_strndup(cmd, space - cmd);
else
cmdname = a_strdup(cmd);
SnLauncherContext *context = sn_launcher_context_new(globalconf.sndisplay, screen_virttophys(screen));
sn_launcher_context_set_name(context, "awesome");
sn_launcher_context_set_description(context, "awesome spawn");
sn_launcher_context_set_binary_name(context, cmdname);
sn_launcher_context_initiate(context, "awesome", cmdname, XCB_CURRENT_TIME);
p_delete(&cmdname);
/* app will have AWESOME_SPAWN_TIMEOUT seconds to complete,
* or the timeout function will terminate the launch sequence anyway */
struct ev_timer *ev_timeout = p_new(struct ev_timer, 1);
ev_timer_init(ev_timeout, spawn_launchee_timeout, AWESOME_SPAWN_TIMEOUT, 0.);
ev_timeout->data = context;
ev_timer_start(globalconf.loop, ev_timeout);
sn_launcher_context_setup_child_process(context);
}
/* The double-fork construct avoids zombie processes and keeps the code
* clean from stupid signal handlers. */
if(fork() == 0)

View File

@ -23,7 +23,10 @@
#define AWESOME_SPAWN_H
#include <lua.h>
#include "structs.h"
void spawn_init(void);
void spawn_start_notify(client_t *);
int luaA_spawn(lua_State *);
#endif

View File

@ -22,6 +22,9 @@
#ifndef AWESOME_STRUCTS_H
#define AWESOME_STRUCTS_H
#define SN_API_NOT_YET_FROZEN
#include <libsn/sn.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xcb_property.h>
@ -285,6 +288,8 @@ typedef struct
} systray;
/** Focused client */
client_t *client_focus;
/** The monitor of startup notifications */
SnMonitorContext *snmonitor;
} screen_t;
/** Main configuration structure */
@ -365,6 +370,8 @@ struct awesome_t
luaA_ref property;
/** Command to run on time */
luaA_ref timer;
/** Startup notification hooks */
luaA_ref startup_notification;
#ifdef WITH_DBUS
/** Command to run on dbus events */
luaA_ref dbus;
@ -382,6 +389,8 @@ struct awesome_t
screen_t *screen_focus;
/** Need to call client_stack_refresh() */
bool client_need_stack_refresh;
/** The startup notification display struct */
SnDisplay *sndisplay;
};
DO_ARRAY(const void *, void, DO_NOTHING)