Index: doc/startup-notification.txt
===================================================================
RCS file: /cvs/startup-notification/startup-notification/doc/startup-notification.txt,v
retrieving revision 1.9
diff -p -u -r1.9 startup-notification.txt
--- doc/startup-notification.txt	11 Oct 2004 14:27:54 -0000	1.9
+++ doc/startup-notification.txt	23 Oct 2004 03:25:22 -0000
@@ -209,7 +209,7 @@ All messages must include these keys:
   ID
  
           uniquely identifies a startup sequence; should be some globally 
-          unique string (for example, hostname+pid+current time).
+          unique string (for example, hostname+pid+"_TIME"+current time).
           The string should be in the form of <unique>_TIME<timestamp>,
           where the timestamp is the X server timestamp of the user
           action that caused the launch. See the TIMESTAMP key
Index: libsn/sn-internals.h
===================================================================
RCS file: /cvs/startup-notification/startup-notification/libsn/sn-internals.h,v
retrieving revision 1.5
diff -p -u -r1.5 sn-internals.h
--- libsn/sn-internals.h	19 Jan 2003 05:43:49 -0000	1.5
+++ libsn/sn-internals.h	23 Oct 2004 03:25:22 -0000
@@ -71,6 +71,9 @@ void      sn_internal_strfreev      (cha
 
 unsigned long sn_internal_string_to_ulong (const char* str);
 
+char*     sn_internal_find_last_occurrence (const char* haystack, 
+                                            const char* needle);
+
 void sn_internal_append_to_string (char      **append_to,
                                    int        *current_len,
                                    const char *append);
Index: libsn/sn-launchee.c
===================================================================
RCS file: /cvs/startup-notification/startup-notification/libsn/sn-launchee.c,v
retrieving revision 1.6
diff -p -u -r1.6 sn-launchee.c
--- libsn/sn-launchee.c	15 Dec 2002 19:04:07 -0000	1.6
+++ libsn/sn-launchee.c	23 Oct 2004 03:25:22 -0000
@@ -123,6 +123,51 @@ sn_launchee_context_get_startup_id (SnLa
   return context->startup_id;
 }
 
+/**
+ * sn_launchee_context_get_id_has_timestamp:
+ * @context: an #SnLauncheeContext
+ * 
+ * Return whether the startup ID for the context contains a timestamp
+ * 
+ * Return value: whether the startup ID has an embedded timestamp
+ **/
+int
+sn_launchee_context_get_id_has_timestamp (SnLauncheeContext *context)
+{
+  char * time_str;
+
+  time_str = sn_internal_find_last_occurrence(context->startup_id, "_TIME");
+
+  return time_str != NULL;
+}
+
+/**
+ * sn_launchee_context_get_timestamp:
+ * @context: an #SnLauncheeContext
+ * 
+ * Return the timestamp embedded in the startup ID
+ * 
+ * Return value: timestamp embedded in the startup ID
+ **/
+Time
+sn_launchee_context_get_timestamp (SnLauncheeContext *context)
+{
+  char * time_str;
+
+  time_str = sn_internal_find_last_occurrence(context->startup_id, "_TIME");
+  if (time_str != NULL)
+    {
+      /* Skip past the "_TIME" part */
+      time_str += 5;
+
+      return sn_internal_string_to_ulong (time_str);
+    }
+
+  fprintf (stderr,
+           "libsn: No timestamp contained in the startup ID!\n");
+  /* Unfortunately, all values are valid; let's just return -1 */
+  return -1;
+}
 
 /**
  * sn_launchee_context_complete:
Index: libsn/sn-launchee.h
===================================================================
RCS file: /cvs/startup-notification/startup-notification/libsn/sn-launchee.h,v
retrieving revision 1.3
diff -p -u -r1.3 sn-launchee.h
--- libsn/sn-launchee.h	21 Oct 2002 02:47:15 -0000	1.3
+++ libsn/sn-launchee.h	23 Oct 2004 03:25:22 -0000
@@ -41,6 +41,8 @@ SnLauncheeContext* sn_launchee_context_n
 void               sn_launchee_context_ref                  (SnLauncheeContext *context);
 void               sn_launchee_context_unref                (SnLauncheeContext *context);
 const char*        sn_launchee_context_get_startup_id       (SnLauncheeContext *context);
+int                sn_launchee_context_get_id_has_timestamp (SnLauncheeContext *context);
+Time               sn_launchee_context_get_timestamp        (SnLauncheeContext *context);
 void               sn_launchee_context_complete             (SnLauncheeContext *context);
 void               sn_launchee_context_setup_window         (SnLauncheeContext *context,
                                                              Window             xwindow);
Index: libsn/sn-launcher.c
===================================================================
RCS file: /cvs/startup-notification/startup-notification/libsn/sn-launcher.c,v
retrieving revision 1.10
diff -p -u -r1.10 sn-launcher.c
--- libsn/sn-launcher.c	24 Jun 2004 08:32:38 -0000	1.10
+++ libsn/sn-launcher.c	23 Oct 2004 03:25:22 -0000
@@ -41,7 +41,6 @@ struct SnLauncherContext
   char               *name;
   char               *description;
   int                 workspace;
-  Time                timestamp;
   char               *wmclass;
   char               *binary_name;
   char               *icon_name;
@@ -81,7 +80,6 @@ sn_launcher_context_new (SnDisplay      
   sn_display_ref (context->display);
 
   context->workspace = -1;
-  context->timestamp = 0;
   
   sn_list_prepend (context_list, context);
 
@@ -179,7 +177,6 @@ sn_launcher_context_initiate (SnLauncher
   char *values[MAX_PROPS];  
   char *message;
   char workspacebuf[257];
-  char timestampbuf[257];
   char screenbuf[257];
   
   if (context->startup_id != NULL)
@@ -205,15 +202,15 @@ sn_launcher_context_initiate (SnLauncher
     256; /* 256 is longer than a couple %d and some slashes */
   
   s = sn_malloc (len + 3);
-  snprintf (s, len, "%s/%s/%lu/%d-%d-%s",
-            canonicalized_launcher, canonicalized_launchee, (unsigned long) timestamp,
-            (int) getpid (), (int) sequence_number, hostbuf);
+  snprintf (s, len, "%s/%s/%d-%d-%s_TIME%lu",
+            canonicalized_launcher, canonicalized_launchee,
+            (int) getpid (), (int) sequence_number, hostbuf,
+            (unsigned long) timestamp);
   ++sequence_number;
   
   sn_free (canonicalized_launcher);
   sn_free (canonicalized_launchee);
 
-  context->timestamp = timestamp;  
   context->startup_id = s;
   
   i = 0;
@@ -227,11 +224,6 @@ sn_launcher_context_initiate (SnLauncher
   values[i] = screenbuf;
   ++i;
   
-  names[i] = "TIMESTAMP";
-  sprintf (timestampbuf, "%lu", context->timestamp);
-  values[i] = timestampbuf;
-  ++i;
-
   if (context->name != NULL)
     {
       names[i] = "NAME";
Index: libsn/sn-monitor.c
===================================================================
RCS file: /cvs/startup-notification/startup-notification/libsn/sn-monitor.c,v
retrieving revision 1.9
diff -p -u -r1.9 sn-monitor.c
--- libsn/sn-monitor.c	24 Jun 2004 08:32:38 -0000	1.9
+++ libsn/sn-monitor.c	23 Oct 2004 03:25:23 -0000
@@ -70,6 +70,7 @@ struct SnStartupSequence
 
   unsigned int completed : 1;
   unsigned int canceled : 1;
+  unsigned int timestamp_set : 1;
   
   int creation_serial;
 
@@ -307,7 +308,15 @@ sn_startup_sequence_get_workspace (SnSta
 Time
 sn_startup_sequence_get_timestamp (SnStartupSequence *sequence)
 {
-  return sequence->timestamp;
+  if (!sequence->timestamp_set)
+    {
+      fprintf (stderr,
+               "libsn: Buggy startup-notification launcher!  No timestamp!\n");
+      /* Unfortunately, all values are valid; let's just return -1 */
+      return -1;
+    }
+  else
+    return sequence->timestamp;
 }
 
 const char*
@@ -430,6 +439,7 @@ sn_startup_sequence_new (SnDisplay *disp
   sequence->screen = -1; /* not set */
   sequence->workspace = -1; /* not set */
   sequence->timestamp = 0;
+  sequence->timestamp_set = FALSE;
 
   sequence->initiation_time.tv_sec = 0;
   sequence->initiation_time.tv_usec = 0;
@@ -708,12 +718,26 @@ xmessage_func (SnDisplay  *display,
       if (sequence == NULL)
         {
           SnMonitorEvent *event;
+          char *time_str;
 
           sequence = add_sequence (display);
           if (sequence == NULL)
             goto out;
           
           sequence->id = sn_internal_strdup (launch_id);
+
+          /* Current spec says timestamp is part of the startup id; so we need
+           * to get the timestamp here if the launcher is using the current spec
+           */
+          time_str = sn_internal_find_last_occurrence(sequence->id, "_TIME");
+          if (time_str != NULL)
+            {
+              /* Skip past the "_TIME" part */
+              time_str += 5;
+
+              sequence->timestamp = sn_internal_string_to_ulong (time_str);
+              sequence->timestamp_set = TRUE;
+            }
           
           event = sn_new (SnMonitorEvent, 1);
           
@@ -791,13 +815,20 @@ xmessage_func (SnDisplay  *display,
               sequence->workspace = workspace;
               changed = TRUE;
             }
-          else if (strcmp (names[i], "TIMESTAMP") == 0)
+          else if (strcmp (names[i], "TIMESTAMP") == 0 && 
+                   !sequence->timestamp_set)
             {
+              /* Old version of the spec says that the timestamp was
+               * sent as part of a TIMESTAMP message.  We try to
+               * handle that to enable backwards compatibility with
+               * older launchers.
+               */
               Time timestamp;
-              
+
               timestamp = sn_internal_string_to_ulong (values[i]);
 
               sequence->timestamp = timestamp;
+              sequence->timestamp_set = TRUE;
               changed = TRUE;
             }
           else if (strcmp (names[i], "WMCLASS") == 0)
Index: libsn/sn-util.c
===================================================================
RCS file: /cvs/startup-notification/startup-notification/libsn/sn-util.c,v
retrieving revision 1.4
diff -p -u -r1.4 sn-util.c
--- libsn/sn-util.c	8 Dec 2002 03:25:25 -0000	1.4
+++ libsn/sn-util.c	23 Oct 2004 03:25:23 -0000
@@ -310,6 +310,56 @@ sn_internal_string_to_ulong (const char*
   return retval;
 }
 
+/**
+ * sn_internal_find_last_occurrence:
+ * @haystack: a nul-terminated string.
+ * @needle: the nul-terminated string to search for.
+ *
+ * Searches the string @haystack for the last occurrence
+ * of the string @needle.
+ *
+ * Return value: a pointer to the found occurrence, or
+ *    %NULL if not found.
+ **/
+char*
+sn_internal_find_last_occurrence (const char* haystack, 
+                                  const char* needle)
+{
+  int i;
+  int needle_len;
+  int haystack_len;
+  const char *p;
+      
+  if (haystack == NULL)
+    return NULL;
+  if (needle == NULL)
+    return NULL;
+
+  needle_len = strlen (needle);
+  haystack_len = strlen (haystack);
+
+  if (needle_len == 0)
+    return (char *)haystack;
+
+  if (haystack_len < needle_len)
+    return NULL;
+  
+  p = haystack + haystack_len - needle_len;
+
+  while (p >= haystack)
+    {
+      for (i = 0; i < needle_len; i++)
+        if (p[i] != needle[i])
+          goto next;
+      
+      return (char *)p;
+      
+    next:
+      p--;
+    }
+  
+  return NULL;
+}
 
 void
 sn_internal_append_to_string (char      **append_to,
