From 3f45cc2d9aefbcae8b6af9f2e1a276ba603a6e2b 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.cc | 4 +++ src/marshal.list | 1 + src/vte.cc | 38 ++++++++++++++++++++++ src/vte/vteterminal.h | 3 +- src/vteinternal.hh | 5 +++ src/vteseq-n.gperf | 1 + src/vteseq.cc | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src/caps.cc b/src/caps.cc index 82078af3f405..e3514eb11f38 100644 --- a/src/caps.cc +++ b/src/caps.cc @@ -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 0276422ec6d4..2c35c685930a 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.cc b/src/vte.cc index 470163c7ac70..646154f701be 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -9643,6 +9643,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); @@ -11387,6 +11390,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; @@ -11461,6 +11465,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 * @@ -13482,6 +13505,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) @@ -13529,6 +13562,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/vte/vteterminal.h b/src/vte/vteterminal.h index 857819f385d0..e5d4d9dc998f 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -79,6 +79,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); @@ -112,7 +113,7 @@ struct _VteTerminalClass { void (*bell)(VteTerminal* terminal); /* Padding for future expansion. */ - gpointer padding[16]; + gpointer padding[15]; VteTerminalClassPrivate *priv; }; diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 7573ada9aaf4..cdfb8761d1c6 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -389,6 +389,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 --git a/src/vteseq-n.gperf b/src/vteseq-n.gperf index ad5b6d99f938..287caa5b5928 100644 --- a/src/vteseq-n.gperf +++ b/src/vteseq-n.gperf @@ -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 --git a/src/vteseq.cc b/src/vteseq.cc index af27ff5ef277..368e6700caf8 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -2243,6 +2243,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, (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) -- 2.5.0 From 391e5c57c07666476f8de883e4bf4a4a65c27aac 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 2d211caa2f17..1c0543bd9d26 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.5.0 From 77886ec3ddabc54ac06767a5a875beaa9704a9be 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 c233a488d1d7..6f7a7561271e 100644 --- a/src/app.vala +++ b/src/app.vala @@ -318,6 +318,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(false); @@ -748,6 +750,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.5.0