unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Tom Tromey <tromey@redhat.com>
To: Dan Nicolaescu <dann@ics.uci.edu>
Cc: emacs-devel@gnu.org
Subject: Re: RFC: status icon support
Date: Fri, 11 Jan 2008 18:38:44 -0700	[thread overview]
Message-ID: <m3zlvbkel7.fsf@fleche.redhat.com> (raw)
In-Reply-To: <200801120157.m0C1v6WL020654@oogie-boogie.ics.uci.edu> (Dan Nicolaescu's message of "Fri\, 11 Jan 2008 17\:57\:06 -0800")

>>>>> "Dan" == Dan Nicolaescu <dann@ics.uci.edu> writes:

Dan> Please no K&R in new code.  

Here is the updated patch.

Tom

ChangeLog:
2008-01-11  Tom Tromey  <tromey@redhat.com>

	* configure: Rebuild.
	* configure.in: Check for GtkStatusIcon, libnotify.

doc/lispref/ChangeLog:
2008-01-11  Tom Tromey  <tromey@redhat.com>

	* frames.texi (Status Icons): New node.
	(Frames): Update.

src/ChangeLog:
2008-01-11  Tom Tromey  <tromey@redhat.com>

	* xmenu.c: Include systray.h.
	(Fx_popup_menu): Update documentation.  Handle status-icon
	position argument.
	(create_and_show_popup_menu): Add status_icon and status_button
	arguments.  Update.  Use gtk_get_current_event_time.
	(xmenu_show): Add status_icon and status_button arguments.
	Update.
	* termhooks.h (enum event_kind) <STATUS_ICON_CLICK_EVENT>: New
	constant.
	* print.c (print_object): Handle status icons.
	* lisp.h (enum pvec_type) <PVEC_STATUS_ICON> New constant.
	<PVEC_TYPE_MASK>: Update.
	(STATUS_ICONP): New macro.
	(GC_STATUS_ICONP): Likewise.
	(allocate_status_icon, syms_of_systray): Declare.
	* keyboard.c (Qstatus_icon_click_event): New global.
	(kbd_buffer_get_event): Handle STATUS_ICON_CLICK_EVENT.
	(make_lispy_event): Likewise.
	(syms_of_keyboard): Initialize Qstatus_icon_click_event.  Define
	"status-icon-click-event" event.
	* emacs.c (main): Call syms_of_systray.
	* alloc.c: Include systray.h.
	(allocate_status_icon): New function.
	* config.in: Rebuild.
	* Makefile.in (LIBNOTIFY_CFLAGS): New variable.
	(LIBNOTIFY_LIBS): Likewise.
	(ALL_CFLAGS): Add LIBNOTIFY_CFLAGS.
	(GTK_OBJ): Add systray.o
	(LIBES): Add LIBNOTIFY_LIBS.
	(systray.o): New target.
	* systray.c: New file.
	* systray.h: New file.

Index: configure.in
===================================================================
RCS file: /sources/emacs/emacs/configure.in,v
retrieving revision 1.490
diff -u -r1.490 configure.in
--- configure.in	6 Jan 2008 11:49:25 -0000	1.490
+++ configure.in	12 Jan 2008 02:09:06 -0000
@@ -4,7 +4,7 @@
 dnl in the directory containing this script.
 dnl
 dnl  Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2003,
-dnl    2004, 2005, 2006, 2007  Free Software Foundation, Inc.
+dnl    2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
 dnl
 dnl  This file is part of GNU Emacs.
 dnl
@@ -1777,6 +1777,15 @@
               [Define to 1 if GTK has both file selection and chooser dialog.])
   fi
 
+  dnl  Check if we have GtkStatusIcon.
+  HAVE_GTK_STATUS_ICON=no
+  AC_CHECK_FUNCS(gtk_status_icon_new, HAVE_GTK_STATUS_ICON=yes)
+
+  if test "$HAVE_GTK_STATUS_ICON" = yes; then
+    AC_DEFINE(HAVE_GTK_STATUS_ICON, 1,
+              [Define to 1 if GTK has GtkStatusIcon.])
+  fi
+
   dnl Check if pthreads are available.  Emacs only needs this when using
   dnl gtk_file_chooser under Gnome.
   if test "$HAVE_GTK_FILE_CHOOSER" = yes; then
@@ -1807,6 +1816,14 @@
    fi
 fi
 
+dnl  Check if we have libnotify.
+PKG_CHECK_MODULES(LIBNOTIFY, libnotify >= 0.4.1, HAVE_LIBNOTIFY=yes,
+                  HAVE_LIBNOTIFY=no)
+if test "$HAVE_LIBNOTIFY" = yes; then
+  AC_DEFINE(HAVE_LIBNOTIFY, 1,
+            [Define to 1 if the system has libnotify.])
+fi
+
 ### Link with -lXft if available to work around a bug.
 HAVE_XFT=maybe
 if test "${HAVE_GTK}" = "yes"; then
Index: doc/lispref/frames.texi
===================================================================
RCS file: /sources/emacs/emacs/doc/lispref/frames.texi,v
retrieving revision 1.5
diff -u -r1.5 frames.texi
--- doc/lispref/frames.texi	28 Dec 2007 22:26:13 -0000	1.5
+++ doc/lispref/frames.texi	12 Jan 2008 02:09:07 -0000
@@ -1,7 +1,7 @@
 @c -*-texinfo-*-
 @c This is part of the GNU Emacs Lisp Reference Manual.
 @c Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1998, 1999, 2001,
-@c   2002, 2003, 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
+@c   2002, 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
 @c See the file elisp.texi for copying conditions.
 @setfilename ../../info/frames
 @node Frames, Positions, Windows, Top
@@ -75,6 +75,7 @@
 * Text Terminal Colors::        Defining colors for text-only terminals.
 * Resources::		        Getting resource values from the server.
 * Display Feature Testing::     Determining the features of a terminal.
+* Status Icons::                Status icons and notifications.
 @end menu
 
   @xref{Display}, for information about the related topic of
@@ -2218,6 +2219,128 @@
 width and height of an X Window frame, measured in pixels.
 @end ignore
 
+@node Status Icons
+@section Status Icons and Notifications
+@cindex status icons
+@cindex system tray
+
+Some platforms provide a ``system tray'', an area holding icons tied
+to particular applications.  Applications use these icons to
+unobtrusively communicate information to the user.  Status icons also
+support ``notifications'', which are pop-up windows displaying some
+text and perhaps having some buttons.  A notification is less
+intrusive than a dialog because it does not steal the focus and need
+not be manually dismissed.  Emacs can create and manipulate status
+icons, and send notifications.
+
+If status icons are available, the feature @code{systray} is provided.
+
+@defun make-status-icon &optional alist
+This function creates and returns a new status icon.  The optional
+argument @var{alist} provides initial properties for the icon.  Some
+properties are specially recognized; unrecognized properties are
+retained (for use by @code{status-icon-parameters}) but otherwise
+ignored.
+
+The recognized properties are:
+@table @code
+@item icon-name
+The value is the file name of the image to display on the icon.
+@code{nil} means that Emacs should display a default image.
+
+@item blinking
+If the value is non-@code{nil}, the icon will blink.  The default is
+@code{nil}.
+
+@item visible
+If the value is @code{nil}, the icon will not be shown.  The default
+is @code{t}.
+
+@item help-echo
+The value is a string which will displayed as a tooltip for the icon.
+
+@item click-callback
+The value is a no-argument function which will be called when the user
+clicks on the icon.
+
+@item menu
+The value is a menu (of any kind recognized by @code{popup-menu})
+which will be displayed when the user right-clicks on the icon.
+@end table
+@end defun
+
+@defun delete-status-icon icon
+Destroy @var{icon}.
+@end defun
+
+@defun modify-status-icon-parameters icon alist
+Modify the parameters of @var{icon} according.  @var{alist} is an
+alist of parameters of the same kind accepted by
+@code{make-status-icon}.
+@end defun
+
+@defun status-iconp object
+Return non-@code{nil} if @var{object} is a status icon.
+@end defun
+
+@defun status-icon-live-p object
+Return non-@code{nil} if @var{object} is an undestroyed status icon.
+@end defun
+
+@defun status-icon-list
+Return a list of all undestroyed status icons.
+@end defun
+
+@defun show-status-icon-message icon summary &rest args
+This function creates and displays a new notification.  Note that this
+function may not be available on all platforms, and can be missing
+even if the basic status icon support is available.
+
+@var{icon} is the status icon to which the notification should attach.
+If @var{icon} is @code{nil}, then the notification will not be
+attached to an icon, but instead will display in some default
+location.
+
+@var{summary} is the summary text of the notification, a string.
+
+@var{args}, if given, is a list of key-value pairs.  The recognized
+keys and their meanings are:
+@table @code
+@item :body @var{string}
+Set the body text of the notification.  This is ordinarily displayed
+in a different font than the summary text.
+
+@item :timeout @var{value}
+By default a notification will time out.  The default timeout interval
+is system-specific.  If @var{value} is @code{nil}, the default is
+used; if it is @code{t}, the notification will never time out.
+Otherwise, if @var{value} is an integer, then it specifies the amount
+of time in milliseconds.
+
+@item :urgency @var{value}
+Set the urgency level.  @var{value} is a symbol, one of @samp{low},
+@samp{normal}, @samp{critical}.  The default is @samp{normal}.  This
+setting may affect how the notification is displayed; for instance a
+@samp{critical} notification may have a red background.
+
+@item :icon @var{value}
+Set the notification's icon.  @samp{value} is a symbol, currently one
+of @samp{warning}, @samp{info}, @samp{question}, @samp{error}.  The
+default is @samp{info}.
+
+@item :action (@var{label} . @var{function})
+Add an action to the notification, with label @var{label}.  An action
+typically appears on the notification as a small button.  Multiple
+actions can be added, to give the user a choice of how to react to a
+given event.  @var{function} is called with no arguments if the action
+is chosen.
+
+@item :closed-callback @var{function}
+Call @var{function} with no arguments when the notification is closed.
+This is not called when the user clicks an action button.
+@end table
+@end defun
+
 @ignore
    arch-tag: 94977df6-3dca-4730-b57b-c6329e9282ba
 @end ignore
Index: src/Makefile.in
===================================================================
RCS file: /sources/emacs/emacs/src/Makefile.in,v
retrieving revision 1.361
diff -u -r1.361 Makefile.in
--- src/Makefile.in	2 Dec 2007 16:23:40 -0000	1.361
+++ src/Makefile.in	12 Jan 2008 02:09:08 -0000
@@ -1,6 +1,6 @@
 # Makefile for GNU Emacs.
 # Copyright (C) 1985, 1987, 1988, 1993, 1994, 1995, 1999, 2000, 2001, 2002,
-#               2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+#               2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 # This file is part of GNU Emacs.
 
@@ -268,6 +268,11 @@
 DBUS_OBJ = dbusbind.o
 #endif
 
+#ifdef HAVE_LIBNOTIFY
+LIBNOTIFY_CFLAGS = @LIBNOTIFY_CFLAGS@
+LIBNOTIFY_LIBS = @LIBNOTIFY_LIBS@
+#endif
+
 /* DO NOT use -R.  There is a special hack described in lastfile.c
    which is used instead.  Some initialized data areas are modified
    at initial startup, then labeled as part of the text area when
@@ -281,7 +286,7 @@
 
 /* C_SWITCH_X_SITE must come before C_SWITCH_X_MACHINE and C_SWITCH_X_SYSTEM
    since it may have -I options that should override those two.  */
-ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(TOOLKIT_DEFINES) $(MYCPPFLAGS) -I. -I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_SITE C_SWITCH_X_SITE C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS_SOUND} ${RSVG_CFLAGS} ${DBUS_CFLAGS} ${CFLAGS}
+ALL_CFLAGS=-Demacs -DHAVE_CONFIG_H $(TOOLKIT_DEFINES) $(MYCPPFLAGS) -I. -I${srcdir} C_SWITCH_MACHINE C_SWITCH_SYSTEM C_SWITCH_SITE C_SWITCH_X_SITE C_SWITCH_X_MACHINE C_SWITCH_X_SYSTEM C_SWITCH_SYSTEM_TEMACS ${CFLAGS_SOUND} ${RSVG_CFLAGS} ${DBUS_CFLAGS} ${LIBNOTIFY_CFLAGS} ${CFLAGS}
 .c.o:
 	$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
 
@@ -309,7 +314,7 @@
 #ifdef HAVE_MENUS
 
 #ifdef USE_GTK
-GTK_OBJ= gtkutil.o
+GTK_OBJ= gtkutil.o systray.o
 #endif
 
 /* The X Menu stuff is present in the X10 distribution, but missing
@@ -945,6 +950,7 @@
    duplicated symbols.  If the standard libraries were compiled
    with GCC, we might need gnulib again after them.  */
 LIBES = $(LOADLIBES) $(LIBS) $(LIBX) $(LIBSOUND) $(RSVG_LIBS) $(DBUS_LIBS) \
+   $(LIBNOTIFY_LIBS) \
    LIBGPM LIBRESOLV LIBS_SYSTEM LIBS_MACHINE LIBS_TERMCAP \
    LIBS_DEBUG $(GETLOADAVG_LIBS) $(GNULIB_VAR) LIB_MATH LIB_STANDARD \
    $(GNULIB_VAR)
@@ -1190,6 +1196,7 @@
 sysdep.o: sysdep.c syssignal.h systty.h systime.h syswait.h blockinput.h \
    process.h dispextern.h termhooks.h termchar.h termopts.h \
    frame.h atimer.h window.h msdos.h dosfns.h keyboard.h cm.h $(config_h)
+systray.o: systray.c $(config_h) lisp.h frame.h
 term.o: term.c termchar.h termhooks.h termopts.h $(config_h) cm.h frame.h \
    disptab.h dispextern.h keyboard.h charset.h coding.h ccl.h msdos.h \
    window.h keymap.h blockinput.h atimer.h systime.h
Index: src/alloc.c
===================================================================
RCS file: /sources/emacs/emacs/src/alloc.c,v
retrieving revision 1.432
diff -u -r1.432 alloc.c
--- src/alloc.c	16 Nov 2007 21:24:59 -0000	1.432
+++ src/alloc.c	12 Jan 2008 02:09:08 -0000
@@ -1,6 +1,6 @@
 /* Storage allocation and gc for GNU Emacs Lisp interpreter.
    Copyright (C) 1985, 1986, 1988, 1993, 1994, 1995, 1997, 1998, 1999,
-      2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007  Free Software Foundation, Inc.
+      2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -56,6 +56,7 @@
 #include "charset.h"
 #include "syssignal.h"
 #include "termhooks.h"		/* For struct terminal.  */
+#include "systray.h"
 #include <setjmp.h>
 
 /* GC_MALLOC_CHECK defined means perform validity checks of malloc'd
@@ -3021,6 +3022,17 @@
   return f;
 }
 
+struct status_icon *
+allocate_status_icon ()
+{
+  struct status_icon *s = ALLOCATE_PSEUDOVECTOR (struct status_icon,
+						 notification_alist,
+						 PVEC_STATUS_ICON);
+  /* Zero out the non-GC'd fields.  FIXME: This should be made unnecessary.  */
+  bzero (&(s->param_alist),
+	 ((char*)(s+1)) - ((char*)&(s->notification_alist)));
+  return s;
+}
 
 struct Lisp_Process *
 allocate_process ()
Index: src/emacs.c
===================================================================
RCS file: /sources/emacs/emacs/src/emacs.c,v
retrieving revision 1.413
diff -u -r1.413 emacs.c
--- src/emacs.c	2 Dec 2007 16:23:40 -0000	1.413
+++ src/emacs.c	12 Jan 2008 02:09:08 -0000
@@ -1,6 +1,6 @@
 /* Fully extensible Emacs, running on Unix, intended for GNU.
    Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1997, 1998, 1999,
-                 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+                 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -1643,6 +1643,10 @@
       syms_of_dbusbind ();
 #endif /* HAVE_DBUS */
 
+#ifdef HAVE_GTK
+      syms_of_systray ();
+#endif
+
 #ifdef SYMS_SYSTEM
       SYMS_SYSTEM;
 #endif
Index: src/keyboard.c
===================================================================
RCS file: /sources/emacs/emacs/src/keyboard.c,v
retrieving revision 1.937
diff -u -r1.937 keyboard.c
--- src/keyboard.c	6 Jan 2008 21:34:57 -0000	1.937
+++ src/keyboard.c	12 Jan 2008 02:09:09 -0000
@@ -500,6 +500,8 @@
 #ifdef HAVE_DBUS
 Lisp_Object Qdbus_event;
 #endif
+Lisp_Object Qstatus_icon_click_event;
+
 /* Lisp_Object Qmouse_movement; - also an event header */
 
 /* Properties of event headers.  */
@@ -4298,6 +4300,11 @@
 	  kbd_fetch_ptr = event + 1;
 	}
 #endif
+      else if (event->kind == STATUS_ICON_CLICK_EVENT)
+	{
+	  obj = make_lispy_event (event);
+	  kbd_fetch_ptr = event + 1;
+	}
       else
 	{
 	  /* If this event is on a different frame, return a switch-frame this
@@ -6174,6 +6181,9 @@
       }
 #endif /* HAVE_DBUS */
 
+    case STATUS_ICON_CLICK_EVENT:
+      return Fcons (Qstatus_icon_click_event, event->arg);
+
 #ifdef HAVE_GPM
     case GPM_CLICK_EVENT:
       {
@@ -11781,6 +11791,9 @@
   staticpro (&Qdbus_event);
 #endif
 
+  Qstatus_icon_click_event = intern ("status-icon-click-event");
+  staticpro (&Qstatus_icon_click_event);
+
   Qmenu_enable = intern ("menu-enable");
   staticpro (&Qmenu_enable);
   Qmenu_alias = intern ("menu-alias");
@@ -12517,6 +12530,9 @@
   initial_define_lispy_key (Vspecial_event_map, "dbus-event",
 			    "dbus-handle-event");
 #endif
+
+  initial_define_lispy_key (Vspecial_event_map, "status-icon-click-event",
+			    "status-icon-handle-click-event");
 }
 
 /* Mark the pointers in the kboard objects.
Index: src/lisp.h
===================================================================
RCS file: /sources/emacs/emacs/src/lisp.h,v
retrieving revision 1.603
diff -u -r1.603 lisp.h
--- src/lisp.h	22 Nov 2007 01:01:26 -0000	1.603
+++ src/lisp.h	12 Jan 2008 02:09:10 -0000
@@ -349,7 +349,8 @@
   PVEC_HASH_TABLE = 0x40000,
   PVEC_TERMINAL = 0x80000,
   PVEC_OTHER = 0x100000,
-  PVEC_TYPE_MASK = 0x1ffe00
+  PVEC_STATUS_ICON = 0x200000,
+  PVEC_TYPE_MASK = 0x2ffe00
 
 #if 0 /* This is used to make the value of PSEUDOVECTOR_FLAG available to
 	 GDB.  It doesn't work on OS Alpha.  Moved to a variable in
@@ -1552,6 +1553,8 @@
 #define GC_BOOL_VECTOR_P(x) GC_PSEUDOVECTORP (x, PVEC_BOOL_VECTOR)
 #define FRAMEP(x) PSEUDOVECTORP (x, PVEC_FRAME)
 #define GC_FRAMEP(x) GC_PSEUDOVECTORP (x, PVEC_FRAME)
+#define STATUS_ICONP(x) PSEUDOVECTORP (x, PVEC_STATUS_ICON)
+#define GC_STATUS_ICONP(x) GC_PSEUDOVECTORP (x, PVEC_STATUS_ICON)
 
 #define SUB_CHAR_TABLE_P(x) (CHAR_TABLE_P (x) && NILP (XCHAR_TABLE (x)->top))
 
@@ -2616,6 +2620,7 @@
 extern struct Lisp_Hash_Table *allocate_hash_table P_ ((void));
 extern struct window *allocate_window P_ ((void));
 extern struct frame *allocate_frame P_ ((void));
+extern struct status_icon *allocate_status_icon P_ ((void));
 extern struct Lisp_Process *allocate_process P_ ((void));
 extern struct terminal *allocate_terminal P_ ((void));
 extern int gc_in_progress;
@@ -3248,6 +3253,9 @@
 extern void syms_of_sound P_ ((void));
 extern void init_sound P_ ((void));
 
+/* Defined in systray.c */
+extern void syms_of_systray P_ ((void));
+
 /* Defined in category.c */
 extern void init_category_once P_ ((void));
 extern void syms_of_category P_ ((void));
Index: src/print.c
===================================================================
RCS file: /sources/emacs/emacs/src/print.c,v
retrieving revision 1.243
diff -u -r1.243 print.c
--- src/print.c	22 Nov 2007 16:16:02 -0000	1.243
+++ src/print.c	12 Jan 2008 02:09:10 -0000
@@ -2031,6 +2031,8 @@
 	  strout (buf, -1, -1, printcharfun, 0);
 	  PRINTCHAR ('>');
 	}
+      else if (STATUS_ICONP (obj))
+	strout ("#<status-icon>", -1, -1, printcharfun, 0);
       else
 	{
 	  EMACS_INT size = XVECTOR (obj)->size;
Index: src/systray.c
===================================================================
RCS file: src/systray.c
diff -N src/systray.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/systray.c	12 Jan 2008 02:09:10 -0000
@@ -0,0 +1,667 @@
+/* Elisp bindings system tray icons
+   Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs 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 3, or (at your option)
+any later version.
+
+GNU Emacs 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+#include "config.h"
+
+#include "lisp.h"
+#include "systray.h"
+#include "frame.h"
+#include "charset.h"
+#include "coding.h"
+#include "blockinput.h"
+#include "termhooks.h"
+#include "xterm.h"
+
+#include <gtk/gtk.h>
+
+#ifdef HAVE_LIBNOTIFY
+#include <libnotify/notify.h>
+#endif
+
+#ifdef HAVE_GTK_STATUS_ICON
+
+/* FIXME: should be in a header.  */
+extern Lisp_Object Qhelp_echo;
+extern Lisp_Object Qmenu;
+extern Lisp_Object Qstatus_icon_click_event;
+
+Lisp_Object Vstatus_icon_list;
+Lisp_Object Vstandalone_icon;
+
+Lisp_Object Qblinking;
+Lisp_Object Qstatus_icon_live_p;
+Lisp_Object Qclick_callback;
+Lisp_Object QCbody, QCtimeout, QCurgency, QCicon, QCaction, QCclosed_callback;
+Lisp_Object Qlow, Qcritical, Qwarning, Qquestion, Qerror;
+Lisp_Object Qpopup_menu;
+
+#define GET_ICON(s) ((GtkStatusIcon *) ((s)->icon))
+
+/* Callback which is called when the user clicks on a status icon.  */
+static void
+activate_icon (GtkWidget *widget, gpointer data)
+{
+  Lisp_Object icon = (Lisp_Object) data;
+
+  /* Just ignore garbage.  */
+  if (STATUS_ICON_LIVE_P (XSTATUS_ICON (icon)))
+    {
+      struct status_icon *sicon = XSTATUS_ICON (icon);
+      Lisp_Object elt = Fassq (Qmenu, sicon->param_alist);
+      if (CONSP (elt) && FUNCTIONP (XCDR (elt)))
+	{
+	  struct input_event event;
+	  EVENT_INIT (event);
+	  event.kind = STATUS_ICON_CLICK_EVENT;
+	  event.frame_or_window = Qnil;
+	  event.arg = XCDR (elt);
+	  kbd_buffer_store_event (&event);
+	}
+    }
+}
+
+static void
+pop_up_status_menu (GtkStatusIcon *gicon, guint button,
+		    guint activate_time, gpointer user_data)
+{
+  Lisp_Object icon = (Lisp_Object) user_data;
+
+  /* Just ignore garbage.  */
+  if (STATUS_ICON_LIVE_P (XSTATUS_ICON (icon)))
+    {
+      struct status_icon *sicon = XSTATUS_ICON (icon);
+      Lisp_Object elt = Fassq (Qmenu, sicon->param_alist);
+      if (CONSP (elt) && ! NILP (XCDR (elt)))
+	{
+	  struct gcpro gcpro1;
+	  struct input_event event;
+	  Lisp_Object function;
+
+	  GCPRO1 (function);
+	  /* Construct the 'position' list.  */
+	  function = Fcons (icon,
+			    Fcons (make_number (button), Qnil));
+	  function = Fcons (Qquote,
+			    Fcons (function, Qnil));
+	  /* Construct the call to `popup-menu'.  */
+	  function = Fcons (Qpopup_menu,
+			    Fcons (Fcons (Qquote,
+					  Fcons (XCDR (elt), Qnil)),
+				   Fcons (function, Qnil)));
+	  /* Construct the function.  */
+	  function = Fcons (Qlambda,
+			    Fcons (Qnil,
+				   Fcons (function, Qnil)));
+
+	  EVENT_INIT (event);
+	  event.kind = STATUS_ICON_CLICK_EVENT;
+	  event.frame_or_window = Qnil;
+	  event.arg = function;
+	  kbd_buffer_store_event (&event);
+	}
+    }
+}
+
+/* Apply property KEY with value VALUE to icon SICON.  */
+static void
+apply_one_icon_property (struct status_icon *sicon, Lisp_Object key,
+			 Lisp_Object value)
+{
+  if (EQ (key, Qicon_name))
+    {
+      struct gcpro gcpro1;
+      Lisp_Object filename = Qnil;
+      GCPRO1 (filename);
+      if (NILP (value))
+	{
+	  /* Default to the GNU.  */
+	  filename = build_string ("emacs_48.png");
+	}
+      else
+	{
+	  CHECK_STRING (value);
+	  filename = value;
+	}
+      /* Not clear if x_find_image_file returns a string with the
+	 correct encoding for Gtk.  However, it is already used this
+	 way in gtkutil.c.  */
+      filename = x_find_image_file (filename);
+      if (NILP (filename))
+	{
+	  /* No good choice, so choose a semi-appropriate stock
+	     icon.  */
+	  gtk_status_icon_set_from_stock (GET_ICON (sicon),
+					  GTK_STOCK_DIALOG_QUESTION);
+	}
+      else
+	gtk_status_icon_set_from_file (GET_ICON (sicon),
+				       (char *) SDATA (filename));
+      UNGCPRO;
+    }
+  else if (EQ (key, Qblinking))
+    gtk_status_icon_set_blinking (GET_ICON (sicon), ! NILP (value));
+  else if (EQ (key, Qvisible))
+    gtk_status_icon_set_visible (GET_ICON (sicon), ! NILP (value));
+  else if (EQ (key, Qhelp_echo))
+    {
+      struct gcpro gcpro1;
+      Lisp_Object tooltip = Qnil;
+      GCPRO1 (tooltip);
+      if (NILP (value))
+	gtk_status_icon_set_tooltip (GET_ICON (sicon), NULL);
+      else
+	{
+	  /* FIXME: should accept a sexp to eval here.  */
+	  CHECK_STRING (value);
+	  tooltip = ENCODE_UTF_8 (value);
+	  gtk_status_icon_set_tooltip (GET_ICON (sicon),
+				       (char *) SDATA (tooltip));
+	}
+      UNGCPRO;
+    }
+  else
+    {
+      /* Ignore things we don't understand.  */
+    }
+}
+
+/* Apply the ALIST of properties to status icon ICON.  */
+static void
+apply_status_icon_alist (Lisp_Object icon, Lisp_Object alist)
+{
+  Lisp_Object tail;
+  struct status_icon *sicon;
+
+  CHECK_LIVE_STATUS_ICON (icon);
+  sicon = XSTATUS_ICON (icon);
+
+  for (tail = alist; CONSP (tail); tail = XCDR (tail))
+    {
+      Lisp_Object key, value;
+      Lisp_Object elt = XCAR (tail);
+      if (! CONSP (elt))
+	continue;
+      key = XCAR (elt);
+      value = XCDR (elt);
+
+      apply_one_icon_property (sicon, key, value);
+
+      /* Update frame parameters.  */
+      elt = Fassq (key, sicon->param_alist);
+      if (NILP (elt))
+	sicon->param_alist = Fcons (Fcons (key, value), sicon->param_alist);
+      else
+	Fsetcdr (elt, value);
+    }
+}
+
+DEFUN ("make-status-icon", Fmake_status_icon, Smake_status_icon,
+       0, 1, 0,
+       doc: /* Return a newly created status icon.
+Optional argument ALIST is an alist of parameters for the new frame.
+See `modify-status-icon-parameters' for a list of recognized parameters.*/)
+     (alist)
+     Lisp_Object alist;
+{
+  struct status_icon *sicon;
+  Lisp_Object icon, deflist;
+  struct gcpro gcpro1;
+
+  check_x ();
+
+  BLOCK_INPUT;
+
+  sicon = allocate_status_icon ();
+  XSETSTATUS_ICON (icon, sicon);
+  sicon->param_alist = Qnil;
+  sicon->notification_alist = Qnil;
+  sicon->icon = gtk_status_icon_new ();
+
+  g_signal_connect (sicon->icon, "activate", G_CALLBACK (activate_icon),
+		    (gpointer) icon);
+  g_signal_connect (sicon->icon, "popup-menu", G_CALLBACK (pop_up_status_menu),
+		    (gpointer) icon);
+
+  Vstatus_icon_list = Fcons (icon, Vstatus_icon_list);
+
+  GCPRO1 (deflist);
+
+  /* Make some defaults.  */
+  deflist = Fcons (Fcons (Qclick_callback, Qnil),
+		   Fcons (Fcons (Qicon_name, Qnil),
+			  Fcons (Fcons (Qblinking, Qnil),
+				 Fcons (Fcons (Qvisible, Qt),
+					Fcons (Fcons (Qhelp_echo, Qnil),
+					       alist)))));
+  apply_status_icon_alist (icon, deflist);
+
+  UNBLOCK_INPUT;
+
+  RETURN_UNGCPRO (icon);
+}
+
+DEFUN ("modify-status-icon-parameters", Fmodify_status_icon_parameters,
+       Smodify_status_icon_parameters,
+       2, 2, 0,
+       doc: /* Modify the parameters of status icon ICON according to ALIST.
+Each element of alist has the form (PARM . VALUE), where PARM is a symbol.
+Undefined PARMs are ignored, but stored in the frame's parameter list
+so that `status-icon-parameters' will return them.
+
+Currently defined parameters and their values are:
+
+  icon-name       The file name of an icon to display.
+  blinking        If non-nil, the status icon will blink.
+  visible         If nil, status icon is invisible.
+  help-echo       A string which will be displayed as a tooltip for the
+                  status icon.
+  click-callback  A no-argument function which will be called when the
+                  user clicks on the icon.
+  menu            A menu which will pop up when the user clicks on the
+                  icon.  May not be available on all platforms.*/)
+     (icon, alist)
+     Lisp_Object icon, alist;
+{
+  BLOCK_INPUT;
+  apply_status_icon_alist (icon, alist);
+  UNBLOCK_INPUT;
+  return Qnil;
+}
+
+DEFUN ("status-icon-parameters", Fstatus_icon_parameters,
+       Sstatus_icon_parameters,
+       1, 1, 0,
+       doc: /* Return the parameters-alist of the status icon ICON.
+It is a list of elements of the form (PARM . VALUE), where PARM is a symbol.*/)
+     (icon)
+     Lisp_Object icon;
+{
+  struct status_icon *sicon;
+  CHECK_LIVE_STATUS_ICON (icon);
+  sicon = XSTATUS_ICON (icon);
+  return Fcopy_alist (sicon->param_alist);
+}
+
+DEFUN ("status-icon-list", Fstatus_icon_list, Sstatus_icon_list,
+       0, 0, 0,
+       doc: /* Return a list of all status icons.  */)
+     ()
+{
+  return Fcopy_sequence (Vstatus_icon_list);
+}
+
+DEFUN ("delete-status-icon", Fdelete_status_icon, Sdelete_status_icon,
+       1, 1, 0,
+       doc: /* Delete the status icon ICON.  */)
+     (icon)
+     Lisp_Object icon;
+{
+  struct status_icon *sicon;
+  CHECK_LIVE_STATUS_ICON (icon);
+  sicon = XSTATUS_ICON (icon);
+  BLOCK_INPUT;
+  g_object_unref (sicon->icon);
+  UNBLOCK_INPUT;
+  sicon->icon = NULL;
+  Vstatus_icon_list = Fdelq (icon, Vstatus_icon_list);
+  return Qnil;
+}
+
+#ifdef HAVE_LIBNOTIFY
+
+/* Called in response to notification action clicks.  */
+static void
+handle_notification_event (NotifyNotification *notification,
+			   int action_number, Lisp_Object callback_arg)
+{
+  Lisp_Object icon = XCAR (callback_arg);
+  struct status_icon *sicon = XSTATUS_ICON (icon);
+  Lisp_Object callback = Fnth (make_number (action_number + 2), callback_arg);
+  if (FUNCTIONP (callback))
+    {
+      struct input_event event;
+      EVENT_INIT (event);
+      event.kind = STATUS_ICON_CLICK_EVENT;
+      event.frame_or_window = Qnil;
+      event.arg = callback;
+      kbd_buffer_store_event (&event);
+    }
+  sicon->notification_alist = Fdelq (callback_arg,
+				     sicon->notification_alist);
+  g_object_unref (notification);
+}
+
+static void
+action_click (NotifyNotification *notification, gchar *actionid,
+	      gpointer user_data)
+{
+  int id = atoi (actionid);
+  Lisp_Object callback_arg = (Lisp_Object) user_data;
+  handle_notification_event (notification, id, callback_arg);
+}
+
+static void
+cleanup_notification (NotifyNotification *notification, gpointer user_data)
+{
+  Lisp_Object callback_arg = (Lisp_Object) user_data;
+  handle_notification_event (notification, -1, callback_arg);
+}
+
+DEFUN ("show-status-icon-message", Fshow_status_icon_message,
+       Sshow_status_icon_message,
+       2, MANY, 0,
+       doc: /* Post a notification message attached to the icon ICON.
+ICON is a status icon created by `make-status-icon', or nil for a
+standalone notification.
+SUMMARY is the message to display, a string.
+
+The remaining arguments, if any, are a property list specifying
+additional parameters of the notification:
+
+  :body STRING     Set the body text of the notification.
+  :timeout VALUE   Set the timeout value.
+                   nil means use the default.
+		   t means never time out.
+		   An integer specifies the timeout in milliseconds.
+  :urgency VALUE   Set the urgency level.  VALUE is a symbol, one of
+                   'low', 'normal', 'critical'.  The default is 'normal'.
+  :icon VALUE      Set the notification icon.  VALUE is a symbol, currently
+                   one of 'warning', 'info', 'question', 'error'.  The
+		   default is 'info'.
+  :action (LABEL . FUNCTION)
+                   Add an action to the notification, with label LABEL.
+		   FUNCTION is called with no arguments if the action
+		   is chosen.
+  :closed-callback FUNCTION
+                   Call FUNCTION with no arguments when the notification
+		   is closed.  This is not called when the user clicks
+		   an action button.
+
+usage: (show-status-icon-message ICON SUMMARY &rest ARGS)  */)
+     (nargs, args)
+     int nargs;
+     Lisp_Object *args;
+{
+  struct status_icon *sicon;
+  struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+  Lisp_Object gc_temp = Qnil;
+  Lisp_Object callback_list = Qnil;
+  Lisp_Object closed_callback = Qnil;
+  Lisp_Object callback_arg = Qnil;
+  NotifyNotification *notification;
+
+  int i;
+  int action_count = 0;
+  Lisp_Object icon = args[0];
+  Lisp_Object summary = args[1];
+
+  check_x ();
+  if ((nargs - 2) % 2 != 0)
+    error ("Invalid number of arguments");
+
+  if (! NILP (icon))
+    CHECK_LIVE_STATUS_ICON (icon);
+  CHECK_STRING (summary);
+
+  BLOCK_INPUT;
+
+  if (! notify_is_initted () && ! notify_init ("Emacs"))
+    {
+      UNBLOCK_INPUT;
+      error ("Couldn't connect to notification server");
+    }
+
+  GCPRO4 (gc_temp, callback_list, closed_callback, callback_arg);
+  gc_temp = ENCODE_UTF_8 (summary);
+
+  /* Pre-allocate the cons that will be passed to all callbacks.  We
+     will fill it in later.  */
+  callback_arg = Fcons (Qnil, Qnil);
+
+  if (! NILP (icon))
+    {
+      sicon = XSTATUS_ICON (icon);
+      notification
+	= notify_notification_new_with_status_icon (SDATA (gc_temp), NULL,
+						    GTK_STOCK_DIALOG_INFO,
+						    GET_ICON (XSTATUS_ICON (icon)));
+    }
+  else
+    {
+      icon = Vstandalone_icon;
+      sicon = XSTATUS_ICON (icon);
+      notification
+	= notify_notification_new (SDATA (gc_temp), NULL, GTK_STOCK_DIALOG_INFO,
+				   NULL);
+    }
+
+  for (i = 2; i < nargs; i += 2)
+    {
+      Lisp_Object key = args[i];
+      Lisp_Object value = args[i + 1];
+      /* FIXME: still no access to: setting the icon from a pixbuf,
+	 maybe hints (what are they good for?), closing the
+	 notification by caller.  */
+      if (EQ (key, QCbody))
+	{
+	  /* Ignore things we don't understand.  */
+	  if (STRINGP (value))
+	    {
+	      gc_temp = ENCODE_UTF_8 (value);
+	      g_object_set (G_OBJECT (notification), "body", SDATA (gc_temp),
+			    NULL);
+	    }
+	}
+      else if (EQ (key, QCtimeout))
+	{
+	  gint timeout = NOTIFY_EXPIRES_DEFAULT;
+	  if (EQ (value, Qt))
+	    timeout = NOTIFY_EXPIRES_NEVER;
+	  else if (INTEGERP (value))
+	    {
+	      timeout = XINT (value);
+	      if (timeout <= 0)
+		timeout = NOTIFY_EXPIRES_DEFAULT;
+	    }
+	  notify_notification_set_timeout (notification, timeout);
+	}
+      else if (EQ (key, QCurgency))
+	{
+	  NotifyUrgency urgency = NOTIFY_URGENCY_NORMAL;
+	  if (EQ (value, Qlow))
+	    urgency = NOTIFY_URGENCY_LOW;
+	  else if (EQ (value, Qcritical))
+	    urgency = NOTIFY_URGENCY_CRITICAL;
+	  notify_notification_set_urgency (notification, urgency);
+	}
+      else if (EQ (key, QCicon))
+	{
+	  char *icon_name = GTK_STOCK_DIALOG_INFO;
+	  if (EQ (value, Qwarning))
+	    icon_name = GTK_STOCK_DIALOG_WARNING;
+	  else if (EQ (value, Qquestion))
+	    icon_name = GTK_STOCK_DIALOG_QUESTION;
+	  else if (EQ (value, Qerror))
+	    icon_name = GTK_STOCK_DIALOG_ERROR;
+	  g_object_set (G_OBJECT (notification), "icon-name", icon_name, NULL);
+	}
+      else if (EQ (key, QCaction))
+	{
+	  /* Ignore if we don't understand the value.  */
+	  if (CONSP (value) && STRINGP (XCAR (value)))
+	    {
+	      char actionid[20];
+	      Lisp_Object label = XCAR (value);
+	      gc_temp = ENCODE_UTF_8 (label);
+	      /* Use a plain integer for the action id, because we
+		 will use it later to look up the action.  */
+	      sprintf (actionid, "%d", action_count);
+	      ++action_count;
+	      notify_notification_add_action (notification, actionid,
+					      SDATA (gc_temp),
+					      action_click,
+					      (gpointer) callback_arg, NULL);
+
+	      /* We build the list from the back, the reverse it
+		 later.  */
+	      callback_list = Fcons (XCDR (value), callback_list);
+	    }
+	}
+      else if (EQ (key, QCclosed_callback))
+	closed_callback = value;
+    }
+
+  /* Put the callback list into the right order, then put the 'closed'
+     callback at the start of the list.  */
+  callback_list = Fcons (closed_callback, Fnreverse (callback_list));
+
+  XSETCAR (callback_arg, icon);
+  XSETCDR (callback_arg, callback_list);
+  sicon->notification_alist = Fcons (callback_arg, sicon->notification_alist);
+
+  /* Arrange to clean up when this notification goes away.  */
+  g_signal_connect (G_OBJECT (notification), "closed",
+		    G_CALLBACK (cleanup_notification),
+		    (gpointer) callback_arg);
+
+  /* Ignore errors here for now.  */
+  notify_notification_show (notification, NULL);
+
+  UNBLOCK_INPUT;
+
+  RETURN_UNGCPRO (Qnil);
+}
+
+#endif /* HAVE_LIBNOTIFY */
+
+DEFUN ("status-iconp", Fstatus_iconp, Sstatus_iconp,
+       1, 1, 0, doc: /* Return non-nil if OBJECT is a status icon.  */)
+     (object)
+     Lisp_Object object;
+{
+  return STATUS_ICONP (object) ? Qt : Qnil;
+}
+
+DEFUN ("status-icon-live-p", Fstatus_icon_live_p, Sstatus_icon_live_p,
+       1, 1, 0,
+       doc: /* Return non-nil if OBJECT is a status icon which has not been deleted.*/)
+     (object)
+     Lisp_Object object;
+{
+  return ((STATUS_ICONP (object) && STATUS_ICON_LIVE_P (XSTATUS_ICON (object)))
+	  ? Qt : Qnil);
+}
+
+DEFUN ("status-icon-handle-click-event", Fstatus_icon_handle_click_event,
+       Sstatus_icon_handle_click_event,
+       1, 1, "e",
+       doc: /* Internal handler for status icon click events.
+A status icon click event is generated in response to the user clicking
+on a status icon.  This handler calls the specified callback function,
+if any.*/)
+     (event)
+     Lisp_Object event;
+{
+  Lisp_Object fun;
+  struct gcpro gcpro1;
+
+  /* Just ignore garbage.  */
+  if (! CONSP (event) || ! EQ (XCAR (event), Qstatus_icon_click_event)
+      || ! FUNCTIONP (XCDR (event)))
+    return Qnil;
+
+  GCPRO1 (fun);
+  fun = XCDR (event);
+  Ffuncall (1, &fun);
+  RETURN_UNGCPRO (Qnil);
+}
+
+void
+syms_of_systray ()
+{
+  struct status_icon *sicon;
+
+  Qstatus_icon_live_p = intern ("status-icon-live-p");
+  staticpro (&Qstatus_icon_live_p);
+  Qblinking = intern ("blinking");
+  staticpro (&Qblinking);  
+  Qclick_callback = intern ("click-callback");
+  staticpro (&Qclick_callback);
+  QCbody = intern (":body");
+  staticpro (&QCbody);
+  QCtimeout = intern (":timeout");
+  staticpro (&QCtimeout);
+  QCurgency = intern (":urgency");
+  staticpro (&QCurgency);
+  QCicon = intern (":icon");
+  staticpro (&QCicon);
+  QCaction = intern (":action");
+  staticpro (&QCaction);
+  QCclosed_callback = intern (":closed-callback");
+  staticpro (&QCclosed_callback);
+  Qlow = intern ("low");
+  staticpro (&Qlow);
+  Qcritical = intern ("critical");
+  staticpro (&Qcritical);
+  Qwarning = intern ("warning");
+  staticpro (&Qwarning);
+  Qquestion = intern ("question");
+  staticpro (&Qquestion);
+  Qerror = intern ("error");
+  staticpro (&Qerror);
+  Qpopup_menu = intern ("popup-menu");
+  staticpro (&Qpopup_menu);
+
+  staticpro (&Vstatus_icon_list);
+  Vstatus_icon_list = Qnil;
+
+  defsubr (&Smake_status_icon);
+  defsubr (&Smodify_status_icon_parameters);
+  defsubr (&Sdelete_status_icon);
+  defsubr (&Sstatus_icon_parameters);
+  defsubr (&Sstatus_icon_list);
+#ifdef HAVE_LIBNOTIFY
+  defsubr (&Sshow_status_icon_message);
+#endif /* HAVE_LIBNOTIFY */
+  defsubr (&Sstatus_iconp);
+  defsubr (&Sstatus_icon_live_p);
+  defsubr (&Sstatus_icon_handle_click_event);
+
+  /* We make a dummy status icon so that standalone notifications have
+     a place to store their user-data.  This icon never has a GUI or
+     associated native state.  */
+  staticpro (&Vstandalone_icon);
+  sicon = allocate_status_icon ();
+  sicon->param_alist = Qnil;
+  sicon->notification_alist = Qnil;
+  XSETSTATUS_ICON (Vstandalone_icon, sicon);
+
+  Fprovide (intern ("systray"), Qnil);
+}
+
+#else /* HAVE_GTK_STATUS_ICON */
+
+void
+syms_of_systray ()
+{
+  /* Nothing.  */
+}
+
+#endif
Index: src/systray.h
===================================================================
RCS file: src/systray.h
diff -N src/systray.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/systray.h	12 Jan 2008 02:09:10 -0000
@@ -0,0 +1,56 @@
+/* Define systray object for GNU Emacs.
+   Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs 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 3, or (at your option)
+any later version.
+
+GNU Emacs 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.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+#ifndef EMACS_SYSTRAY_H
+#define EMACS_SYSTRAY_H
+
+extern Lisp_Object Qstatus_icon_live_p;
+
+struct status_icon
+{
+  EMACS_UINT size;
+  struct Lisp_Vector *next;
+
+  /* Parameter alist of this status icon.  */
+  Lisp_Object param_alist;
+
+  /* List holding objects we want to keep live while a notification is
+     up.  */
+  Lisp_Object notification_alist;
+
+  /* Beyond here, there should be no more Lisp_Object components.  */
+
+  /* The icon widget, or NULL if it has been destroyed.  Really a
+     'GtkStatusIcon *'.  */
+  void *icon;
+};
+
+#define XSTATUS_ICON(p) \
+  (eassert (GC_STATUS_ICONP (p)), (struct status_icon *) XPNTR (p))
+
+#define XSETSTATUS_ICON(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_STATUS_ICON))
+
+#define STATUS_ICON_LIVE_P(s) (((s)->icon) != 0)
+
+#define CHECK_LIVE_STATUS_ICON(s)					\
+  CHECK_TYPE (STATUS_ICONP (s) && STATUS_ICON_LIVE_P (XSTATUS_ICON (s)), \
+	      Qstatus_icon_live_p, s)
+
+#endif /* EMACS_SYSTRAY_H */
Index: src/termhooks.h
===================================================================
RCS file: /sources/emacs/emacs/src/termhooks.h,v
retrieving revision 1.90
diff -u -r1.90 termhooks.h
--- src/termhooks.h	2 Dec 2007 16:23:40 -0000	1.90
+++ src/termhooks.h	12 Jan 2008 02:09:10 -0000
@@ -201,6 +201,8 @@
   , DBUS_EVENT
 #endif
 
+  , STATUS_ICON_CLICK_EVENT
+
 #ifdef WINDOWSNT
   /* Generated when an APPCOMMAND event is received, in response to
      Multimedia or Internet buttons on some keyboards.
Index: src/xmenu.c
===================================================================
RCS file: /sources/emacs/emacs/src/xmenu.c,v
retrieving revision 1.324
diff -u -r1.324 xmenu.c
--- src/xmenu.c	13 Oct 2007 12:10:07 -0000	1.324
+++ src/xmenu.c	12 Jan 2008 02:09:10 -0000
@@ -1,6 +1,6 @@
 /* X Communication module for terminals which understand the X protocol.
    Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2002, 2003,
-                 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+                 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -51,6 +51,7 @@
 #include "charset.h"
 #include "coding.h"
 #include "sysselect.h"
+#include "systray.h"
 
 #ifdef MSDOS
 #include "msdos.h"
@@ -156,7 +157,7 @@
 				Lisp_Object, Lisp_Object));
 static int update_frame_menubar P_ ((struct frame *));
 static Lisp_Object xmenu_show P_ ((struct frame *, int, int, int, int,
-				   Lisp_Object, char **));
+				   Lisp_Object, char **, void *, int));
 static void keymap_panes P_ ((Lisp_Object *, int, int));
 static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
 				     int, int));
@@ -780,6 +781,9 @@
 corner of WINDOW.  (WINDOW may be a window or a frame object.)
 This controls the position of the top left of the menu as a whole.
 If POSITION is t, it means to use the current mouse position.
+POSITION can also be a list (STATUS-ICON BUTTON-TIME),
+where STATUS-ICON is a status icon and BUTTON is an integer.
+In this case the menu is displayed over the indicated status icon.
 
 MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.
 The menu items come from key bindings that have a menu string as well as
@@ -829,6 +833,8 @@
   int for_click = 0;
   int specpdl_count = SPECPDL_INDEX ();
   struct gcpro gcpro1;
+  void *status_icon = NULL;
+  int status_button = 0;
 
 #ifdef HAVE_MENUS
   if (! NILP (position))
@@ -843,6 +849,19 @@
 	{
           get_current_pos_p = 1;
         }
+      else if (CONSP (position) && STATUS_ICONP (XCAR (position)))
+	{
+	  Lisp_Object icon = XCAR (position);
+
+	  CHECK_LIVE_STATUS_ICON (icon);
+	  status_icon = XSTATUS_ICON (icon)->icon;
+	  status_button = XINT (Fcar (Fcdr (position)));
+	  for_click = 1;
+	  /* Placate later code.  */
+	  XSETINT (x, 0);
+	  XSETINT (y, 0);
+	  window = selected_window;
+	}
       else
 	{
 	  tem = Fcar (position);
@@ -1029,7 +1048,8 @@
   BLOCK_INPUT;
 
   selection = xmenu_show (f, xpos, ypos, for_click,
-			  keymaps, title, &error_name);
+			  keymaps, title, &error_name,
+			  status_icon, status_button);
   UNBLOCK_INPUT;
 
   discard_menu_items ();
@@ -2664,18 +2684,22 @@
    menu pops down.
    menu_item_selection will be set to the selection.  */
 static void
-create_and_show_popup_menu (f, first_wv, x, y, for_click)
+create_and_show_popup_menu (f, first_wv, x, y, for_click,
+			    status_icon, status_button)
      FRAME_PTR f;
      widget_value *first_wv;
      int x;
      int y;
      int for_click;
+     void *status_icon;
+     int status_button;
 {
   int i;
   GtkWidget *menu;
   GtkMenuPositionFunc pos_func = 0;  /* Pop up at pointer.  */
   struct next_popup_x_y popup_x_y;
   int specpdl_count = SPECPDL_INDEX ();
+  gpointer user_data = &popup_x_y;
 
   if (! FRAME_X_P (f))
     abort ();
@@ -2702,6 +2726,14 @@
 
       i = 0;  /* gtk_menu_popup needs this to be 0 for a non-button popup.  */
     }
+#ifdef HAVE_GTK_STATUS_ICON
+  else if (status_icon)
+    {
+      user_data = status_icon;
+      i = status_button;
+      pos_func = gtk_status_icon_position_menu;
+    }
+#endif /* HAVE_GTK_STATUS_ICON */
   else
     {
       for (i = 0; i < 5; i++)
@@ -2711,7 +2743,8 @@
 
   /* Display the menu.  */
   gtk_widget_show_all (menu);
-  gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
+  gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, user_data, i,
+		  gtk_get_current_event_time ());
 
   record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
 
@@ -2772,14 +2805,18 @@
 
 /* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
    menu pops down.
-   menu_item_selection will be set to the selection.  */
+   menu_item_selection will be set to the selection.
+   The status-icon-related arguments are ignore in this implementation.  */
 static void
-create_and_show_popup_menu (f, first_wv, x, y, for_click)
+create_and_show_popup_menu (f, first_wv, x, y, for_click,
+			    status_icon, status_button)
      FRAME_PTR f;
      widget_value *first_wv;
      int x;
      int y;
      int for_click;
+     void *status_icon;
+     int status_button;
 {
   int i;
   Arg av[2];
@@ -2848,7 +2885,8 @@
 #endif /* not USE_GTK */
 
 static Lisp_Object
-xmenu_show (f, x, y, for_click, keymaps, title, error)
+xmenu_show (f, x, y, for_click, keymaps, title, error,
+	    status_icon, status_button)
      FRAME_PTR f;
      int x;
      int y;
@@ -2856,6 +2894,8 @@
      int keymaps;
      Lisp_Object title;
      char **error;
+     void *status_icon;
+     int status_button;
 {
   int i;
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
@@ -3062,7 +3102,8 @@
   menu_item_selection = 0;
 
   /* Actually create and show the menu until popped down.  */
-  create_and_show_popup_menu (f, first_wv, x, y, for_click);
+  create_and_show_popup_menu (f, first_wv, x, y, for_click,
+			      status_icon, status_button);
 
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
@@ -3523,6 +3564,10 @@
      int keymaps;
      Lisp_Object title;
      char **error;
+     /* Note that status-icon-related arguments are ignored in this
+	implementation  */
+     void *status_icon;
+     int status_button;
 {
   Window root;
   XMenu *menu;

  parent reply	other threads:[~2008-01-12  1:38 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-01-11 23:28 RFC: status icon support Tom Tromey
2008-01-12  1:57 ` Dan Nicolaescu
2008-01-12  1:28   ` Tom Tromey
2008-01-12  1:38   ` Tom Tromey [this message]
2008-01-12  8:45     ` Ulrich Mueller
2008-01-12 17:45       ` Tom Tromey
2008-01-14  2:01         ` Richard Stallman
2008-01-14  1:35           ` Tom Tromey
2008-01-14 17:26             ` Richard Stallman
2008-01-19  5:18               ` Tom Tromey
2008-01-20  6:14                 ` Richard Stallman
2008-01-23  4:00                   ` Michael Olson
2008-01-14  1:41           ` Tom Tromey
2008-01-14  1:03     ` Michael Olson
2008-01-14  1:01       ` Tom Tromey
2008-01-14  7:03         ` Jan Djärv
2008-01-15  6:01         ` Michael Olson
2008-01-16  1:10           ` Tom Tromey
2008-01-16  4:10             ` Michael Olson
2008-01-12 11:11   ` Richard Stallman
2008-01-12 11:25     ` David Kastrup
2008-01-12 11:27       ` Andreas Schwab
2008-01-12 11:46         ` David Kastrup
2008-01-12 14:10           ` Andreas Schwab
2008-01-12 14:19             ` David Kastrup
2008-01-12 17:33               ` Andreas Schwab
2008-01-14  2:00               ` Richard Stallman
2008-01-14  2:25                 ` Stefan Monnier
2008-01-14  7:05                 ` Jan Djärv
2008-01-12 13:52     ` Dan Nicolaescu
2008-01-12 14:13       ` Andreas Schwab
2008-01-12 14:26         ` Dan Nicolaescu
2008-01-12 17:36           ` Andreas Schwab
2008-01-12 18:59             ` Dan Nicolaescu
2008-01-12 14:33         ` David Kastrup
2008-01-12 17:45           ` Andreas Schwab
2008-01-12 18:07             ` David Kastrup
2008-01-12 18:16               ` Andreas Schwab
2008-01-14  2:01           ` Richard Stallman
2008-01-14  2:47             ` Dan Nicolaescu
2008-01-14 17:26               ` Richard Stallman
2008-01-14  9:14             ` David Kastrup
2008-01-14 17:26               ` Richard Stallman
2008-01-14  3:47 ` YAMAMOTO Mitsuharu
2008-01-14  3:49   ` Tom Tromey
2008-01-14 13:35     ` Stefan Monnier
2008-01-14 21:40     ` YAMAMOTO Mitsuharu
2008-01-16  1:17       ` Tom Tromey
2008-01-16 11:55         ` YAMAMOTO Mitsuharu
2008-01-14 17:26   ` Richard Stallman
2008-01-14 17:10     ` Tom Tromey
2008-01-16  2:42       ` Richard Stallman
  -- strict thread matches above, loose matches on Subject: below --
2007-12-30 19:56 Tom Tromey
2007-12-31 17:18 ` Dan Nicolaescu
2007-12-31 18:29   ` Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m3zlvbkel7.fsf@fleche.redhat.com \
    --to=tromey@redhat.com \
    --cc=dann@ics.uci.edu \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).