setenv and unsetenv dont exist in Solaris 9. (Fixes bug #4663)
[mikachu/openbox.git] / openbox / startupnotify.c
index 8866e40..e249002 100644 (file)
@@ -1,34 +1,60 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+   startupnotify.c for the Openbox window manager
+   Copyright (c) 2006        Mikael Magnusson
+   Copyright (c) 2003-2007   Dana Jansens
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   See the COPYING file for a copy of the GNU General Public License.
+*/
+
 #include "startupnotify.h"
+#include "gettext.h"
+#include "event.h"
+#include "obt/xqueue.h"
+
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
 
 #ifndef USE_LIBSN
 
 void sn_startup(gboolean reconfig) {}
 void sn_shutdown(gboolean reconfig) {}
 gboolean sn_app_starting() { return FALSE; }
-void sn_app_started(gchar *wmclass) {}
+Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name)
+{
+    return CurrentTime;
+}
 gboolean sn_get_desktop(gchar *id, guint *desktop) { return FALSE; }
+void sn_setup_spawn_environment(const gchar *program, const gchar *name,
+                                const gchar *icon_name, const gchar *wmclass,
+                                gint desktop) {}
+void sn_spawn_cancel() {}
 
 #else
 
 #include "openbox.h"
-#include "mainloop.h"
 #include "screen.h"
 
 #define SN_API_NOT_YET_FROZEN
 #include <libsn/sn.h>
 
-typedef struct {
-    SnStartupSequence *seq;
-    gboolean feedback;
-} ObWaitData;
-
 static SnDisplay *sn_display;
 static SnMonitorContext *sn_context;
-static GSList *sn_waits; /* list of ObWaitDatas */
+static SnLauncherContext *sn_launcher;
+static GSList *sn_waits; /* list of SnStartupSequences we're waiting on */
 
-static ObWaitData* wait_data_new(SnStartupSequence *seq);
-static void wait_data_free(ObWaitData *d);
-static ObWaitData* wait_find(const gchar *id);
+static SnStartupSequence* sequence_find(const gchar *id);
 
 static void sn_handler(const XEvent *e, gpointer data);
 static void sn_event_func(SnMonitorEvent *event, gpointer data);
@@ -37,11 +63,12 @@ void sn_startup(gboolean reconfig)
 {
     if (reconfig) return;
 
-    sn_display = sn_display_new(ob_display, NULL, NULL);
+    sn_display = sn_display_new(obt_display, NULL, NULL);
     sn_context = sn_monitor_context_new(sn_display, ob_screen,
                                         sn_event_func, NULL, NULL);
+    sn_launcher = sn_launcher_context_new(sn_display, ob_screen);
 
-    ob_main_loop_x_add(ob_main_loop, sn_handler, NULL, NULL);
+    xqueue_add_callback(sn_handler, NULL);
 }
 
 void sn_shutdown(gboolean reconfig)
@@ -50,79 +77,48 @@ void sn_shutdown(gboolean reconfig)
 
     if (reconfig) return;
 
+    xqueue_remove_callback(sn_handler, NULL);
+
     for (it = sn_waits; it; it = g_slist_next(it))
-        wait_data_free(it->data);
+        sn_startup_sequence_unref((SnStartupSequence*)it->data);
     g_slist_free(sn_waits);
     sn_waits = NULL;
 
     screen_set_root_cursor();
 
+    sn_launcher_context_unref(sn_launcher);
     sn_monitor_context_unref(sn_context);
     sn_display_unref(sn_display);
 }
 
-static ObWaitData* wait_data_new(SnStartupSequence *seq)
-{
-    ObWaitData *d = g_new(ObWaitData, 1);
-    d->seq = seq;
-    d->feedback = TRUE;
-
-    sn_startup_sequence_ref(d->seq);
-
-    return d;
-}
-
-static void wait_data_free(ObWaitData *d)
+static SnStartupSequence* sequence_find(const gchar *id)
 {
-    if (d) {
-        sn_startup_sequence_unref(d->seq);
-
-        g_free(d);
-    }
-}
-
-static ObWaitData* wait_find(const gchar *id)
-{
-    ObWaitData *ret = NULL;
+    SnStartupSequence*ret = NULL;
     GSList *it;
 
     for (it = sn_waits; it; it = g_slist_next(it)) {
-        ObWaitData *d = it->data;
-        if (!strcmp(id, sn_startup_sequence_get_id(d->seq))) {
-            ret = d;
+        SnStartupSequence *seq = it->data;
+        if (!strcmp(id, sn_startup_sequence_get_id(seq))) {
+            ret = seq;
             break;
         }
     }
     return ret;
 }
 
-gboolean sn_app_starting()
+gboolean sn_app_starting(void)
 {
-    GSList *it;
-
-    for (it = sn_waits; it; it = g_slist_next(it)) {
-        ObWaitData *d = it->data;
-        if (d->feedback)
-            return TRUE;
-    }
-    return FALSE;
+    return sn_waits != NULL;
 }
 
 static gboolean sn_wait_timeout(gpointer data)
 {
-    ObWaitData *d = data;
-    d->feedback = FALSE;
+    SnStartupSequence *seq = data;
+    sn_waits = g_slist_remove(sn_waits, seq);
     screen_set_root_cursor();
     return FALSE; /* don't repeat */
 }
 
-static void sn_wait_destroy(gpointer data)
-{
-    ObWaitData *d = data;
-    sn_waits = g_slist_remove(sn_waits, d);
-    wait_data_free(d);
-}
-
 static void sn_handler(const XEvent *e, gpointer data)
 {
     XEvent ec;
@@ -134,19 +130,19 @@ static void sn_event_func(SnMonitorEvent *ev, gpointer data)
 {
     SnStartupSequence *seq;
     gboolean change = FALSE;
-    ObWaitData *d;
 
     if (!(seq = sn_monitor_event_get_startup_sequence(ev)))
         return;
 
     switch (sn_monitor_event_get_type(ev)) {
     case SN_MONITOR_EVENT_INITIATED:
-        g_message("starting");
-        d = wait_data_new(seq);
-        sn_waits = g_slist_prepend(sn_waits, d);
-        /* 30 second timeout for apps to start */
-        ob_main_loop_timeout_add(ob_main_loop, 30 * G_USEC_PER_SEC,
-                                 sn_wait_timeout, d, sn_wait_destroy);
+        sn_startup_sequence_ref(seq);
+        sn_waits = g_slist_prepend(sn_waits, seq);
+        /* 20 second timeout for apps to start if the launcher doesn't
+           have a timeout */
+        g_timeout_add_full(G_PRIORITY_DEFAULT,
+                           20 * 1000, sn_wait_timeout, seq,
+                           (GDestroyNotify)sn_startup_sequence_unref);
         change = TRUE;
         break;
     case SN_MONITOR_EVENT_CHANGED:
@@ -155,9 +151,9 @@ static void sn_event_func(SnMonitorEvent *ev, gpointer data)
         break;
     case SN_MONITOR_EVENT_COMPLETED:
     case SN_MONITOR_EVENT_CANCELED:
-        if ((d = wait_find(sn_startup_sequence_get_id(seq)))) {
-            d->feedback = FALSE;
-            ob_main_loop_timeout_remove_data(ob_main_loop, sn_wait_timeout, d);
+        if ((seq = sequence_find(sn_startup_sequence_get_id(seq)))) {
+            sn_waits = g_slist_remove(sn_waits, seq);
+            g_source_remove_by_user_data(seq);
             change = TRUE;
         }
         break;
@@ -167,27 +163,59 @@ static void sn_event_func(SnMonitorEvent *ev, gpointer data)
         screen_set_root_cursor();
 }
 
-void sn_app_started(gchar *wmclass)
+Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name)
 {
     GSList *it;
+    Time t = CurrentTime;
+
+    if (!id && !wmclass)
+        return t;
 
     for (it = sn_waits; it; it = g_slist_next(it)) {
-        ObWaitData *d = it->data;
-        if (sn_startup_sequence_get_wmclass(d->seq) &&
-            !strcmp(sn_startup_sequence_get_wmclass(d->seq), wmclass))
-        {
-            sn_startup_sequence_complete(d->seq);
+        SnStartupSequence *seq = it->data;
+        gboolean found = FALSE;
+        const gchar *seqid, *seqclass, *seqbin;
+        seqid = sn_startup_sequence_get_id(seq);
+        seqclass = sn_startup_sequence_get_wmclass(seq);
+        seqbin = sn_startup_sequence_get_binary_name(seq);
+
+        if (id && seqid) {
+            /* if the app has a startup id, then look for that for highest
+               accuracy */
+            if (!strcmp(seqid, id))
+                found = TRUE;
+        }
+        else if (seqclass) {
+            /* seqclass = "a string to match against the "resource name" or
+               "resource class" hints.  These are WM_CLASS[0] and WM_CLASS[1]"
+               - from the startup-notification spec
+            */
+            found = (seqclass && !strcmp(seqclass, wmclass)) ||
+                (seqclass && !strcmp(seqclass, name));
+        }
+        else if (seqbin) {
+            /* Check the binary name against the class and name hints
+               as well, to help apps that don't have the class set
+               correctly */
+            found = (seqbin && !g_ascii_strcasecmp(seqbin, wmclass)) ||
+                (seqbin && !g_ascii_strcasecmp(seqbin, name));
+        }
+
+        if (found) {
+            sn_startup_sequence_complete(seq);
+            t = sn_startup_sequence_get_timestamp(seq);
             break;
         }
     }
+    return t;
 }
 
 gboolean sn_get_desktop(gchar *id, guint *desktop)
 {
-    ObWaitData *d;
+    SnStartupSequence *seq;
 
-    if (id && (d = wait_find(id))) {
-        gint desk = sn_startup_sequence_get_workspace(d->seq);
+    if (id && (seq = sequence_find(id))) {
+        gint desk = sn_startup_sequence_get_workspace(seq);
         if (desk != -1) {
             *desktop = desk;
             return TRUE;
@@ -196,4 +224,53 @@ gboolean sn_get_desktop(gchar *id, guint *desktop)
     return FALSE;
 }
 
+static gboolean sn_launch_wait_timeout(gpointer data)
+{
+    SnLauncherContext *sn = data;
+    sn_launcher_context_complete(sn);
+    return FALSE; /* don't repeat */
+}
+
+void sn_setup_spawn_environment(const gchar *program, const gchar *name,
+                                const gchar *icon_name, const gchar *wmclass,
+                                gint desktop)
+{
+    gchar *desc;
+    const char *id;
+
+    desc = g_strdup_printf(_("Running %s"), program);
+
+    if (sn_launcher_context_get_initiated(sn_launcher)) {
+        sn_launcher_context_unref(sn_launcher);
+        sn_launcher = sn_launcher_context_new(sn_display, ob_screen);
+    }
+
+    sn_launcher_context_set_name(sn_launcher, name ? name : program);
+    sn_launcher_context_set_description(sn_launcher, desc);
+    sn_launcher_context_set_icon_name(sn_launcher, icon_name ?
+                                      icon_name : program);
+    sn_launcher_context_set_binary_name(sn_launcher, program);
+    if (wmclass) sn_launcher_context_set_wmclass(sn_launcher, wmclass);
+    if (desktop >= 0 && (unsigned) desktop < screen_num_desktops)
+        sn_launcher_context_set_workspace(sn_launcher, (signed) desktop);
+    sn_launcher_context_initiate(sn_launcher, "openbox", program,
+                                 event_time());
+    id = sn_launcher_context_get_startup_id(sn_launcher);
+
+    /* 20 second timeout for apps to start */
+    sn_launcher_context_ref(sn_launcher);
+    g_timeout_add_full(G_PRIORITY_DEFAULT,
+                       20 * 1000, sn_launch_wait_timeout, sn_launcher,
+                       (GDestroyNotify)sn_launcher_context_unref);
+
+    g_setenv("DESKTOP_STARTUP_ID", id, TRUE);
+
+    g_free(desc);
+}
+
+void sn_spawn_cancel(void)
+{
+    sn_launcher_context_complete(sn_launcher);
+}
+
 #endif