From 7eab05481b294ad7bfe35df1341744bb4e010a2f Mon Sep 17 00:00:00 2001 From: Boris Astardzhiev Date: Sun, 8 Mar 2020 19:17:18 +0200 Subject: [PATCH] Restore scrollback history on save session. It's configurable via UI preferences. TAB_ID environment variable for every screen. --- terminal/main.c | 6 +- terminal/terminal-app.c | 1 + terminal/terminal-options.c | 35 +++++ terminal/terminal-options.h | 3 + terminal/terminal-preferences-dialog.c | 10 ++ terminal/terminal-preferences.c | 22 +++ terminal/terminal-preferences.glade | 53 +++++++ terminal/terminal-screen.c | 204 ++++++++++++++++++++++++- terminal/terminal-screen.h | 3 + terminal/terminal-window.c | 22 +++ terminal/terminal-window.h | 1 + 11 files changed, 357 insertions(+), 3 deletions(-) diff --git a/terminal/main.c b/terminal/main.c index 9252852c..d2ac919f 100644 --- a/terminal/main.c +++ b/terminal/main.c @@ -120,7 +120,7 @@ usage (void) " -x, --execute; -e, --command=%s; -T, --title=%s;\n" " --dynamic-title-mode=%s ('replace', 'before', 'after', 'none');\n" " --initial-title=%s; --working-directory=%s; -H, --hold;\n" - " --active-tab; --color-text=%s; --color-bg=%s\n\n", + " --active-tab; --color-text=%s; --color-bg=%s; --tab-id=%s\n\n", _("Tab Options"), /* parameter of --command */ _("command"), @@ -135,7 +135,9 @@ usage (void) /* parameter of --color-text */ _("color"), /* parameter of --color-bg */ - _("color")); + _("color"), + /* parameter of --tab-id */ + _("tab-id")); g_print ("%s:\n" " --display=%s; --geometry=%s; --role=%s; --drop-down;\n" diff --git a/terminal/terminal-app.c b/terminal/terminal-app.c index 6e46aa40..7f299e72 100644 --- a/terminal/terminal-app.c +++ b/terminal/terminal-app.c @@ -579,6 +579,7 @@ terminal_app_save_yourself (XfceSMClient *client, if (n++ != 0) result = g_slist_append (result, g_strdup ("--window")); result = g_slist_concat (result, terminal_window_get_restart_command (lp->data)); + terminal_window_save_state (lp->data); } /* no windows were saved - this can happen if there is only a dropdown window diff --git a/terminal/terminal-options.c b/terminal/terminal-options.c index 6ab09adf..0f29acb0 100644 --- a/terminal/terminal-options.c +++ b/terminal/terminal-options.c @@ -332,6 +332,21 @@ terminal_window_attr_parse (gint argc, tab_attr->initial_title = g_strdup (s); } } + else if (terminal_option_cmp ("tab-id", 0, argc, argv, &n, &s)) + { + if (G_UNLIKELY (s == NULL)) + { + g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED, + _("Option \"--tab-id\" requires specifying " + "the tab unique id as its parameter")); + goto failed; + } + else + { + g_free (tab_attr->tab_id); + tab_attr->tab_id = g_strdup (s); + } + } else if (terminal_option_cmp ("hold", 'H', argc, argv, &n, NULL)) { tab_attr->hold = TRUE; @@ -635,6 +650,23 @@ terminal_window_attr_new (void) +/** + **/ +gchar* +terminal_tab_id_new (void) +{ + gchar *seed, *tab_id; + + seed = g_strdup_printf ("%s-%u-%u", PACKAGE_NAME, (guint) time (NULL), g_random_int ()); + tab_id = g_compute_checksum_for_string (G_CHECKSUM_SHA256, seed, strlen (seed)); + tab_id[16] = '\0'; + g_free (seed); + + return tab_id; +} + + + /** **/ TerminalTabAttr* @@ -644,6 +676,8 @@ terminal_tab_attr_new (void) tab_attr->dynamic_title_mode = TERMINAL_TITLE_DEFAULT; tab_attr->position = -1; + /* Every tab has unique value */ + tab_attr->tab_id = terminal_tab_id_new (); return tab_attr; } @@ -663,6 +697,7 @@ terminal_tab_attr_free (TerminalTabAttr *attr) g_free (attr->directory); g_free (attr->title); g_free (attr->initial_title); + g_free (attr->tab_id); g_free (attr->color_text); g_free (attr->color_bg); g_free (attr->color_title); diff --git a/terminal/terminal-options.h b/terminal/terminal-options.h index c587ed51..415fc9fb 100644 --- a/terminal/terminal-options.h +++ b/terminal/terminal-options.h @@ -58,6 +58,7 @@ typedef struct gchar *directory; gchar *title; gchar *initial_title; + gchar *tab_id; gchar *color_text; gchar *color_bg; gchar *color_title; @@ -115,6 +116,8 @@ void terminal_tab_attr_free (TerminalTabAttr *attr); void terminal_window_attr_free (TerminalWindowAttr *attr); +gchar *terminal_tab_id_new (void); + G_END_DECLS #endif /* !TERMINAL_OPTIONS_H */ diff --git a/terminal/terminal-preferences-dialog.c b/terminal/terminal-preferences-dialog.c index 8be9645d..b59a2010 100644 --- a/terminal/terminal-preferences-dialog.c +++ b/terminal/terminal-preferences-dialog.c @@ -169,6 +169,7 @@ terminal_preferences_dialog_init (TerminalPreferencesDialog *dialog) "use-default-working-dir", "scrolling-on-output", "scrolling-on-keystroke", "scrolling-bar", "scrolling-unlimited", "misc-cursor-shape", + "scrolling-save-session", "misc-cursor-shape", "misc-cursor-blinks", "font-allow-bold", "font-use-system", "text-blink-mode", "misc-show-unsafe-paste-dialog", "misc-menubar-default", @@ -278,6 +279,7 @@ error: BIND_PROPERTIES ("default-working-dir", "text"); BIND_PROPERTIES ("word-chars", "text"); BIND_PROPERTIES ("scrolling-lines", "value"); + BIND_PROPERTIES ("scrolling-save-session-lines", "value"); BIND_PROPERTIES ("tab-activity-timeout", "value"); BIND_PROPERTIES ("background-darkness", "value"); BIND_PROPERTIES ("background-image-shading", "value"); @@ -361,6 +363,14 @@ error: object2, "sensitive", G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); + /* scrollback save session button */ + object = gtk_builder_get_object (GTK_BUILDER (dialog), "scrolling-save-session"); + object2 = gtk_builder_get_object (GTK_BUILDER (dialog), "scrolling-save-session-lines"); + terminal_return_if_fail (G_IS_OBJECT (object) && G_IS_OBJECT (object2)); + g_object_bind_property (object, "active", + object2, "sensitive", + G_BINDING_SYNC_CREATE); + /* use system font button */ object = gtk_builder_get_object (GTK_BUILDER (dialog), "font-use-system"); object2 = gtk_builder_get_object (GTK_BUILDER (dialog), "font-name"); diff --git a/terminal/terminal-preferences.c b/terminal/terminal-preferences.c index 3f941e7b..314b9773 100644 --- a/terminal/terminal-preferences.c +++ b/terminal/terminal-preferences.c @@ -120,6 +120,8 @@ enum PROP_SCROLLING_ON_OUTPUT, PROP_SCROLLING_ON_KEYSTROKE, PROP_SCROLLING_UNLIMITED, + PROP_SCROLLING_SAVE_SESSION, + PROP_SCROLLING_SAVE_SESSION_LINES, PROP_SHORTCUTS_NO_HELPKEY, PROP_SHORTCUTS_NO_MENUKEY, PROP_SHORTCUTS_NO_MNEMONICS, @@ -1111,6 +1113,26 @@ terminal_preferences_class_init (TerminalPreferencesClass *klass) FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * TerminalPreferences:scrolling-save-session-lines: + **/ + preferences_props[PROP_SCROLLING_SAVE_SESSION_LINES] = + g_param_spec_uint ("scrolling-save-session-lines", + NULL, + "ScrollingSaveSessionLines", + 0u, 1024u * 1024u, 1000u, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * TerminalPreferences:scrolling-save-session: + **/ + preferences_props[PROP_SCROLLING_SAVE_SESSION] = + g_param_spec_boolean ("scrolling-save-session", + NULL, + "ScrollingSaveSession", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** * TerminalPreferences:scrolling-on-output: **/ diff --git a/terminal/terminal-preferences.glade b/terminal/terminal-preferences.glade index 8e207393..af191fc5 100644 --- a/terminal/terminal-preferences.glade +++ b/terminal/terminal-preferences.glade @@ -249,6 +249,12 @@ 1 10 + + 1 + 5242880 + 1 + 10 + 30 1 @@ -660,6 +666,53 @@ 2 + + + True + False + start + Save session scrollback: + True + + + 0 + 3 + + + + + True + True + Specifies the number of lines that will be saved with session. + start + + False + False + scrolling-save-session-line + True + if-valid + + + 1 + 3 + + + + + Save scrollback with session + True + True + False + This option controls whether saving scrollback with session is enabled. + start + True + True + + + 2 + 3 + + True diff --git a/terminal/terminal-screen.c b/terminal/terminal-screen.c index dc5c85f4..ba14f9a2 100644 --- a/terminal/terminal-screen.c +++ b/terminal/terminal-screen.c @@ -68,6 +68,7 @@ /* minimum terminal dimensions */ #define MIN_COLUMNS 4 #define MIN_ROWS 1 +#define SCROLLBACK_PATH "xfce4/terminal" @@ -194,6 +195,7 @@ struct _TerminalScreen gchar **custom_command; gchar *custom_title; gchar *initial_title; + gchar *tab_id; gchar *custom_fg_color; gchar *custom_bg_color; @@ -388,6 +390,7 @@ terminal_screen_finalize (GObject *object) g_free (screen->working_directory); g_free (screen->custom_title); g_free (screen->initial_title); + g_free (screen->tab_id); g_free (screen->custom_fg_color); g_free (screen->custom_bg_color); g_free (screen->custom_title_color); @@ -864,7 +867,7 @@ terminal_screen_get_child_environment (TerminalScreen *screen) env = g_listenv (); n = g_strv_length (env); - result = g_new (gchar *, n + 4); + result = g_new (gchar *, n + 5); for (n = 0, p = env; *p != NULL; ++p) { @@ -910,6 +913,7 @@ terminal_screen_get_child_environment (TerminalScreen *screen) result[n++] = g_strdup_printf ("DISPLAY=%s", display_name); } #endif + result[n++] = g_strdup_printf("TAB_ID=%s", screen->tab_id); result[n] = NULL; @@ -1827,6 +1831,74 @@ terminal_screen_paste_unsafe_text (TerminalScreen *screen, +static void +terminal_screen_revive_scrollback (VteTerminal *terminal, + TerminalScreen *screen) +{ + GFile *file; + GInputStream *base_stream; + gchar *filename, *scrollback_path; + GError *error = NULL; + gboolean scrolling_save_session; + + terminal_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + g_object_get (G_OBJECT (screen->preferences), + "scrolling-save-session", &scrolling_save_session, + NULL); + if (scrolling_save_session == FALSE) + return; + + scrollback_path = g_strdup_printf ("%s/%s", SCROLLBACK_PATH, screen->tab_id); + if (scrollback_path == NULL) + return; + + filename = xfce_resource_lookup (XFCE_RESOURCE_CACHE, scrollback_path); + g_free (scrollback_path); + if (filename == NULL) + return; + + file = g_file_new_for_path (filename); + base_stream = G_INPUT_STREAM (g_file_read (file, NULL, &error)); + if (base_stream) + { + GInputStream *conv_stream; + GConverter *converter; + char *line; + gsize linelen; + + converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP)); + conv_stream = g_converter_input_stream_new (base_stream, converter); + g_object_unref (converter); + if (conv_stream) + { + GDataInputStream *stream; + + stream = g_data_input_stream_new (G_INPUT_STREAM (conv_stream)); + if (stream) + { + while ((line = g_data_input_stream_read_line_utf8 (stream, &linelen, NULL, NULL)) != NULL) + { + vte_terminal_feed (VTE_TERMINAL (screen->terminal), line, linelen); + vte_terminal_feed (VTE_TERMINAL (screen->terminal), "\r\n", 2); + g_free (line); + } + g_object_unref (stream); + } + g_object_unref (conv_stream); + } + g_object_unref (base_stream); + } + + if (error) + g_error_free (error); + + g_object_unref (file); + g_remove (filename); + g_free (filename); +} + + #if VTE_CHECK_VERSION (0, 48, 0) static void terminal_screen_spawn_async_cb (VteTerminal *terminal, @@ -1841,6 +1913,7 @@ terminal_screen_spawn_async_cb (VteTerminal *terminal, screen->pid = pid; + terminal_screen_revive_scrollback (terminal, screen); if (error) { xfce_dialog_show_error (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (screen))), @@ -1883,6 +1956,8 @@ terminal_screen_new (TerminalTabAttr *attr, terminal_screen_set_custom_title (screen, attr->title); if (attr->initial_title != NULL) screen->initial_title = g_strdup (attr->initial_title); + if (attr->tab_id != NULL) + screen->tab_id = g_strdup (attr->tab_id); screen->dynamic_title_mode = attr->dynamic_title_mode; screen->hold = attr->hold; vte_terminal_set_size (VTE_TERMINAL (screen->terminal), columns, rows); @@ -2366,6 +2441,34 @@ terminal_screen_get_working_directory (TerminalScreen *screen) +/** + * terminal_screen_get_tab_id: + * @screen : A #TerminalScreen. + **/ +const gchar * +terminal_screen_get_tab_id (TerminalScreen *screen) +{ + terminal_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + return screen->tab_id; +} + + + +/** + * terminal_screen_set_tab_id: + * @screen : A #TerminalScreen. + **/ +void +terminal_screen_set_tab_id (TerminalScreen *screen) +{ + terminal_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + screen->tab_id = terminal_tab_id_new (); +} + + + /** * terminal_screen_set_working_directory: * @screen : A #TerminalScreen. @@ -2541,6 +2644,7 @@ GSList* terminal_screen_get_restart_command (TerminalScreen *screen) { const gchar *directory; + const gchar *tab_id; GSList *result = NULL; terminal_return_val_if_fail (TERMINAL_IS_SCREEN (screen), NULL); @@ -2567,6 +2671,13 @@ terminal_screen_get_restart_command (TerminalScreen *screen) if (G_UNLIKELY (screen->hold)) result = g_slist_prepend (result, g_strdup ("--hold")); + tab_id = terminal_screen_get_tab_id (screen); + if (G_LIKELY (tab_id != NULL)) + { + result = g_slist_prepend (result, g_strdup ("--tab-id")); + result = g_slist_prepend (result, g_strdup (tab_id)); + } + return result; } @@ -2931,6 +3042,97 @@ terminal_screen_set_scroll_on_output (TerminalScreen *screen, +static void +terminal_screen_save_contents_nrows (TerminalScreen *screen, + glong nrows, + GOutputStream *stream, + GError *error) +{ + glong startrow, startcol, endrow, endcol; + char *output; + + terminal_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + vte_terminal_get_cursor_position (VTE_TERMINAL (screen->terminal), &endcol, &endrow); + startrow = endrow <= nrows ? -1 : (endrow - nrows); + startcol = vte_terminal_get_column_count (VTE_TERMINAL (screen->terminal)); + + output = vte_terminal_get_text_range (VTE_TERMINAL (screen->terminal), + startrow, + startcol, + endrow + 1, + endcol, + NULL, + NULL, + NULL); + + if (output) + { + g_output_stream_write_all (stream, output, strlen (output), NULL, NULL, &error); + g_free (output); + } +} + + + +void +terminal_screen_save_scrollback (TerminalScreen *screen) +{ + GFile *file; + GOutputStream *stream; + gchar *filename, *scrollback_path; + GError *error = NULL; + gboolean scrolling_save_session; + + terminal_return_if_fail (TERMINAL_IS_SCREEN (screen)); + + g_object_get (G_OBJECT (screen->preferences), + "scrolling-save-session", &scrolling_save_session, + NULL); + if (scrolling_save_session == FALSE) + return; + + scrollback_path = g_strdup_printf ("%s/%s", SCROLLBACK_PATH, screen->tab_id); + if (scrollback_path == NULL) + return; + + filename = xfce_resource_save_location (XFCE_RESOURCE_CACHE, scrollback_path, TRUE); + g_free (scrollback_path); + if (filename == NULL) + return; + + file = g_file_new_for_path (filename); + stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_PRIVATE, NULL, &error)); + if (stream) + { + GOutputStream *conv_stream; + GConverter *converter; + guint max_lines; + + g_object_get (G_OBJECT (screen->preferences), + "scrolling-save-session-lines", &max_lines, + NULL); + + converter = G_CONVERTER (g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1)); + conv_stream = g_converter_output_stream_new (stream, converter); + g_object_unref (converter); + if (conv_stream) + { + terminal_screen_save_contents_nrows (screen, (glong) max_lines, conv_stream, error); + g_object_unref (conv_stream); + } + g_object_unref (stream); + } + + if (error) + g_error_free (error); + + g_object_unref (file); + g_free (filename); +} + + + void terminal_screen_save_contents (TerminalScreen *screen, GOutputStream *stream, diff --git a/terminal/terminal-screen.h b/terminal/terminal-screen.h index 6aa27922..04fa7c24 100644 --- a/terminal/terminal-screen.h +++ b/terminal/terminal-screen.h @@ -74,6 +74,8 @@ gchar *terminal_screen_get_title (TerminalScreen *scree const gchar *terminal_screen_get_working_directory (TerminalScreen *screen); void terminal_screen_set_working_directory (TerminalScreen *screen, const gchar *directory); +const gchar *terminal_screen_get_tab_id (TerminalScreen *screen); +void terminal_screen_set_tab_id (TerminalScreen *screen); gboolean terminal_screen_has_selection (TerminalScreen *screen); @@ -121,6 +123,7 @@ gboolean terminal_screen_get_scroll_on_output (TerminalScreen *scree void terminal_screen_set_scroll_on_output (TerminalScreen *screen, gboolean enabled); +void terminal_screen_save_scrollback (TerminalScreen *screen); void terminal_screen_save_contents (TerminalScreen *screen, GOutputStream *stream, GError *error); diff --git a/terminal/terminal-window.c b/terminal/terminal-window.c index 97a2200f..14fe9596 100644 --- a/terminal/terminal-window.c +++ b/terminal/terminal-window.c @@ -1733,6 +1733,7 @@ terminal_window_action_new_tab (GtkAction *action, terminal_screen_set_working_directory (terminal, directory); g_free (directory); } + terminal_screen_set_tab_id (terminal); terminal_window_add (window, terminal); terminal_screen_launch_child (terminal); @@ -2895,6 +2896,27 @@ terminal_window_notebook_show_tabs (TerminalWindow *window) +/** + * terminal_window_save_state + * @window : A #TerminalWindow. + **/ +void +terminal_window_save_state (TerminalWindow *window) +{ + GList *children, *lp; + + terminal_return_val_if_fail (TERMINAL_IS_WINDOW (window), NULL); + + /* save scrollback history per tab */ + children = gtk_container_get_children (GTK_CONTAINER (window->priv->notebook)); + for (lp = children; lp != NULL; lp = lp->next) + terminal_screen_save_scrollback (lp->data); + + g_list_free (children); +} + + + /** * terminal_window_get_restart_command: * @window : A #TerminalWindow. diff --git a/terminal/terminal-window.h b/terminal/terminal-window.h index 75d17d6c..71e401fc 100644 --- a/terminal/terminal-window.h +++ b/terminal/terminal-window.h @@ -68,6 +68,7 @@ TerminalScreen *terminal_window_get_active (TerminalWindow void terminal_window_notebook_show_tabs (TerminalWindow *window); +void terminal_window_save_state (TerminalWindow *window); GSList *terminal_window_get_restart_command (TerminalWindow *window); void terminal_window_set_grid_size (TerminalWindow *window, -- 2.20.1