unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* RFC: status icon support again
@ 2008-02-03  6:35 Tom Tromey
  2008-02-03  7:37 ` YAMAMOTO Mitsuharu
  0 siblings, 1 reply; 8+ messages in thread
From: Tom Tromey @ 2008-02-03  6:35 UTC (permalink / raw)
  To: emacs-devel

Here is a new revision of the status icon patch.  I think I've read
all the responses and I tried to take them into account.  Namely:

* I looked a little at rewriting so status icons would be part of
  keymaps, but this looked complicated and I didn't see a benefit --
  in particular this code has no relation to toolbars at all, and I
  didn't think any code could be shared.

* The new patch does not introduce a new type.  Instead, a status icon
  is named by a symbol.  The API changed slightly as a consequence of
  this.

* I tried to make all the pre-function comments verbose and readable.

* Michael Olson's test cases should work ok now.  At least, they used
  to fail for me, and now they work.  There is a hack here, in that
  Gtk does not actually provide a reliable way to get the information
  we want.  (I am not sure whether this code will work if the user
  doesn't actually have a notification area.)

I think it is a bit more robust now as well.  I found a few problems
with the earlier patch that I've fixed.

Tom

ChangeLog:
2008-02-03  Tom Tromey  <tromey@redhat.com>

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

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

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

src/ChangeLog:
2008-02-03  Tom Tromey  <tromey@redhat.com>

	* alloc.c (Fgarbage_collect): Call mark_status_icons.
	* 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.
	* lisp.h (syms_of_systray, get_status_icon_for_menu): 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.
	* 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.

Index: configure.in
===================================================================
RCS file: /sources/emacs/emacs/configure.in,v
retrieving revision 1.506
diff -u -r1.506 configure.in
--- configure.in	2 Feb 2008 15:49:10 -0000	1.506
+++ configure.in	3 Feb 2008 06:54:40 -0000
@@ -148,6 +148,7 @@
 
 OPTION_DEFAULT_ON([gpm],[don't use -lgpm for mouse support on a GNU/Linux console])
 OPTION_DEFAULT_OFF([dbus],[compile with D-Bus support])
+OPTION_DEFAULT_ON([libnotify],[don't use libnotify])
 
 AC_ARG_WITH([pkg-config-prog],dnl
 [AS_HELP_STRING([--with-pkg-config-prog=PATH],
@@ -1769,6 +1770,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
@@ -1799,6 +1809,16 @@
    fi
 fi
 
+dnl Check if we have libnotify.
+if test "${with_libnotify}" = yes; then
+  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
+fi
+
 dnl Do not put whitespace before the #include statements below.
 dnl Older compilers (eg sunos4 cc) choke on it.
 HAVE_XAW3D=no
Index: doc/lispref/frames.texi
===================================================================
RCS file: /sources/emacs/emacs/doc/lispref/frames.texi,v
retrieving revision 1.6
diff -u -r1.6 frames.texi
--- doc/lispref/frames.texi	8 Jan 2008 20:45:50 -0000	1.6
+++ doc/lispref/frames.texi	3 Feb 2008 06:54:41 -0000
@@ -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,126 @@
 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 name &optional alist
+
+This function creates and returns a new status icon.  @var{name} is a
+symbol which is used to refer to the status icon.  It is an error if
+@var{name} already names a 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-list
+Return a list of all 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.365
diff -u -r1.365 Makefile.in
--- src/Makefile.in	1 Feb 2008 23:10:21 -0000	1.365
+++ src/Makefile.in	3 Feb 2008 06:54:44 -0000
@@ -270,6 +270,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
@@ -283,7 +288,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} @FREETYPE_CFLAGS@ @FONTCONFIG_CFLAGS@ @LIBOTF_CFLAGS@ @M17N_FLT_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} @FREETYPE_CFLAGS@ @FONTCONFIG_CFLAGS@ @LIBOTF_CFLAGS@ @M17N_FLT_CFLAGS@
 .c.o:
 	$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<
 
@@ -311,7 +316,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
@@ -957,6 +962,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) \
    @FREETYPE_LIBS@ @FONTCONFIG_LIBS@ @LIBOTF_LIBS@ @M17N_FLT_LIBS@ \
@@ -1221,6 +1227,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 character.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.435
diff -u -r1.435 alloc.c
--- src/alloc.c	1 Feb 2008 16:00:57 -0000	1.435
+++ src/alloc.c	3 Feb 2008 06:54:44 -0000
@@ -5087,6 +5087,13 @@
     extern void xg_mark_data ();
     xg_mark_data ();
   }
+
+#ifdef HAVE_GTK_STATUS_ICON
+  {
+    extern void mark_status_icons ();
+    mark_status_icons ();
+  }
+#endif
 #endif
 
 #if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
Index: src/emacs.c
===================================================================
RCS file: /sources/emacs/emacs/src/emacs.c,v
retrieving revision 1.416
diff -u -r1.416 emacs.c
--- src/emacs.c	1 Feb 2008 16:00:45 -0000	1.416
+++ src/emacs.c	3 Feb 2008 06:54:45 -0000
@@ -1656,6 +1656,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.942
diff -u -r1.942 keyboard.c
--- src/keyboard.c	1 Feb 2008 16:00:40 -0000	1.942
+++ src/keyboard.c	3 Feb 2008 06:54:46 -0000
@@ -489,6 +489,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:
       {
@@ -11811,6 +11821,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");
@@ -12547,6 +12560,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.606
diff -u -r1.606 lisp.h
--- src/lisp.h	1 Feb 2008 16:00:44 -0000	1.606
+++ src/lisp.h	3 Feb 2008 06:54:46 -0000
@@ -3242,6 +3243,10 @@
 extern void syms_of_sound P_ ((void));
 extern void init_sound P_ ((void));
 
+/* Defined in systray.c */
+extern void syms_of_systray P_ ((void));
+extern void *get_status_icon_for_menu (Lisp_Object symbol);
+
 /* Defined in category.c */
 extern void init_category_once P_ ((void));
 extern Lisp_Object char_category_set P_ ((int));
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	3 Feb 2008 06:54:46 -0000
@@ -0,0 +1,850 @@
+/* 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 "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
+
+/* A linked list of notifications.  This is used to associate a
+   notification with its corresponding status icon.  */
+struct notification_list
+{
+  /* The notification.  */
+  NotifyNotification *notification;
+
+  /* Lisp data associated with this notification, which must remain
+     live while the notification is active.  */
+  Lisp_Object list;
+
+  /* Link to the next one.  */
+  struct notification_list *next;
+};
+
+/* Representation of a status icon.  */
+struct status_icon
+{
+  /* The symbol used when the icon was created.  */
+  Lisp_Object name;
+
+  /* Parameter alist of this status icon.  */
+  Lisp_Object param_alist;
+
+  /* The icon widget.  */
+  GtkStatusIcon *icon;
+
+  /* Linked list of all notifications sent via this icon.  */
+  struct notification_list *notifications;
+
+  /* The next icon in the list.  */
+  struct status_icon *next;
+};
+
+/* All the status icons.  */
+static struct status_icon *all_icons;
+
+/* List of notifications that were not issued against a status icon.  */
+static struct notification_list *iconless_notifications;
+
+/* FIXME: should be in a header.  */
+extern Lisp_Object Qhelp_echo;
+extern Lisp_Object Qmenu;
+extern Lisp_Object Qstatus_icon_click_event;
+
+Lisp_Object Qblinking;
+Lisp_Object Qclick_callback;
+Lisp_Object QCbody, QCtimeout, QCurgency, QCicon, QCaction, QCclosed_callback;
+Lisp_Object Qlow, Qcritical, Qwarning, Qquestion, Qerror;
+Lisp_Object Qpopup_menu;
+
+/* Return a status icon of the given name, or NULL if no such icon
+   exists.  */
+static struct status_icon *
+find_status_icon (Lisp_Object name)
+{
+  struct status_icon *iter;
+  for (iter = all_icons; iter; iter = iter->next)
+    if (EQ (name, iter->name))
+      break;
+  return iter;
+}
+
+/* Return a status icon of the given name, or throw an error if no
+   such icon exists, or if NAME is not a symbol.  */
+static struct status_icon *
+get_status_icon (Lisp_Object name)
+{
+  struct status_icon *result;
+
+  CHECK_SYMBOL (name);
+  result = find_status_icon (name);
+  if (! result)
+    error ("no status icon named `%s'", SDATA (SYMBOL_NAME (name)));
+  return result;
+}
+
+/* Like find_status_icon, but for use by the menu-handling code.
+   Returns the underlying GtkStatusIcon rather than status_icon
+   struct.  */
+void *
+get_status_icon_for_menu (Lisp_Object name)
+{
+  struct status_icon *sicon = find_status_icon (name);
+  return sicon ? sicon->icon : NULL;
+}
+
+/* This callback is attached to the "activate" signal on the status
+   icon.  It is called when the user clicks (usually the left button)
+   on the icon.  WIDGET is the widget which is clicked, and DATA is
+   the data we passed to the signal; in our case, the icon
+   descriptor.  */
+static void
+activate_icon (GtkWidget *widget, gpointer data)
+{
+  Lisp_Object icon = (Lisp_Object) data;
+  struct status_icon *sicon = find_status_icon (icon);
+
+  /* Just ignore garbage.  */
+  if (sicon)
+    {
+      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);
+	}
+    }
+}
+
+/* This callback is attached to the "popup-menu" signal on the status
+   icon.  It is called when the user clicks (usually the right button)
+   on the icon.  The arguments are as specified by Gtk; in our case
+   USER_DATA is the icon descriptor.  */
+static void
+pop_up_status_menu (GtkStatusIcon *gicon, guint button,
+		    guint activate_time, gpointer user_data)
+{
+  Lisp_Object icon = (Lisp_Object) user_data;
+  struct status_icon *sicon = find_status_icon (icon);
+
+  /* Just ignore garbage.  */
+  if (sicon)
+    {
+      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.  The keys and
+   values are as documented in modify-status-icon-parameters.
+   Unrecognized keys are ignored here.  This function modifies the
+   status icon as it works.  */
+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 (sicon->icon,
+					  GTK_STOCK_DIALOG_QUESTION);
+	}
+      else
+	gtk_status_icon_set_from_file (sicon->icon,
+				       (char *) SDATA (filename));
+      UNGCPRO;
+    }
+  else if (EQ (key, Qblinking))
+    gtk_status_icon_set_blinking (sicon->icon, ! NILP (value));
+  else if (EQ (key, Qvisible))
+    gtk_status_icon_set_visible (sicon->icon, ! 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 (sicon->icon, NULL);
+      else
+	{
+	  /* FIXME: should accept a sexp to eval here.  */
+	  CHECK_STRING (value);
+	  tooltip = ENCODE_UTF_8 (value);
+	  gtk_status_icon_set_tooltip (sicon->icon,
+				       (char *) SDATA (tooltip));
+	}
+      UNGCPRO;
+    }
+  else
+    {
+      /* Ignore things we don't understand.  */
+    }
+}
+
+/* Apply the ALIST of properties to status icon ICON.  The contents of
+   ALIST are as documented in modify-status-icon-parameters.  */
+static void
+apply_status_icon_alist (struct status_icon *sicon, Lisp_Object alist)
+{
+  Lisp_Object tail;
+
+  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 parameters.  */
+      elt = Fassq (key, sicon->param_alist);
+      if (NILP (elt))
+	sicon->param_alist = Fcons (Fcons (key, value), sicon->param_alist);
+      else
+	Fsetcdr (elt, value);
+    }
+}
+
+/* Flush the Gtk event queue.  Must be called with input blocked.  */
+static void
+flush_gtk_events ()
+{
+  while (gtk_events_pending ())
+    gtk_main_iteration ();
+}
+
+/* Helper function for make-status-icon.  This is a Gtk callback that
+   is attached to the GtkStatusIcon 'size-changed' signal.  */
+static gboolean
+set_size_changed (GtkStatusIcon *icon, gint size, gpointer user_data)
+{
+  int *valp = (int *) user_data;
+  *valp = 1;
+  return 0;
+}
+
+DEFUN ("make-status-icon", Fmake_status_icon, Smake_status_icon,
+       1, 2, 0,
+       doc: /* Return a newly created status icon.
+NAME is a symbol used to identify the icon.
+Optional argument ALIST is an alist of parameters for the new frame.
+See `modify-status-icon-parameters' for a list of recognized parameters.*/)
+     (name, alist)
+     Lisp_Object name, alist;
+{
+  struct status_icon *sicon;
+  Lisp_Object deflist;
+  struct gcpro gcpro1;
+  int size_changed;
+  gulong handler;
+
+  check_x ();
+
+  CHECK_SYMBOL (name);
+  if (find_status_icon (name))
+    error ("status icon `%s' already exists", SDATA (SYMBOL_NAME (name)));
+
+  BLOCK_INPUT;
+
+  sicon = (struct status_icon *) xmalloc (sizeof (struct status_icon));
+  sicon->name = name;
+  sicon->param_alist = Qnil;
+  sicon->icon = gtk_status_icon_new ();
+  sicon->notifications = NULL;
+  sicon->next = all_icons;
+  all_icons = sicon;
+
+  size_changed = 0;
+  g_signal_connect (sicon->icon, "size-changed", G_CALLBACK (set_size_changed),
+		    (gpointer) &size_changed);
+
+  g_signal_connect (sicon->icon, "activate", G_CALLBACK (activate_icon),
+		    (gpointer) name);
+  handler = g_signal_connect (sicon->icon, "popup-menu",
+			      G_CALLBACK (pop_up_status_menu),
+			      (gpointer) name);
+
+  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 (sicon, deflist);
+
+  /* This is a hack: if we wait for a size event, then an immediate
+     notification will show up at the correct location.  We only try
+     to do this when we've requested that the icon be visible.  */
+  if (! gtk_status_icon_get_visible (sicon->icon))
+    size_changed = 1;
+  do
+    {
+      flush_gtk_events ();
+    }
+  while (! size_changed);
+  g_signal_handler_disconnect (sicon->icon, handler);
+
+  UNBLOCK_INPUT;
+
+  RETURN_UNGCPRO (name);
+}
+
+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.
+ICON is a symbol naming a status icon.
+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;
+{
+  struct status_icon *sicon = get_status_icon (icon);
+  BLOCK_INPUT;
+  apply_status_icon_alist (sicon, alist);
+  flush_gtk_events ();
+  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 = get_status_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.  */)
+     ()
+{
+  Lisp_Object result = Qnil;
+  struct status_icon *iter;
+  for (iter = all_icons; iter; iter = iter->next)
+    result = Fcons (iter->name, result);
+  return result;
+}
+
+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 = get_status_icon (icon), *iter;
+
+  BLOCK_INPUT;
+
+  while (sicon->notifications)
+    {
+      struct notification_list *next;
+      /* Apparently this does not call the 'close' callback, so we do
+	 all the handling here rather relying on the callback.  */
+      notify_notification_close (sicon->notifications->notification, NULL);
+      g_object_unref (sicon->notifications->notification);
+      next = sicon->notifications->next;
+      free (sicon->notifications);
+      sicon->notifications = next;
+    }
+
+  gtk_status_icon_set_visible (sicon->icon, 0);
+  g_object_unref (sicon->icon);
+  flush_gtk_events ();
+  UNBLOCK_INPUT;
+
+  if (all_icons == sicon)
+    all_icons = sicon->next;
+  else
+    {
+      struct status_icon *iter;
+      for (iter = all_icons; iter; iter = iter->next)
+	if (iter->next == sicon)
+	  {
+	    iter->next = sicon->next;
+	    break;
+	  }
+    }
+  free (sicon);
+
+  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)
+{
+  struct notification_list *iter, **prev;
+  Lisp_Object icon = XCAR (callback_arg);
+  struct status_icon *sicon = find_status_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);
+    }
+
+  if (sicon)
+    {
+      prev = &sicon->notifications;
+      iter = sicon->notifications;
+    }
+  else
+    {
+      prev = &iconless_notifications;
+      iter = iconless_notifications;
+    }
+  while (iter)
+    {
+      if (iter->notification == notification)
+	{
+	  *prev = iter->next;
+	  free (iter);
+	  break;
+	}
+      prev = &iter->next;
+      iter = iter->next;
+    }
+
+  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;
+  struct notification_list *entry;
+
+  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))
+    sicon = NULL;
+  else
+    sicon = get_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 (sicon)
+    {
+      notification
+	= notify_notification_new_with_status_icon (SDATA (gc_temp), NULL,
+						    GTK_STOCK_DIALOG_INFO,
+						    sicon->icon);
+    }
+  else
+    {
+      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);
+
+  /* Arrange to clean up when this notification goes away.  */
+  g_signal_connect (G_OBJECT (notification), "closed",
+		    G_CALLBACK (cleanup_notification),
+		    (gpointer) callback_arg);
+
+  entry = ((struct notification_list *)
+	   xmalloc (sizeof (struct notification_list)));
+  entry->notification = notification;
+  entry->list = callback_arg;
+  if (sicon)
+    {
+      entry->next = sicon->notifications;
+      sicon->notifications = entry;
+    }
+  else
+    {
+      entry->next = iconless_notifications;
+      iconless_notifications = entry;
+    }
+
+  /* Ignore errors here for now.  */
+  notify_notification_show (notification, NULL);
+
+  flush_gtk_events ();
+  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 SYMBOLP (object) && find_status_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;
+
+  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);
+
+  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_handle_click_event);
+
+  Fprovide (intern ("systray"), Qnil);
+}
+
+void
+mark_status_icons ()
+{
+  struct status_icon *iter;
+  struct notification_list *niter;
+
+  for (iter = all_icons; iter; iter = iter->next)
+    {
+      mark_object (iter->name);
+      mark_object (iter->param_alist);
+
+      for (niter = iter->notifications; niter; niter = niter->next)
+	mark_object (niter->list);
+    }
+
+  for (niter = iconless_notifications; niter; niter = niter->next)
+    mark_object (niter->list);
+}
+
+#else /* HAVE_GTK_STATUS_ICON */
+
+void
+syms_of_systray ()
+{
+  /* Nothing.  */
+}
+
+#endif
Index: src/termhooks.h
===================================================================
RCS file: /sources/emacs/emacs/src/termhooks.h,v
retrieving revision 1.91
diff -u -r1.91 termhooks.h
--- src/termhooks.h	8 Jan 2008 20:44:20 -0000	1.91
+++ src/termhooks.h	3 Feb 2008 06:54:47 -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.326
diff -u -r1.326 xmenu.c
--- src/xmenu.c	1 Feb 2008 16:00:56 -0000	1.326
+++ src/xmenu.c	3 Feb 2008 06:54:47 -0000
@@ -156,7 +156,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 +780,10 @@
 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 symbol used when creating 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,16 @@
 	{
           get_current_pos_p = 1;
         }
+      else if (CONSP (position)
+	       && (status_icon = get_status_icon_for_menu (XCAR (position))))
+	{
+	  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 +1045,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 +2681,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 +2723,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 +2740,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 +2802,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 +2882,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 +2891,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 +3099,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 +3561,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;




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: RFC: status icon support again
  2008-02-03  7:37 ` YAMAMOTO Mitsuharu
@ 2008-02-03  7:35   ` Tom Tromey
  2008-02-03  9:23     ` YAMAMOTO Mitsuharu
                       ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Tom Tromey @ 2008-02-03  7:35 UTC (permalink / raw)
  To: YAMAMOTO Mitsuharu; +Cc: emacs-devel

>>>>> ">" == YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp> writes:

>>>>> On Sat, 02 Feb 2008 23:35:18 -0700, Tom Tromey <tromey@redhat.com> said:
>> * I looked a little at rewriting so status icons would be part of
>> keymaps, but this looked complicated and I didn't see a benefit --
>> in particular this code has no relation to toolbars at all, and I
>> didn't think any code could be shared.

>> Of course code cannot be shared between them without changing the
>> fundamental design of your code for status icons.  Do you claim that
>> status icons and tool bar icons are inherently different enough to
>> resort to different design in implementation code and also in elisp
>> interface?

I don't want to make any very strong claims.  I am not really expert
at Emacs internals.

>> Another benefit of the use of keymaps is that it makes easier to move
>> between status icons from/to tool bar icons (and possibly also between
>> menu bar items?), in the case that the use of either is
>> impossible/inappropriate.

In my view toolbars and the status area are very different; though it
is true that there is some overlap between them.

Pretty much every buffer can be expected to have a toolbar.  It is
typical for the toolbar to change as the user uses different parts of
an application.

On the other hand, space for status icons is limited and generally
they should be used for more "global" things.  It would be
disconcerting if the visible status icons changed frequently, say in
response to switching buffers.

In an earlier message you mentioned toolbars being integrated into
redisplay.  This was one disconnect for me -- I don't see how, or why,
we would want to integrate status icons into redisplay.  They are not
connected to a frame at all.

>> I think user-configurable event dispatcher
>> tables should always be implemented as keymaps unless there are strong
>> reasons to avoid them.

May I ask why?

I have basically two reasons for the approach I have taken.

First, it is simple and easy to understand.  In Gtk, a status icon
only has two possible events; using a keymap for this seemed like
overkill.

Second, unlike the keymap idea, I knew how to implement it this way.
I suppose ignorance is no excuse, though.

Could you help with the next reimplementation?

Tom




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: RFC: status icon support again
  2008-02-03  6:35 RFC: status icon support again Tom Tromey
@ 2008-02-03  7:37 ` YAMAMOTO Mitsuharu
  2008-02-03  7:35   ` Tom Tromey
  0 siblings, 1 reply; 8+ messages in thread
From: YAMAMOTO Mitsuharu @ 2008-02-03  7:37 UTC (permalink / raw)
  To: Tom Tromey; +Cc: emacs-devel

>>>>> On Sat, 02 Feb 2008 23:35:18 -0700, Tom Tromey <tromey@redhat.com> said:

> * I looked a little at rewriting so status icons would be part of
> keymaps, but this looked complicated and I didn't see a benefit --
> in particular this code has no relation to toolbars at all, and I
> didn't think any code could be shared.

Of course code cannot be shared between them without changing the
fundamental design of your code for status icons.  Do you claim that
status icons and tool bar icons are inherently different enough to
resort to different design in implementation code and also in elisp
interface?

Another benefit of the use of keymaps is that it makes easier to move
between status icons from/to tool bar icons (and possibly also between
menu bar items?), in the case that the use of either is
impossible/inappropriate.  I think user-configurable event dispatcher
tables should always be implemented as keymaps unless there are strong
reasons to avoid them.

				     YAMAMOTO Mitsuharu
				mituharu@math.s.chiba-u.ac.jp




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: RFC: status icon support again
  2008-02-03  7:35   ` Tom Tromey
@ 2008-02-03  9:23     ` YAMAMOTO Mitsuharu
  2008-02-03 17:18       ` Tom Tromey
  2008-02-03 10:12     ` YAMAMOTO Mitsuharu
  2008-02-04  3:13     ` Stefan Monnier
  2 siblings, 1 reply; 8+ messages in thread
From: YAMAMOTO Mitsuharu @ 2008-02-03  9:23 UTC (permalink / raw)
  To: tromey; +Cc: emacs-devel

>>>>> On Sun, 03 Feb 2008 00:35:37 -0700, Tom Tromey <tromey@redhat.com> said:

>>> Another benefit of the use of keymaps is that it makes easier to
>>> move between status icons from/to tool bar icons (and possibly
>>> also between menu bar items?), in the case that the use of either
>>> is impossible/inappropriate.

> In my view toolbars and the status area are very different; though
> it is true that there is some overlap between them.

I can imagine that they are usually used for different purposes.
Nevertheless I think they are very similar in attributes they have and
functionality they provide.

> In an earlier message you mentioned toolbars being integrated into
> redisplay.  This was one disconnect for me -- I don't see how, or
> why, we would want to integrate status icons into redisplay.  They
> are not connected to a frame at all.

I can't say users never want to make changes to some attributes of
status icons depending on the frame.

>>> I think user-configurable event dispatcher tables should always be
>>> implemented as keymaps unless there are strong reasons to avoid
>>> them.

> May I ask why?

I think the reason is rather apparent.  That provides uniform
interface to elisp programmers and they can use existing keymap
functions to manipulate those tables.

> I have basically two reasons for the approach I have taken.

> First, it is simple and easy to understand.  In Gtk, a status icon
> only has two possible events; using a keymap for this seemed like
> overkill.

Are you possibly thinking about using one keymap per one status icon?
It's natural to use one keymap for the set of all status icons, just
as in tool bar keymaps.

> Second, unlike the keymap idea, I knew how to implement it this way.
> I suppose ignorance is no excuse, though.

Generally speaking, I don't think it's a good idea to add a new
feature that is implemented without understanding how the existing
related/similar features are implemented in the target application.

				     YAMAMOTO Mitsuharu
				mituharu@math.s.chiba-u.ac.jp




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: RFC: status icon support again
  2008-02-03  7:35   ` Tom Tromey
  2008-02-03  9:23     ` YAMAMOTO Mitsuharu
@ 2008-02-03 10:12     ` YAMAMOTO Mitsuharu
  2008-02-03 17:11       ` Tom Tromey
  2008-02-04  3:13     ` Stefan Monnier
  2 siblings, 1 reply; 8+ messages in thread
From: YAMAMOTO Mitsuharu @ 2008-02-03 10:12 UTC (permalink / raw)
  To: tromey; +Cc: emacs-devel

>>>>> On Sun, 03 Feb 2008 00:35:37 -0700, Tom Tromey <tromey@redhat.com> said:

> In an earlier message you mentioned toolbars being integrated into
> redisplay.  This was one disconnect for me -- I don't see how, or
> why, we would want to integrate status icons into redisplay.  They
> are not connected to a frame at all.

Integration into redisplay makes it possible to provide an alternative
implementation for the case that the status icons are not available.
As for tool bars, Emacs has both native and toolkit implementations.

BTW, how status icons work when Emacs is connecting to multiple X11
displays?

				      YAMAMOTO Mitsuharu
				mituharu@math.s.chiba-u.ac.jp




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: RFC: status icon support again
  2008-02-03 10:12     ` YAMAMOTO Mitsuharu
@ 2008-02-03 17:11       ` Tom Tromey
  0 siblings, 0 replies; 8+ messages in thread
From: Tom Tromey @ 2008-02-03 17:11 UTC (permalink / raw)
  To: YAMAMOTO Mitsuharu; +Cc: emacs-devel

>>>>> ">" == YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp> writes:

>> Integration into redisplay makes it possible to provide an alternative
>> implementation for the case that the status icons are not available.
>> As for tool bars, Emacs has both native and toolkit implementations.

>> BTW, how status icons work when Emacs is connecting to multiple X11
>> displays?

Poorly, AFAICT.  Gtk seems to always create a new status icon on the
default display.  But good point, I forgot to go back and look at
this, particularly what happens when a display is closed.

Tom




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: RFC: status icon support again
  2008-02-03  9:23     ` YAMAMOTO Mitsuharu
@ 2008-02-03 17:18       ` Tom Tromey
  0 siblings, 0 replies; 8+ messages in thread
From: Tom Tromey @ 2008-02-03 17:18 UTC (permalink / raw)
  To: YAMAMOTO Mitsuharu; +Cc: emacs-devel

>>>>> ">" == YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp> writes:

>> Generally speaking, I don't think it's a good idea to add a new
>> feature that is implemented without understanding how the existing
>> related/similar features are implemented in the target application.

Yes, I suppose you are right.  Please consider my patch withdrawn.  I
will look into this more to see if I can come up with something more
acceptable.  Or, maybe someone else can take over development of this
feature.

Tom




^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: RFC: status icon support again
  2008-02-03  7:35   ` Tom Tromey
  2008-02-03  9:23     ` YAMAMOTO Mitsuharu
  2008-02-03 10:12     ` YAMAMOTO Mitsuharu
@ 2008-02-04  3:13     ` Stefan Monnier
  2 siblings, 0 replies; 8+ messages in thread
From: Stefan Monnier @ 2008-02-04  3:13 UTC (permalink / raw)
  To: Tom Tromey; +Cc: YAMAMOTO Mitsuharu, emacs-devel

> On the other hand, space for status icons is limited and generally
> they should be used for more "global" things.  It would be
> disconcerting if the visible status icons changed frequently, say in
> response to switching buffers.

[ Probably talking nonsense because I know nothing about your patch: ]
Maybe you could simply use a separate keymap specifically for the
status icons.  This would be stored in a terminal-local variable, so
each terminal (aka display) could get its own set of status icons.

The main problem I can imagine is that modifying the keymap will not
automatically call the status-icon code, so we'd have to either
constantly check this keymap for changes, or use
a force-status-icon-update function which would cause a rescan of the
keymap to discover which icons should be added/removed/modified/...

Not sure if it's really better than special purpose code.

And of course, I'm ignoring (and ignorant) of the other side: how to
react to the user clicking on one of those icons.  Should these events
be looked in the usual keymaps?

Ideally the C code should just provide a low-level access to the
facilities on top of which either solution can be implemented in elisp.


        Stefan




^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-02-04  3:13 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-03  6:35 RFC: status icon support again Tom Tromey
2008-02-03  7:37 ` YAMAMOTO Mitsuharu
2008-02-03  7:35   ` Tom Tromey
2008-02-03  9:23     ` YAMAMOTO Mitsuharu
2008-02-03 17:18       ` Tom Tromey
2008-02-03 10:12     ` YAMAMOTO Mitsuharu
2008-02-03 17:11       ` Tom Tromey
2008-02-04  3:13     ` Stefan Monnier

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).