From 7f5342005b81a7a226a9eb634571ba0799f12bc4 Mon Sep 17 00:00:00 2001 From: lenemter Date: Tue, 27 Jan 2026 16:19:46 +0300 Subject: [PATCH 1/3] Don't store clipboard object in TerminalWidget We can always get it from Gdk.DIsplay --- src/Widgets/TerminalWidget.vala | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Widgets/TerminalWidget.vala b/src/Widgets/TerminalWidget.vala index ef229698e6..ce1946f734 100644 --- a/src/Widgets/TerminalWidget.vala +++ b/src/Widgets/TerminalWidget.vala @@ -104,8 +104,6 @@ namespace Terminal { public bool killed { get; private set; default = false; } - private unowned Gdk.Clipboard clipboard; - private GLib.SimpleAction open_in_browser_action; private GLib.SimpleAction copy_action; private GLib.SimpleAction copy_output_action; @@ -228,12 +226,9 @@ namespace Terminal { contents_changed.connect (on_contents_changed); child_exited.connect (on_child_exited); window_title_changed.connect (on_window_title_changed); - ulong once = 0; - once = realize.connect (() => { - clipboard = Gdk.Display.get_default ().get_clipboard (); - clipboard.changed.connect (update_actions_state); - disconnect (once); - }); + + unowned var clipboard = Gdk.Display.get_default ().get_clipboard (); + clipboard.changed.connect (update_actions_state); var drop_target = new Gtk.DropTarget (Type.STRING, Gdk.DragAction.COPY); drop_target.drop.connect (on_drop); @@ -468,7 +463,7 @@ namespace Terminal { } } else if ( match_keycode (Gdk.Key.v, keycode) && (natural || shift_pressed) && - clipboard.get_formats ().contain_gtype (Type.STRING) + Gdk.Display.get_default ().get_clipboard ().get_formats ().contain_gtype (Type.STRING) ) { paste_clipboard (); return true; @@ -498,6 +493,7 @@ namespace Terminal { open_in_browser_action.set_enabled (appinfo != null); // Update the "Paste" menu option + unowned var clipboard = Gdk.Display.get_default ().get_clipboard (); var clipboard_has_string = clipboard.formats != null && clipboard.formats.contain_gtype (Type.STRING); paste_action.set_enabled (clipboard_has_string); @@ -588,15 +584,14 @@ namespace Terminal { protected override void copy_clipboard () { if (link_uri != null && !get_has_selection ()) { - clipboard.set_text (link_uri); + Gdk.Display.get_default ().get_clipboard ().set_text (link_uri); } else { base.copy_clipboard (); } } private void copy_output () { - var output = get_last_output (); - clipboard.set_text (output); + Gdk.Display.get_default ().get_clipboard ().set_text (get_last_output ()); } public delegate void ConfirmedActionCallback (bool confirmed); @@ -683,6 +678,8 @@ namespace Terminal { } protected override void paste_clipboard () { + unowned var clipboard = Gdk.Display.get_default ().get_clipboard (); + var content_provider = clipboard.get_content (); if (content_provider != null) { try { @@ -696,8 +693,10 @@ namespace Terminal { } } else { clipboard.read_text_async.begin (null, (obj, res) => { + unowned var _clipboard = (Gdk.Clipboard) obj; + try { - var text = clipboard.read_text_async.end (res); + var text = _clipboard.read_text_async.end (res); validated_paste (text); } catch (Error e) { warning ("Error reading text from clipboard - %s", e.message); From bc750ce355507dec6b0aa0bdd334892216bdedf7 Mon Sep 17 00:00:00 2001 From: lenemter Date: Tue, 27 Jan 2026 16:54:46 +0300 Subject: [PATCH 2/3] introduce MainWindow.get_page () My goal here is to gradually reduce the use of TerminalWidget.tab because, well, TerminalWidget in the Terminal and IMHO shouldn't contain info about it's page --- src/Application.vala | 17 +++++++++-------- src/MainWindow.vala | 23 ++++++++++++++--------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/Application.vala b/src/Application.vala index c83a962a34..0a4806f4b3 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -33,14 +33,15 @@ public class Terminal.Application : Gtk.Application { var act = new SimpleAction ("process-finished", VariantType.STRING); add_action (act); - act.activate.connect ((v) => { - MainWindow window_to_present = (MainWindow)active_window; - size_t len; - var tid = v.get_string (out len); - foreach (var window in (List) get_windows ()) { - var terminal = window.get_terminal (tid); - if (terminal != null) { - window.set_active_terminal_tab (terminal.tab); + act.activate.connect ((parameter) => { + unowned var window_to_present = active_window; + + unowned var terminal_id = parameter.get_string (); + + foreach (unowned var window in (List) get_windows ()) { + unowned var page = window.get_page (terminal_id); + if (page != null) { + window.notebook.selected_page = page; window_to_present = window; break; } diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 58f8dba2c0..63b2e7ddf5 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -6,7 +6,6 @@ namespace Terminal { public class MainWindow : Adw.Window { private Adw.HeaderBar header; - public TerminalView notebook { get; private set construct; } private Terminal.Widgets.SearchToolbar search_toolbar; private Gtk.Button unfullscreen_button; private Gtk.Label title_label; @@ -23,7 +22,7 @@ namespace Terminal { public bool recreate_tabs { get; construct; default = true; } public Terminal.Application app { get; construct; } public SimpleActionGroup actions { get; construct; } - + public TerminalView notebook { get; private set; } public TerminalWidget? current_terminal { get; private set; default = null; } public const string ACTION_PREFIX = "win."; @@ -713,10 +712,10 @@ namespace Terminal { return term; } - public unowned TerminalWidget? get_terminal (string id) { - for (int i = 0; i < notebook.n_pages; i++) { + public unowned TerminalWidget? get_terminal (string terminal_id) { + for (var i = 0; i < notebook.n_pages; i++) { unowned var term = get_term_widget (notebook.tab_view.get_nth_page (i)); - if (term.terminal_id == id) { + if (term.terminal_id == terminal_id) { return term; } } @@ -724,8 +723,16 @@ namespace Terminal { return null; } - public void set_active_terminal_tab (Adw.TabPage tab) { - notebook.tab_view.selected_page = tab; + public unowned Adw.TabPage? get_page (string terminal_id) { + for (var i = 0; i < notebook.n_pages; i++) { + unowned var page = notebook.tab_view.get_nth_page (i); + unowned var terminal_widget = get_term_widget (page); + if (terminal_widget.terminal_id == terminal_id) { + return page; + } + } + + return null; } /** Compare every tab label with every other and resolve ambiguities **/ @@ -763,8 +770,6 @@ namespace Terminal { } } } - - return; } private void on_terminal_cwd_changed () { From b3f3a270be2bfdb6e832c9c379704b7416275b7d Mon Sep 17 00:00:00 2001 From: lenemter Date: Tue, 27 Jan 2026 17:47:24 +0300 Subject: [PATCH 3/3] Move terminal exit handling to TerminalView --- src/MainWindow.vala | 43 ++------------------------------- src/Widgets/TerminalView.vala | 35 +++++++++++++++++++++++++++ src/Widgets/TerminalWidget.vala | 2 +- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 63b2e7ddf5..aa3e687e40 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -297,11 +297,6 @@ namespace Terminal { return Source.REMOVE; }); - if (term.tab == null) { - // Happens on opening window - ignore - return; - } - if (term.tab_state != WORKING) { term.tab_state = NONE; } @@ -359,9 +354,8 @@ namespace Terminal { } private void on_tab_added (Adw.TabPage tab, int pos) { - var term = get_term_widget (tab); save_opened_terminals (true, true); - connect_terminal_signals (term); + connect_terminal_signals (get_term_widget (tab)); } private void on_tab_removed (Adw.TabPage tab) { @@ -460,36 +454,11 @@ namespace Terminal { } private void connect_terminal_signals (TerminalWidget terminal_widget) { - terminal_widget.child_exited.connect (on_terminal_child_exited); terminal_widget.cwd_changed.connect (on_terminal_cwd_changed); terminal_widget.foreground_process_changed.connect (on_terminal_program_changed); terminal_widget.window_title_changed.connect (on_terminal_window_title_changed); } - private void on_terminal_child_exited (Vte.Terminal term, int status) { - var tw = (TerminalWidget)term; - if (tw.tab.child != tw.parent) { - // TabView already removed tab - ignore signal - return; - } - - if (!tw.killed) { - if (tw.program_string.length > 0) { - /* If a program was running, do not close the tab so that output of program - * remains visible */ - tw.program_string = ""; - tw.active_shell (tw.current_working_directory); - check_for_tabs_with_same_name (); - } else { - if (tw.tab != null) { - notebook.tab_view.close_page (tw.tab); - } - - return; - } - } - } - private void on_terminal_window_title_changed () { title = current_terminal.window_title; } @@ -724,15 +693,7 @@ namespace Terminal { } public unowned Adw.TabPage? get_page (string terminal_id) { - for (var i = 0; i < notebook.n_pages; i++) { - unowned var page = notebook.tab_view.get_nth_page (i); - unowned var terminal_widget = get_term_widget (page); - if (terminal_widget.terminal_id == terminal_id) { - return page; - } - } - - return null; + return notebook.get_page (terminal_id); } /** Compare every tab label with every other and resolve ambiguities **/ diff --git a/src/Widgets/TerminalView.vala b/src/Widgets/TerminalView.vala index ecc96433c8..9c5a384c88 100644 --- a/src/Widgets/TerminalView.vala +++ b/src/Widgets/TerminalView.vala @@ -169,6 +169,7 @@ public class Terminal.TerminalView : Granite.Bin { terminal_widget.bind_property ("current-working-directory", tab, "tooltip"); terminal_widget.tab = tab; + terminal_widget.child_exited.connect (on_child_exited); terminal_widget.notify["tab-state"].connect (() => { tab.icon = terminal_widget.tab_state.to_icon (); tab.loading = terminal_widget.tab_state == WORKING; @@ -253,6 +254,40 @@ public class Terminal.TerminalView : Granite.Bin { tab_view.transfer_page (target, window.notebook.tab_view, 0); } + public unowned Adw.TabPage? get_page (string terminal_id) { + for (var i = 0; i < n_pages; i++) { + unowned var page = tab_view.get_nth_page (i); + unowned var terminal_widget = get_term_widget (page); + if (terminal_widget.terminal_id == terminal_id) { + return page; + } + } + + return null; + } + + private void on_child_exited (Vte.Terminal vte_terminal, int status) requires (vte_terminal is TerminalWidget) { + unowned var terminal_widget = (TerminalWidget) vte_terminal; + + unowned var page = get_page (terminal_widget.terminal_id); + if (page == null) { + // TabView already removed tab - ignore signal + return; + } + + if (terminal_widget.killed) { + return; + } + + if (terminal_widget.program_string.length > 0) { + // If a program was running, do not close the tab so that output of program remains visible + terminal_widget.program_string = ""; + terminal_widget.active_shell (terminal_widget.current_working_directory); + } else { + tab_view.close_page (page); + } + } + // This is called when tab context menu is opened or closed private void tab_view_setup_menu (Adw.TabPage? page) { tab_menu_target = page; diff --git a/src/Widgets/TerminalWidget.vala b/src/Widgets/TerminalWidget.vala index ce1946f734..acbe8bf107 100644 --- a/src/Widgets/TerminalWidget.vala +++ b/src/Widgets/TerminalWidget.vala @@ -24,7 +24,6 @@ namespace Terminal { } internal const string DEFAULT_LABEL = _("Terminal"); - public string terminal_id; public string current_working_directory { get; private set; default = "";} public string program_string { get; set; default = ""; } static int terminal_id_counter = 0; @@ -103,6 +102,7 @@ namespace Terminal { public const int SYS_PIDFD_OPEN = 434; // Same on every arch public bool killed { get; private set; default = false; } + public string terminal_id { get; private set; } private GLib.SimpleAction open_in_browser_action; private GLib.SimpleAction copy_action;