diff -rupN vte-0.41.90/src/app.vala vte-0.41.90-patched/src/app.vala --- vte-0.41.90/src/app.vala 2015-08-20 14:26:53.000000000 +0200 +++ vte-0.41.90-patched/src/app.vala 2015-09-15 19:38:39.999947051 +0200 @@ -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 diff -rupN vte-0.41.90/src/caps.cc vte-0.41.90-patched/src/caps.cc --- vte-0.41.90/src/caps.cc 2015-08-20 14:26:53.000000000 +0200 +++ vte-0.41.90-patched/src/caps.cc 2015-09-15 19:22:53.058742000 +0200 @@ -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 -rupN vte-0.41.90/src/marshal.list vte-0.41.90-patched/src/marshal.list --- vte-0.41.90/src/marshal.list 2015-05-12 14:32:16.000000000 +0200 +++ vte-0.41.90-patched/src/marshal.list 2015-09-15 19:23:57.632415651 +0200 @@ -1,4 +1,5 @@ VOID:INT,INT VOID:OBJECT,OBJECT +VOID:STRING,STRING VOID:STRING,UINT VOID:UINT,UINT diff -rupN vte-0.41.90/src/vte/vteterminal.h vte-0.41.90-patched/src/vte/vteterminal.h --- vte-0.41.90/src/vte/vteterminal.h 2015-08-20 14:26:53.000000000 +0200 +++ vte-0.41.90-patched/src/vte/vteterminal.h 2015-09-15 19:36:59.769458153 +0200 @@ -78,6 +78,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); @@ -111,7 +112,7 @@ struct _VteTerminalClass { void (*bell)(VteTerminal* terminal); /* Padding for future expansion. */ - gpointer padding[16]; + gpointer padding[15]; VteTerminalClassPrivate *priv; }; diff -rupN vte-0.41.90/src/vte.cc vte-0.41.90-patched/src/vte.cc --- vte-0.41.90/src/vte.cc 2015-08-20 14:27:19.000000000 +0200 +++ vte-0.41.90-patched/src/vte.cc 2015-09-15 19:32:38.601546927 +0200 @@ -8898,6 +8898,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); @@ -10630,6 +10633,7 @@ vte_terminal_class_init(VteTerminalClass 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; @@ -10703,6 +10707,25 @@ vte_terminal_class_init(VteTerminalClass G_TYPE_NONE, 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 @@ -12720,6 +12743,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) @@ -12767,6 +12800,11 @@ vte_terminal_emit_pending_signals(VteTer 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 -rupN vte-0.41.90/src/vte.sh vte-0.41.90-patched/src/vte.sh --- vte-0.41.90/src/vte.sh 2015-08-20 14:26:53.000000000 +0200 +++ vte-0.41.90-patched/src/vte.sh 2015-09-15 19:37:40.379655627 +0200 @@ -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 diff -rupN vte-0.41.90/src/vteinternal.hh vte-0.41.90-patched/src/vteinternal.hh --- vte-0.41.90/src/vteinternal.hh 2015-08-20 14:26:53.000000000 +0200 +++ vte-0.41.90-patched/src/vteinternal.hh 2015-09-15 19:43:58.858195797 +0200 @@ -367,6 +367,11 @@ public: 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 -rupN vte-0.41.90/src/vteseq-n.gperf vte-0.41.90-patched/src/vteseq-n.gperf --- vte-0.41.90/src/vteseq-n.gperf 2015-08-20 14:26:53.000000000 +0200 +++ vte-0.41.90-patched/src/vteseq-n.gperf 2015-09-15 19:34:10.725313524 +0200 @@ -168,3 +168,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 -rupN vte-0.41.90/src/vteseq.cc vte-0.41.90-patched/src/vteseq.cc --- vte-0.41.90/src/vteseq.cc 2015-08-20 14:26:53.000000000 +0200 +++ vte-0.41.90-patched/src/vteseq.cc 2015-09-15 20:52:23.771616988 +0200 @@ -2227,6 +2227,97 @@ vte_sequence_handler_return_terminal_id 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, (const guchar *)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, (const guchar *)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, (const guchar *)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)