From c4cc87d1fc82b20190ad1fc491385df8a75db848 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Wed, 7 Jan 2015 16:01:00 +0100 Subject: [PATCH 1/3] emulation: Add sequences and signals for desktop notification Add sequences OSC 777 ; notify ; SUMMARY ; BODY BEL OSC 777 ; notify ; SUMMARY BEL OSC 777 ; notify ; SUMMARY ; BODY ST OSC 777 ; notify ; SUMMARY ST that let terminal applications send a notification to the desktop environment. Based on Enlightenment's Terminology: https://phab.enlightenment.org/T1765 https://bugzilla.gnome.org/show_bug.cgi?id=711059 --- src/caps.c | 4 +++ src/marshal.list | 1 + src/vte-private.h | 5 +++ src/vte.c | 38 +++++++++++++++++++++++ src/vteseq-n.gperf | 1 + src/vteseq.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vteterminal.h | 3 +- 7 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/caps.c b/src/caps.c index b51882f..0fe0661 100644 --- a/src/caps.c +++ b/src/caps.c @@ -254,6 +254,8 @@ const char _vte_xterm_capability_strings[] = ENTRY(OSC "117" BEL, "reset-highlight-background-color") ENTRY(OSC "118" BEL, "reset-tek-cursor-color") ENTRY(OSC "119" BEL, "reset-highlight-foreground-color") + ENTRY(OSC "777;%s;%s;%s" BEL, "send-notification") + ENTRY(OSC "777;%s;%s" BEL, "send-notification") COMMENT(/* Set text parameters, ST-terminated versions. */) ENTRY(OSC ";%s" ST, "set-icon-and-window-title") COMMENT(/* undocumented default */) @@ -289,6 +291,8 @@ const char _vte_xterm_capability_strings[] = ENTRY(OSC "117" ST, "reset-highlight-background-color") ENTRY(OSC "118" ST, "reset-tek-cursor-color") ENTRY(OSC "119" ST, "reset-highlight-foreground-color") + ENTRY(OSC "777;%s;%s;%s" ST, "send-notification") + ENTRY(OSC "777;%s;%s" ST, "send-notification") COMMENT(/* These may be bogus, I can't find docs for them anywhere (#104154). */) ENTRY(OSC "21;%s" BEL, "set-text-property-21") diff --git a/src/marshal.list b/src/marshal.list index 0276422..2c35c68 100644 --- a/src/marshal.list +++ b/src/marshal.list @@ -1,4 +1,5 @@ VOID:INT,INT VOID:OBJECT,OBJECT +VOID:STRING,STRING VOID:STRING,UINT VOID:UINT,UINT diff --git a/src/vte-private.h b/src/vte-private.h index 43d2266..cbb9402 100644 --- a/src/vte-private.h +++ b/src/vte-private.h @@ -421,6 +421,11 @@ struct _VteTerminalPrivate { gboolean cursor_moved_pending; gboolean contents_changed_pending; + /* desktop notification */ + gboolean notification_received; + gchar *notification_summary; + gchar *notification_body; + /* window name changes */ gchar *window_title; gchar *window_title_changed; diff --git a/src/vte.c b/src/vte.c index d49fccd..8125633 100644 --- a/src/vte.c +++ b/src/vte.c @@ -8581,6 +8581,9 @@ vte_terminal_finalize(GObject *object) remove_update_timeout (terminal); + g_free (terminal->pvt->notification_summary); + g_free (terminal->pvt->notification_body); + /* discard title updates */ g_free(terminal->pvt->window_title); g_free(terminal->pvt->window_title_changed); @@ -10314,6 +10317,7 @@ vte_terminal_class_init(VteTerminalClass *klass) klass->child_exited = NULL; klass->encoding_changed = NULL; klass->char_size_changed = NULL; + klass->notification_received = NULL; klass->window_title_changed = NULL; klass->icon_title_changed = NULL; klass->selection_changed = NULL; @@ -10388,6 +10392,25 @@ vte_terminal_class_init(VteTerminalClass *klass) 1, G_TYPE_INT); /** + * VteTerminal::notification-received: + * @vteterminal: the object which received the signal + * @summary: The summary + * @body: (allow-none): Extra optional text + * + * Emitted when a process running in the terminal wants to + * send a notification to the desktop environment. + */ + g_signal_new(I_("notification-received"), + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(VteTerminalClass, notification_received), + NULL, + NULL, + _vte_marshal_VOID__STRING_STRING, + G_TYPE_NONE, + 2, G_TYPE_STRING, G_TYPE_STRING); + + /** * VteTerminal::window-title-changed: * @vteterminal: the object which received the signal * @@ -12396,6 +12419,16 @@ need_processing (VteTerminal *terminal) return _vte_incoming_chunks_length (terminal->pvt->incoming) != 0; } +static void +vte_terminal_emit_notification_received (VteTerminal *terminal) +{ + _vte_debug_print (VTE_DEBUG_SIGNALS, + "Emitting `notification-received'.\n"); + g_signal_emit_by_name (terminal, "notification-received", + terminal->pvt->notification_summary, + terminal->pvt->notification_body); +} + /* Emit an "icon-title-changed" signal. */ static void vte_terminal_emit_icon_title_changed(VteTerminal *terminal) @@ -12443,6 +12476,11 @@ vte_terminal_emit_pending_signals(VteTerminal *terminal) vte_terminal_emit_adjustment_changed (terminal); + if (terminal->pvt->notification_received) { + vte_terminal_emit_notification_received (terminal); + terminal->pvt->notification_received = FALSE; + } + if (terminal->pvt->window_title_changed) { g_free (terminal->pvt->window_title); terminal->pvt->window_title = terminal->pvt->window_title_changed; diff --git a/src/vteseq-n.gperf b/src/vteseq-n.gperf index f28625b..7ee43be 100644 --- a/src/vteseq-n.gperf +++ b/src/vteseq-n.gperf @@ -167,3 +167,4 @@ struct vteseq_n_struct { #"reset-mouse-cursor-foreground-color", VTE_SEQUENCE_HANDLER_NULL "set-current-directory-uri", VTE_SEQUENCE_HANDLER(vte_sequence_handler_set_current_directory_uri) "set-current-file-uri", VTE_SEQUENCE_HANDLER(vte_sequence_handler_set_current_file_uri) +"send-notification", VTE_SEQUENCE_HANDLER(vte_sequence_handler_send_notification) diff --git a/src/vteseq.c b/src/vteseq.c index b2f4b7d..eb34848 100644 --- a/src/vteseq.c +++ b/src/vteseq.c @@ -2221,6 +2221,96 @@ vte_sequence_handler_return_terminal_id (VteTerminal *terminal, GValueArray *par vte_sequence_handler_send_primary_device_attributes (terminal, params); } +static void +vte_sequence_handler_send_notification (VteTerminal *terminal, GValueArray *params) +{ + GValue *value; + const char *end; + char *option = NULL; + char *str = NULL; + char *p, *validated; + + g_clear_pointer (&terminal->pvt->notification_summary, g_free); + g_clear_pointer (&terminal->pvt->notification_body, g_free); + + value = g_value_array_get_nth (params, 0); + if (value == NULL) { + goto out; + } + + if (G_VALUE_HOLDS_STRING (value)) { + option = g_value_dup_string (value); + } else if (G_VALUE_HOLDS_POINTER (value)) { + option = vte_ucs4_to_utf8 (terminal, g_value_get_pointer (value)); + } else { + goto out; + } + + if (g_strcmp0 (option, "notify") != 0) { + goto out; + } + + value = g_value_array_get_nth (params, 1); + if (value == NULL) { + goto out; + } + + if (G_VALUE_HOLDS_STRING (value)) { + str = g_value_dup_string (value); + } else if (G_VALUE_HOLDS_POINTER (value)) { + str = vte_ucs4_to_utf8 (terminal, g_value_get_pointer (value)); + } else { + goto out; + } + + g_utf8_validate (str, strlen (str), &end); + validated = g_strndup (str, end - str); + + /* No control characters allowed. */ + for (p = validated; *p != '\0'; p++) { + if ((*p & 0x1f) == *p) { + *p = ' '; + } + } + + terminal->pvt->notification_summary = validated; + g_free (str); + + terminal->pvt->notification_received = TRUE; + if (params->n_values == 2) { + goto out; + } + + value = g_value_array_get_nth (params, 2); + if (value == NULL) { + goto out; + } + + if (G_VALUE_HOLDS_STRING (value)) { + str = g_value_dup_string (value); + } else if (G_VALUE_HOLDS_POINTER (value)) { + str = vte_ucs4_to_utf8 (terminal, g_value_get_pointer (value)); + } else { + goto out; + } + + g_utf8_validate (str, strlen (str), &end); + validated = g_strndup (str, end - str); + + /* No control characters allowed. */ + for (p = validated; *p != '\0'; p++) { + if ((*p & 0x1f) == *p) { + *p = ' '; + } + } + + terminal->pvt->notification_body = validated; + g_free (str); + + out: + g_free (option); +} + /* Send secondary device attributes. */ static void vte_sequence_handler_send_secondary_device_attributes (VteTerminal *terminal, GValueArray *params) diff --git a/src/vteterminal.h b/src/vteterminal.h index 08f3b8f..d6ba7e2 100644 --- a/src/vteterminal.h +++ b/src/vteterminal.h @@ -72,6 +72,7 @@ struct _VteTerminalClass { void (*child_exited)(VteTerminal* terminal, int status); void (*encoding_changed)(VteTerminal* terminal); void (*char_size_changed)(VteTerminal* terminal, guint char_width, guint char_height); + void (*notification_received)(VteTerminal* terminal, const gchar *summary, const gchar *body); void (*window_title_changed)(VteTerminal* terminal); void (*icon_title_changed)(VteTerminal* terminal); void (*selection_changed)(VteTerminal* terminal); @@ -105,7 +106,7 @@ struct _VteTerminalClass { void (*bell)(VteTerminal* terminal); /* Padding for future expansion. */ - gpointer padding[16]; + gpointer padding[15]; VteTerminalClassPrivate *priv; }; -- 2.1.0 From 7decbc150a5f6764c0b1778133247fe542622189 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 29 Jan 2015 13:09:17 +0100 Subject: [PATCH 2/3] vte.sh: Emit OSC 777 from PROMPT_COMMAND https://bugzilla.gnome.org/show_bug.cgi?id=711059 --- src/vte.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vte.sh b/src/vte.sh index 2d211ca..1c0543b 100644 --- a/src/vte.sh +++ b/src/vte.sh @@ -50,9 +50,11 @@ __vte_osc7 () { } __vte_prompt_command() { + local command=$(HISTTIMEFORMAT= history 1 | sed 's/^ *[0-9]\+ *//') + command="${command//;/ }" local pwd='~' [ "$PWD" != "$HOME" ] && pwd=${PWD/#$HOME\//\~\/} - printf "\033]0;%s@%s:%s\007%s" "${USER}" "${HOSTNAME%%.*}" "${pwd}" "$(__vte_osc7)" + printf "\033]777;notify;Command completed;%s\007\033]0;%s@%s:%s\007%s" "${command}" "${USER}" "${HOSTNAME%%.*}" "${pwd}" "$(__vte_osc7)" } case "$TERM" in -- 2.1.0 From f0f281dd333f8639eb6e8ddb40bea9c097483243 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 22 Jan 2015 16:37:10 +0100 Subject: [PATCH 3/3] vteapp: Add a test for the notification-received signal --- src/app.vala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app.vala b/src/app.vala index e102e46..cc22480 100644 --- a/src/app.vala +++ b/src/app.vala @@ -102,6 +102,8 @@ class Window : Gtk.ApplicationWindow if (App.Options.object_notifications) terminal.notify.connect(notify_cb); + terminal.notification_received.connect(notification_received_cb); + /* Settings */ if (App.Options.no_double_buffer) terminal.set_double_buffered(true); @@ -511,6 +513,11 @@ class Window : Gtk.ApplicationWindow set_title(terminal.get_window_title()); } + private void notification_received_cb(Vte.Terminal terminal, string summary, string? body) + { + print ("[%s]: %s\n", summary, body); + } + } /* class Window */ class App : Gtk.Application -- 2.1.0