diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index be8624e0fde..b6d34ae0a3d 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -2850,7 +2850,9 @@ Emacs is restarted by the session manager.
@cindex notifications, on desktop
Emacs is able to send @dfn{notifications} on systems that support the
-freedesktop.org Desktop Notifications Specification and on MS-Windows.
+freedesktop.org Desktop Notifications Specification, MS-Windows, and
+Haiku.
+
In order to use this functionality on POSIX hosts, Emacs must have
been compiled with D-Bus support, and the @code{notifications} library
must be loaded. @xref{Top, , D-Bus,dbus,D-Bus integration in Emacs}.
@@ -3167,6 +3169,37 @@ This function removes the tray notification given by its unique
@var{id}.
@end defun
+@cindex desktop notifications, Haiku
+When Emacs runs under Haiku as a GUI program, it is also provides a
+restricted pastiche of the D-Bus desktop notifications interface
+previously addressed. The principle capabilities absent from the
+function detailed below are call-back functions such as
+@code{:actions}, @code{:on-action} and @code{:on-close}.
+
+@defun haiku-notifications-notify &rest params
+This function sends a notification to the desktop notification server,
+incorporating a number of parameters that are akin to some of those
+accepted by @code{notifications-notify}. The parameters are:
+
+@table @code
+@item :title @var{title}
+@item :body @var{body}
+@item :replaces-id @var{replaces-id}
+@item :urgency @var{urgency}
+These have the same meaning as they do when used in calls to
+@code{notifications-notify}.
+
+@item :app-icon @var{app-icon}
+This should be the file name designating an image file to use as the
+icon for the notification displayed. If @code{nil}, the icon
+presented will instead be Emacs's app icon.
+@end table
+
+Its return value is a number identifying the notification, which can
+be exploited as the @code{:replaces-id} parameter to a subsequent call
+to this function.
+@end defun
+
@node File Notifications
@section Notifications on File Changes
@cindex file notifications
diff --git a/etc/NEWS b/etc/NEWS
index 808b5996729..966d1f8c292 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -783,6 +783,12 @@ Use 'define-minor-mode' and 'define-globalized-minor-mode' instead.
See the "(elisp) Porting Old Advice" node for help converting them
to use 'advice-add' or 'define-advice' instead.
++++
+** Desktop notifications are now supported on the Haiku operating system.
+The new function 'haiku-notifications-notify' provides a subset of the
+capabilities of the 'notifications-notify' function in a manner
+analogous to 'w32-notification-notify'.
+
+++
** New value 'if-regular' for the REPLACE argument to 'insert-file-contents'.
It results in 'insert-file-contents' erasing the buffer instead of
diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el
index b85ce6a6368..32ef0eb4291 100644
--- a/lisp/org/org-clock.el
+++ b/lisp/org/org-clock.el
@@ -51,6 +51,7 @@
(declare-function org-dynamic-block-define "org" (type func))
(declare-function w32-notification-notify "w32fns.c" (&rest params))
(declare-function w32-notification-close "w32fns.c" (&rest params))
+(declare-function haiku-notifications-notify "haikufns.c")
(defvar org-frame-title-format-backup nil)
(defvar org-state)
@@ -855,6 +856,11 @@ use libnotify if available, or fall back on a message."
((stringp org-show-notification-handler)
(start-process "emacs-timer-notification" nil
org-show-notification-handler notification))
+ ((fboundp 'haiku-notifications-notify)
+ ;; N.B. timeouts are not available under Haiku.
+ (haiku-notifications-notify :title "Org mode message"
+ :body notification
+ :urgency 'low))
((fboundp 'w32-notification-notify)
(let ((id (w32-notification-notify
:title "Org mode message"
diff --git a/src/haiku_io.c b/src/haiku_io.c
index 4f1b1435b4b..c6d7108bf49 100644
--- a/src/haiku_io.c
+++ b/src/haiku_io.c
@@ -111,6 +111,8 @@ haiku_len (enum haiku_event_type type)
return sizeof (struct haiku_clipboard_changed_event);
case FONT_CHANGE_EVENT:
return sizeof (struct haiku_font_change_event);
+ case NOTIFICATION_CLICK_EVENT:
+ return sizeof (struct haiku_notification_click_event);
}
emacs_abort ();
diff --git a/src/haiku_select.cc b/src/haiku_select.cc
index fe46075a007..76e2d829204 100644
--- a/src/haiku_select.cc
+++ b/src/haiku_select.cc
@@ -17,15 +17,24 @@ You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see . */
#include
+#include
#include
+#include
#include
-#include
-#include
#include
+#include
+#include
+#include
+#include
+#include
+
+#include
#include
#include
+#include
+#include
#include "haikuselect.h"
@@ -58,6 +67,10 @@ static bool owned_secondary;
/* And the clipboard. */
static bool owned_clipboard;
+
+
+/* C++ clipboard support. */
+
static BClipboard *
get_clipboard_object (enum haiku_clipboard clipboard)
{
@@ -517,3 +530,134 @@ be_get_clipboard_count (enum haiku_clipboard id)
clipboard = get_clipboard_object (id);
return clipboard->SystemCount ();
}
+
+
+
+/* C++ notifications support.
+
+ Desktop notifications on Haiku lack some of the features furnished
+ by notifications.el, specifically displaying multiple titled
+ actions within a single notification, sending callbacks when the
+ notification is dismissed, and providing a timeout after which the
+ notification is hidden.
+
+ Other features, such as notification categories and identifiers,
+ have clean straightforward relationships with their counterparts in
+ notifications.el. */
+
+/* The last notification ID allocated. */
+static intmax_t last_notification_id;
+
+/* Return the `enum notification_type' for TYPE. TYPE is the TYPE
+ argument to a call to `be_display_notification'. */
+
+static enum notification_type
+type_for_type (int type)
+{
+ switch (type)
+ {
+ case 0:
+ return B_INFORMATION_NOTIFICATION;
+
+ case 1:
+ return B_IMPORTANT_NOTIFICATION;
+
+ case 2:
+ return B_ERROR_NOTIFICATION;
+ }
+
+ abort ();
+}
+
+/* Return the ID of this team. */
+
+static team_id
+my_team_id (void)
+{
+ thread_id id;
+ thread_info info;
+
+ id = find_thread (NULL);
+ get_thread_info (id, &info);
+
+ return info.team;
+}
+
+/* Display a desktop notification and return its identifier.
+
+ TITLE is the title text of the notification, encoded as UTF-8 text.
+
+ BODY is the text to be displayed within the body of the
+ notification.
+
+ SUPERSEDES is the identifier of a previous notification to replace,
+ or -1 if a new notification should be displayed.
+
+ TYPE states the urgency of the notification. If 0, the
+ notification is displayed without special decoration. If 1, the
+ notification is displayed with a blue band to its left, identifying
+ it as a notification of medium importance. If 2, the notification
+ is displayed with a red band to its left, marking it as one of
+ critical importance.
+
+ ICON is the name of a file containing the icon of the notification,
+ or NULL, in which case Emacs's app icon will be displayed. */
+
+intmax_t
+be_display_notification (const char *title, const char *body,
+ intmax_t supersedes, int type, const char *icon)
+{
+ intmax_t id;
+ BNotification notification (type_for_type (type));
+ char buffer[INT_STRLEN_BOUND (team_id)
+ + INT_STRLEN_BOUND (intmax_t)
+ + sizeof "."];
+ BBitmap *bitmap;
+
+ if (supersedes < 0)
+ {
+ /* SUPERSEDES hasn't been provided, so allocate a new
+ notification ID. */
+
+ INT_ADD_WRAPV (last_notification_id, 1,
+ &last_notification_id);
+ id = last_notification_id;
+ }
+ else
+ id = supersedes;
+
+ /* Set the title and body text. */
+ notification.SetTitle (title);
+ notification.SetContent (body);
+
+ /* Derive the notification ID from the ID of this team, so as to
+ avoid abrogating notifications from other Emacs sessions. */
+ sprintf (buffer, "%d.%jd", my_team_id (), id);
+ notification.SetMessageID (BString (buffer));
+
+ /* Now set the bitmap icon, if given. */
+
+ if (icon)
+ {
+ bitmap = BTranslationUtils::GetBitmap (icon);
+
+ if (bitmap)
+ {
+ notification.SetIcon (bitmap);
+ delete bitmap;
+ }
+ }
+
+ /* After this, Emacs::ArgvReceived should be called when the
+ notification is clicked. Lamentably, this does not come about,
+ probably because arguments are only passed to applications if
+ they are not yet running. */
+#if 0
+ notification.SetOnClickApp ("application/x-vnd.GNU-emacs");
+ notification.AddOnClickArg (BString ("-Notification,") += buffer);
+#endif /* 0 */
+
+ /* Finally, send the notification. */
+ notification.Send ();
+ return id;
+}
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
index 28d8fae39b7..d5649e4d2ce 100644
--- a/src/haiku_support.cc
+++ b/src/haiku_support.cc
@@ -581,6 +581,24 @@ public:
}
};
+#if 0
+
+/* Return the ID of this team. */
+
+static team_id
+my_team_id (void)
+{
+ thread_id id;
+ thread_info info;
+
+ id = find_thread (NULL);
+ get_thread_info (id, &info);
+
+ return info.team;
+}
+
+#endif /* 0 */
+
class Emacs : public BApplication
{
public:
@@ -621,7 +639,8 @@ public:
{
BAlert *about = new BAlert (PACKAGE_NAME,
PACKAGE_STRING
- "\nThe extensible, self-documenting, real-time display editor.",
+ "\nThe extensible, self-documenting, "
+ "real-time display editor.",
"Close");
about->Go ();
}
@@ -674,6 +693,39 @@ public:
else
BApplication::MessageReceived (msg);
}
+
+ /* The code below doesn't function; see `be_display_notification'
+ for further specifics. */
+
+#if 0
+ void
+ ArgvReceived (int32 argc, char **argv)
+ {
+ struct haiku_notification_click_event rq;
+ intmax_t id;
+ team_id team;
+
+ /* ArgvReceived is called after Emacs is first started, with each
+ command line argument passed to Emacs. It is, moreover, called
+ with ARGC set to 1 and ARGV[0] a string starting with
+ -Notification, after a notification is clicked. This string
+ both incorporates the team ID and the notification ID. */
+
+ if (argc == 1
+ && sscanf (argv[0], "-Notification,%d.%jd", &team, &id) == 2)
+ {
+ /* Since this is a valid notification message, generate an
+ event if the team ID matches. */
+ if (team == my_team_id ())
+ {
+ rq.id = id;
+ haiku_write (NOTIFICATION_CLICK_EVENT, &rq);
+ }
+ }
+
+ BApplication::ArgvReceived (argc, argv);
+ }
+#endif /* 0 */
};
class EmacsWindow : public BWindow
diff --git a/src/haiku_support.h b/src/haiku_support.h
index 564f61f57c7..5c22eb3b0db 100644
--- a/src/haiku_support.h
+++ b/src/haiku_support.h
@@ -116,6 +116,7 @@ enum haiku_event_type
MENU_BAR_LEFT,
CLIPBOARD_CHANGED_EVENT,
FONT_CHANGE_EVENT,
+ NOTIFICATION_CLICK_EVENT,
};
struct haiku_clipboard_changed_event
@@ -464,6 +465,12 @@ struct haiku_font_change_event
enum haiku_what_font what;
};
+struct haiku_notification_click_event
+{
+ /* ID uniquely designating a single notification. */
+ intmax_t id;
+};
+
struct haiku_session_manager_reply
{
bool quit_reply;
diff --git a/src/haikuselect.c b/src/haikuselect.c
index b57c336c264..608b8e8fe30 100644
--- a/src/haikuselect.c
+++ b/src/haikuselect.c
@@ -1255,6 +1255,120 @@ haiku_start_watching_selections (void)
be_start_watching_selection (CLIPBOARD_SECONDARY);
}
+
+
+/* Notification support. */
+
+static intmax_t
+haiku_notifications_notify_1 (Lisp_Object title, Lisp_Object body,
+ Lisp_Object replaces_id,
+ Lisp_Object app_icon, Lisp_Object urgency)
+{
+ int type;
+ intmax_t supersedes;
+ const char *icon;
+
+ if (EQ (urgency, Qlow))
+ type = 0;
+ else if (EQ (urgency, Qnormal))
+ type = 1;
+ else if (EQ (urgency, Qcritical))
+ type = 2;
+ else
+ signal_error ("Invalid notification type provided", urgency);
+
+ supersedes = -1;
+
+ if (!NILP (replaces_id))
+ {
+ CHECK_INTEGER (replaces_id);
+ if (!integer_to_intmax (replaces_id, &supersedes))
+ supersedes = -1;
+ }
+
+ icon = NULL;
+
+ if (!NILP (app_icon))
+ icon = SSDATA (ENCODE_FILE (app_icon));
+
+ /* GC should not transpire from here onwards. */
+ return be_display_notification (SSDATA (title), SSDATA (body),
+ supersedes, type, icon);
+}
+
+DEFUN ("haiku-notifications-notify", Fhaiku_notifications_notify,
+ Shaiku_notifications_notify, 0, MANY, 0, doc:
+ /* Display a desktop notification.
+ARGS must contain keywords followed by values. Each of the following
+keywords is understood:
+
+ :title The notification title.
+ :body The notification body.
+ :replaces-id The ID of a previous notification to supersede.
+ :app-icon The file name of the notification's icon, if any.
+ :urgency One of the symbols `low', `normal' or `critical',
+ specifying the importance of the notification.
+
+:title and :body must be provided. Value is an integer (fixnum or
+bignum) identifying the notification displayed.
+
+usage: (haiku-notifications-notify &rest ARGS) */)
+ (ptrdiff_t nargs, Lisp_Object *args)
+{
+ Lisp_Object title, body, replaces_id, app_icon, urgency;
+ Lisp_Object key, value;
+ ptrdiff_t i;
+
+ /* First, clear each of the variables above. */
+ title = body = replaces_id = app_icon = urgency = Qnil;
+
+ /* If NARGS is odd, error. */
+
+ if (nargs & 1)
+ error ("Odd number of arguments in call to `haiku-notifications-notify'");
+
+ /* Next, iterate through ARGS, searching for arguments. */
+
+ for (i = 0; i < nargs; i += 2)
+ {
+ key = args[i];
+ value = args[i + 1];
+
+ if (EQ (key, QCtitle))
+ title = value;
+ else if (EQ (key, QCbody))
+ body = value;
+ else if (EQ (key, QCreplaces_id))
+ replaces_id = value;
+ else if (EQ (key, QCapp_icon))
+ app_icon = value;
+ else if (EQ (key, QCurgency))
+ urgency = value;
+ }
+
+ /* Demand at least TITLE and BODY be present. */
+
+ if (NILP (title) || NILP (body))
+ error ("Title or body not provided");
+
+ /* Now check the type and possibly expand each non-nil argument. */
+
+ CHECK_STRING (title);
+ title = ENCODE_UTF_8 (title);
+ CHECK_STRING (body);
+ body = ENCODE_UTF_8 (body);
+
+ if (NILP (urgency))
+ urgency = Qlow;
+
+ if (!NILP (app_icon))
+ app_icon = Fexpand_file_name (app_icon, Qnil);
+
+ return make_int (haiku_notifications_notify_1 (title, body,
+ replaces_id,
+ app_icon, urgency));
+}
+
void
syms_of_haikuselect (void)
{
@@ -1312,6 +1426,16 @@ keyboard modifiers currently held down. */);
DEFSYM (Qdouble, "double");
DEFSYM (Qalready_running, "already-running");
+ DEFSYM (QCtitle, ":title");
+ DEFSYM (QCbody, ":body");
+ DEFSYM (QCreplaces_id, ":replaces-id");
+ DEFSYM (QCapp_icon, ":app-icon");
+ DEFSYM (QCurgency, ":urgency");
+
+ DEFSYM (Qlow, "low");
+ DEFSYM (Qnormal, "normal");
+ DEFSYM (Qcritical, "critical");
+
defsubr (&Shaiku_selection_data);
defsubr (&Shaiku_selection_timestamp);
defsubr (&Shaiku_selection_put);
@@ -1320,6 +1444,7 @@ keyboard modifiers currently held down. */);
defsubr (&Shaiku_roster_launch);
defsubr (&Shaiku_write_node_attribute);
defsubr (&Shaiku_send_message);
+ defsubr (&Shaiku_notifications_notify);
haiku_dnd_frame = NULL;
}
diff --git a/src/haikuselect.h b/src/haikuselect.h
index 28a1682e587..c117a2ab4f9 100644
--- a/src/haikuselect.h
+++ b/src/haikuselect.h
@@ -21,9 +21,11 @@ along with GNU Emacs. If not, see . */
#ifdef __cplusplus
#include
-#else
+#include
+#else /* !__cplusplus */
#include
-#endif
+#include
+#endif /* __cplusplus */
#include
@@ -37,15 +39,16 @@ enum haiku_clipboard
#ifdef __cplusplus
extern "C"
{
-#endif
+#endif /* __cplusplus */
/* Defined in haikuselect.c. */
extern void haiku_selection_disowned (enum haiku_clipboard, int64);
/* Defined in haiku_select.cc. */
extern void be_clipboard_init (void);
-extern char *be_find_clipboard_data (enum haiku_clipboard, const char *, ssize_t *);
-extern void be_set_clipboard_data (enum haiku_clipboard, const char *, const char *,
- ssize_t, bool);
+extern char *be_find_clipboard_data (enum haiku_clipboard, const char *,
+ ssize_t *);
+extern void be_set_clipboard_data (enum haiku_clipboard, const char *,
+ const char *, ssize_t, bool);
extern bool be_clipboard_owner_p (enum haiku_clipboard);
extern void be_update_clipboard_count (enum haiku_clipboard);
@@ -58,7 +61,8 @@ extern uint32 be_get_message_type (void *);
extern void be_set_message_type (void *, uint32);
extern void *be_get_message_message (void *, const char *, int32);
extern void *be_create_simple_message (void);
-extern int be_add_message_data (void *, const char *, int32, const void *, ssize_t);
+extern int be_add_message_data (void *, const char *, int32, const void *,
+ ssize_t);
extern int be_add_refs_data (void *, const char *, const char *);
extern int be_add_point_data (void *, const char *, float, float);
extern int be_add_message_message (void *, const char *, void *);
@@ -69,9 +73,14 @@ extern void be_start_watching_selection (enum haiku_clipboard);
extern bool be_selection_outdated_p (enum haiku_clipboard, int64);
extern int64 be_get_clipboard_count (enum haiku_clipboard);
+
+
+extern intmax_t be_display_notification (const char *, const char *,
+ intmax_t, int, const char *);
+
#ifdef __cplusplus
};
-#endif
+#endif /* __cplusplus */
#endif /* _HAIKU_SELECT_H_ */
// Local Variables:
diff --git a/src/haikuterm.c b/src/haikuterm.c
index ed28a806ff2..70984936bf9 100644
--- a/src/haikuterm.c
+++ b/src/haikuterm.c
@@ -4042,6 +4042,19 @@ haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
handled in Lisp. */
haiku_handle_font_change_event (buf, &inev);
break;
+
+ case NOTIFICATION_CLICK_EVENT:
+ /* This code doesn't function, but the why is unknown. */
+#if 0
+ {
+ struct haiku_notification_click_event *b = buf;
+
+ inev.kind = NOTIFICATION_CLICKED_EVENT;
+ inev.arg = make_int (b->id);
+ break;
+ }
+#endif /* 0 */
+
case KEY_UP:
case DUMMY_EVENT:
default:
diff --git a/src/termhooks.h b/src/termhooks.h
index d978819aeca..6bf507aae33 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -337,6 +337,12 @@ enum event_kind
monitor configuration changed. .timestamp gives the time on
which the monitors changed. */
, MONITORS_CHANGED_EVENT
+
+#ifdef HAVE_HAIKU
+ /* In a NOTIFICATION_CLICKED_EVENT, .arg is an integer identifying
+ the notification that was clicked. */
+ , NOTIFICATION_CLICKED_EVENT
+#endif /* HAVE_HAIKU */
};
/* Bit width of an enum event_kind tag at the start of structs and unions. */