pkg_check_modules( GLIB2 REQUIRED glib-2.0 )
pkg_check_modules( GOBJECT2 REQUIRED gobject-2.0 )
pkg_check_modules( IMLIB2 REQUIRED imlib2>=1.4.2 )
+pkg_check_modules( SN libstartup-notification-1.0>=0.12 )
find_library( RT_LIBRARY rt )
if( NOT X11_FOUND OR NOT PANGOCAIRO_FOUND OR NOT PANGO_FOUND OR NOT CAIRO_FOUND OR NOT GLIB2_FOUND OR NOT GOBJECT2_FOUND OR NOT IMLIB2_FOUND )
${CAIRO_INCLUDE_DIRS}
${GLIB2_INCLUDE_DIRS}
${GOBJECT2_INCLUDE_DIRS}
- ${IMLIB2_INCLUDE_DIRS} )
+ ${IMLIB2_INCLUDE_DIRS}
+ ${SN_INCLUDE_DIRS} )
set( SOURCES src/config.c
src/panel.c
option( ENABLE_BATTERY "Enable battery status plugin" ON )
option( ENABLE_TINT2CONF "Enable tint2conf build, a GTK+2 theme switcher for tint2" ON )
option( ENABLE_EXAMPLES "Install additional tin2rc examples" OFF )
+option( ENABLE_SN "Startup notification support" ON )
+if( ENABLE_SN )
+ if( SN_FOUND )
+ add_definitions( -DHAVE_SN -DSN_API_NOT_YET_FROZEN )
+ endif( SN_FOUND )
+endif( ENABLE_SN)
if( ENABLE_BATTERY )
set( SOURCES ${SOURCES} src/battery/battery.c )
${CAIRO_LIBRARY_DIRS}
${GLIB2_LIBRARY_DIRS}
${GOBJECT2_LIBRARY_DIRS}
- ${IMLIB2_LIBRARY_DIRS} )
+ ${IMLIB2_LIBRARY_DIRS}
+ ${SN_LIBRARY_DIRS} )
add_executable(tint2 ${SOURCES})
target_link_libraries( tint2 ${X11_LIBRARIES}
${PANGOCAIRO_LIBRARIES}
${CAIRO_LIBRARIES}
${GLIB2_LIBRARIES}
${GOBJECT2_LIBRARIES}
- ${IMLIB2_LIBRARIES} )
+ ${IMLIB2_LIBRARIES}
+ ${SN_LIBRARIES} )
if( RT_LIBRARY )
target_link_libraries( tint2 ${RT_LIBRARY} )
endif( RT_LIBRARY )
#include <stdlib.h>
#include <glib/gi18n.h>
+#ifdef HAVE_SN
+#include <libsn/sn.h>
+#endif
+
#include "window.h"
#include "server.h"
#include "area.h"
}
}
-void launcher_action(LauncherIcon *icon)
+void launcher_action(LauncherIcon *icon, XEvent* evt)
{
char *cmd = malloc(strlen(icon->cmd) + 10);
sprintf(cmd, "(%s&)", icon->cmd);
- tint_exec(cmd);
+#if HAVE_SN
+ SnLauncherContext* ctx;
+ Time time;
+
+ ctx = sn_launcher_context_new(server.sn_dsp, server.screen);
+ sn_launcher_context_set_name(ctx, icon->icon_tooltip);
+ sn_launcher_context_set_description(ctx, "Application launched from tint2");
+ sn_launcher_context_set_binary_name (ctx, icon->cmd);
+ // Get a timestamp from the X event
+ if (evt->type == ButtonPress || evt->type == ButtonRelease) {
+ time = evt->xbutton.time;
+ }
+ else {
+ fprintf(stderr, "Unknown X event: %d\n", evt->type);
+ free(cmd);
+ return;
+ }
+ sn_launcher_context_initiate(ctx, "tint2", icon->cmd, time);
+#endif /* HAVE_SN */
+ pid_t pid;
+ pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "Could not fork\n");
+ }
+ else if (pid == 0) {
+#if HAVE_SN
+ sn_launcher_context_setup_child_process (ctx);
+#endif // HAVE_SN
+ // Allow children to exist after parent destruction
+ setsid ();
+ // Run the command
+ execl("/bin/sh", "/bin/sh", "-c", icon->cmd, NULL);
+
+ fprintf(stderr, "Failed to execlp %s\n", icon->cmd);
+#if HAVE_SN
+ sn_launcher_context_unref (ctx);
+#endif // HAVE_SN
+ _exit(1);
+ }
+#if HAVE_SN
+ else {
+ g_tree_insert (server.pids, GINT_TO_POINTER (pid), ctx);
+ }
+#endif // HAVE_SN
free(cmd);
}
void launcher_load_themes(Launcher *launcher);
// Populates the list_icons list
void launcher_load_icons(Launcher *launcher);
-void launcher_action(LauncherIcon *icon);
+void launcher_action(LauncherIcon *icon, XEvent* e);
void test_launcher_read_desktop_file();
void test_launcher_read_theme_file();
#include <X11/Xatom.h>
#include <X11/extensions/Xinerama.h>
+#ifdef HAVE_SN
+#include <libsn/sn.h>
+#include <glib.h>
+#endif
+
typedef struct Global_atom
{
Colormap colormap;
Colormap colormap32;
Global_atom atom;
+#ifdef HAVE_SN
+ SnDisplay *sn_dsp;
+ GTree *pids;
+#endif // HAVE_SN
} Server_global;
#include <Imlib2.h>
#include <signal.h>
+#ifdef HAVE_SN
+#include <libsn/sn.h>
+#include <sys/wait.h>
+#endif
+
#include <version.h>
#include "server.h"
#include "window.h"
// sigprocmask(SIG_BLOCK, &block_mask, 0);
}
+#ifdef HAVE_SN
+static int error_trap_depth = 0;
+
+static void
+error_trap_push (SnDisplay *display,
+ Display *xdisplay)
+{
+ ++error_trap_depth;
+}
+
+static void
+error_trap_pop (SnDisplay *display,
+ Display *xdisplay)
+{
+ if (error_trap_depth == 0)
+ {
+ fprintf(stderr, "Error trap underflow!\n");
+ return;
+ }
+
+ XSync(xdisplay, False); /* get all errors out of the queue */
+ --error_trap_depth;
+}
+
+static void sigchld_handler(int sig) {
+ // Wait for all dead processes
+ pid_t pid;
+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
+ SnLauncherContext *ctx;
+ ctx = (SnLauncherContext *) g_tree_lookup (server.pids, GINT_TO_POINTER (pid));
+ if (ctx == NULL) {
+ fprintf(stderr, "Unknown child %d terminated!\n", pid);
+ }
+ else {
+ g_tree_remove (server.pids, GINT_TO_POINTER (pid));
+ sn_launcher_context_complete (ctx);
+ sn_launcher_context_unref (ctx);
+ }
+ }
+}
+
+static gint cmp_ptr(gconstpointer a, gconstpointer b) {
+ if (a < b)
+ return -1;
+ else if (a == b)
+ return 0;
+ else
+ return 1;
+}
+
+#endif // HAVE_SN
+
void init_X11()
{
server.dsp = XOpenDisplay (NULL);
server_init_visual();
XSetErrorHandler ((XErrorHandler) server_catch_error);
+#ifdef HAVE_SN
+ // Initialize startup-notification
+ server.sn_dsp = sn_display_new (server.dsp, error_trap_push, error_trap_pop);
+ server.pids = g_tree_new (cmp_ptr);
+ // Setup a handler for child termination
+ struct sigaction act;
+ memset (&act, 0, sizeof (struct sigaction));
+ act.sa_handler = sigchld_handler;
+ if (sigaction(SIGCHLD, &act, 0)) {
+ perror("sigaction");
+ }
+#endif // HAVE_SN
+
imlib_context_set_display (server.dsp);
imlib_context_set_visual (server.visual);
imlib_context_set_colormap (server.colormap);
if ( click_launcher(panel, e->xbutton.x, e->xbutton.y)) {
LauncherIcon *icon = click_launcher_icon(panel, e->xbutton.x, e->xbutton.y);
if (icon) {
- launcher_action(icon);
+ launcher_action(icon, e);
}
task_drag = 0;
return;
if (select(x11_fd+1, &fdset, 0, 0, timeout) > 0) {
while (XPending (server.dsp)) {
XNextEvent(server.dsp, &e);
+#if HAVE_SN
+ sn_display_process_event (server.sn_dsp, &e);
+#endif // HAVE_SN
panel = get_panel(e.xany.window);
if (panel && panel_autohide) {