mirror of
https://github.com/awesomeWM/awesome
synced 2024-11-17 07:47:41 +01:00
spawn: add support for startup-notification
Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
parent
e4f7e99c3f
commit
f4221a1fdf
11 changed files with 269 additions and 7 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
4
client.c
4
client.c
|
@ -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);
|
||||
|
|
5
event.c
5
event.c
|
@ -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
15
hooks.c
|
@ -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 },
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
1
luaa.c
|
@ -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
226
spawn.c
|
@ -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)
|
||||
|
|
3
spawn.h
3
spawn.h
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue