diff --git a/.github/VOUCHED.td b/.github/VOUCHED.td index b6ff45196..3f46fbd76 100644 --- a/.github/VOUCHED.td +++ b/.github/VOUCHED.td @@ -46,6 +46,7 @@ alexfeijoo44 alexjuca alosarjos amadeus +and-rs andrejdaskalov anhthang anmitalidev @@ -160,6 +161,7 @@ j-c-m j0hnm4r5 jacobsandlund jake-stewart +jakeriksen -jamesarch Bad AI etiquette jamylak jarred-sumner @@ -183,6 +185,7 @@ khipp kierancanter kirwiisp kjvdven +kleshzz kloneets knu -kody-w @@ -211,6 +214,7 @@ marrocco-simone masterflitzer matkotiric mattn +mgsloan micaeljarniac michielvk miguelelgallo @@ -301,6 +305,7 @@ simonbcn slsrepo steven-tk sunshine-syz +svector-anu svmhdvn tacherasasi -tangivis @@ -309,6 +314,7 @@ tbrundige tdgroot tdslot thirstycrow +-thomas-trijindev No human in the loop thoutbeckers ticclick tnagatomi diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index f7b076bf0..42f6f3277 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -160,7 +160,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -328,7 +328,7 @@ jobs: - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle diff --git a/.github/workflows/release-tip.yml b/.github/workflows/release-tip.yml index 5cda284bb..12297e03d 100644 --- a/.github/workflows/release-tip.yml +++ b/.github/workflows/release-tip.yml @@ -591,7 +591,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -847,7 +847,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle @@ -1044,7 +1044,7 @@ jobs: # Setup Sparkle - name: Setup Sparkle env: - SPARKLE_VERSION: 2.9.0 + SPARKLE_VERSION: 2.9.6 run: | mkdir -p .action/sparkle cd .action/sparkle diff --git a/build.zig.zon b/build.zig.zon index 96cda01a0..e30650519 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -125,8 +125,8 @@ .apple_sdk = .{ .path = "./pkg/apple-sdk" }, .android_ndk = .{ .path = "./pkg/android-ndk" }, .iterm2_themes = .{ - .url = "https://deps.files.ghostty.org/ghostty-themes-release-20260810-152212-0173c3c.tgz", - .hash = "N-V-__8AAPpcBAD4_75xLBbLiYqdojOwQP74eoWmpL3jPrBl", + .url = "https://deps.files.ghostty.org/ghostty-themes-release-20260824-153547-75c93ee.tgz", + .hash = "N-V-__8AAGZkBACH0haGC9R-hfNPYWxu16hr-ydEsolad9GV", .lazy = true, }, }, diff --git a/build.zig.zon.json b/build.zig.zon.json index 376ca05cb..e3d3b205a 100644 --- a/build.zig.zon.json +++ b/build.zig.zon.json @@ -59,10 +59,10 @@ "url": "https://deps.files.ghostty.org/N-V-__8AAEbOfQBnvcFcCX2W5z7tDaN8vaNZGamEQtNOe0UI.tar.gz", "hash": "sha256-yBbCDox18+Fa6Gc1DnmSVQLRpqhZOLsac7iSfl8x+cs=" }, - "N-V-__8AAPpcBAD4_75xLBbLiYqdojOwQP74eoWmpL3jPrBl": { + "N-V-__8AAGZkBACH0haGC9R-hfNPYWxu16hr-ydEsolad9GV": { "name": "iterm2_themes", - "url": "https://deps.files.ghostty.org/ghostty-themes-release-20260810-152212-0173c3c.tgz", - "hash": "sha256-6ph4RxQg7lsS5/L/SACZyVTqUOVzob34P0PhBcm+Y/A=" + "url": "https://deps.files.ghostty.org/ghostty-themes-release-20260824-153547-75c93ee.tgz", + "hash": "sha256-sf0fMQV/l8EnTbA8bORJYIN6GkH1InXLxkruLm40654=" }, "N-V-__8AAIC5lwAVPJJzxnCAahSvZTIlG-HhtOvnM1uh-66x": { "name": "jetbrains_mono", diff --git a/build.zig.zon.nix b/build.zig.zon.nix index e955d4dc4..58ce3fda6 100644 --- a/build.zig.zon.nix +++ b/build.zig.zon.nix @@ -206,11 +206,11 @@ in }; } { - name = "N-V-__8AAPpcBAD4_75xLBbLiYqdojOwQP74eoWmpL3jPrBl"; + name = "N-V-__8AAGZkBACH0haGC9R-hfNPYWxu16hr-ydEsolad9GV"; path = fetchZigArtifact { name = "iterm2_themes"; - url = "https://deps.files.ghostty.org/ghostty-themes-release-20260810-152212-0173c3c.tgz"; - hash = "sha256-6ph4RxQg7lsS5/L/SACZyVTqUOVzob34P0PhBcm+Y/A="; + url = "https://deps.files.ghostty.org/ghostty-themes-release-20260824-153547-75c93ee.tgz"; + hash = "sha256-sf0fMQV/l8EnTbA8bORJYIN6GkH1InXLxkruLm40654="; unpack = false; }; } diff --git a/build.zig.zon.txt b/build.zig.zon.txt index 82e00bd4b..77df51c0b 100644 --- a/build.zig.zon.txt +++ b/build.zig.zon.txt @@ -9,7 +9,7 @@ https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918 https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz https://deps.files.ghostty.org/gettext-0.24.tar.gz -https://deps.files.ghostty.org/ghostty-themes-release-20260810-152212-0173c3c.tgz +https://deps.files.ghostty.org/ghostty-themes-release-20260824-153547-75c93ee.tgz https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz https://deps.files.ghostty.org/gobject-2026-07-28-36-1.tar.zst https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz diff --git a/flatpak/zig-packages.json b/flatpak/zig-packages.json index ad54aa752..4c9fb5cf8 100644 --- a/flatpak/zig-packages.json +++ b/flatpak/zig-packages.json @@ -73,9 +73,9 @@ }, { "type": "archive", - "url": "https://deps.files.ghostty.org/ghostty-themes-release-20260810-152212-0173c3c.tgz", - "dest": "vendor/p/N-V-__8AAPpcBAD4_75xLBbLiYqdojOwQP74eoWmpL3jPrBl", - "sha256": "ea9878471420ee5b12e7f2ff480099c954ea50e573a1bdf83f43e105c9be63f0" + "url": "https://deps.files.ghostty.org/ghostty-themes-release-20260824-153547-75c93ee.tgz", + "dest": "vendor/p/N-V-__8AAGZkBACH0haGC9R-hfNPYWxu16hr-ydEsolad9GV", + "sha256": "b1fd1f31057f97c1274db03c6ce44960837a1a41f52275cbc64aee2e6e34eb9e" }, { "type": "archive", diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index 96cbaca0a..544f61ba5 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -1099,8 +1099,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/sparkle-project/Sparkle"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.5.1; + kind = exactVersion; + version = 2.9.6; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6e450d9bc..5ed508c36 100644 --- a/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/macos/Ghostty.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/sparkle-project/Sparkle", "state" : { - "revision" : "21d8df80440b1ca3b65fa82e40782f1e5a9e6ba2", - "version" : "2.9.0" + "revision" : "ac2def288cbff5cfc7df3ffef6abdf45b72bcb0a", + "version" : "2.9.6" } } ], diff --git a/po/uk.po b/po/uk.po index 12372e266..f1efb115e 100644 --- a/po/uk.po +++ b/po/uk.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: com.mitchellh.ghostty\n" "Report-Msgid-Bugs-To: m@mitchellh.com\n" "POT-Creation-Date: 2026-08-10 10:06-0500\n" -"PO-Revision-Date: 2026-02-18 13:14+0100\n" +"PO-Revision-Date: 2026-08-19 19:34+0100\n" "Last-Translator: Volodymyr Chernetskyi " "<19735328+chernetskyi@users.noreply.github.com>\n" "Language-Team: Ukrainian \n" @@ -42,7 +42,7 @@ msgstr "Дозволити" #: src/apprt/gtk/ui/1.4/clipboard-confirmation-dialog.blp:92 msgid "Remember choice for this split" -msgstr "Запамʼятати для цієї панелі" +msgstr "Запамʼятати вибір для цієї панелі" #: src/apprt/gtk/ui/1.4/clipboard-confirmation-dialog.blp:93 msgid "Reload configuration to show this prompt again" @@ -70,7 +70,7 @@ msgid "" "and either reload your configuration or ignore these errors." msgstr "" "Виявлено одну або декілька помилок налаштування. Будь ласка, перегляньте " -"помилки нижче і або перезавантажте налаштування, або проігноруйте ці помилки." +"помилки нижче та перезавантажте налаштування або проігноруйте ці помилки." #: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:12 msgid "Ignore" @@ -86,7 +86,7 @@ msgstr "Перезавантажити налаштування" msgid "" "⚠️ You're running a debug build of Ghostty! Performance will be degraded." msgstr "" -"⚠️ Ви використовуєте відладочну збірку Ghostty! Продуктивність буде погіршено." +"⚠️ Ви використовуєте збірку Ghostty для налагодження! Швидкодія буде нижчою." #: src/apprt/gtk/ui/1.5/inspector-window.blp:5 msgid "Ghostty: Terminal Inspector" @@ -118,12 +118,13 @@ msgid "" "through the content, but no input events will be sent to the running " "application." msgstr "" -"Цей термінал працює в режимі читання. Можна переглядати, виділяти і гортати " -"вміст, але ввід не буде передано до запущеної програми." +"Цей термінал працює в режимі «лише читання». Можна переглядати, виділяти та " +"прокручувати вміст, але події введення не передаватимуться до запущеної " +"програми." #: src/apprt/gtk/ui/1.2/surface.blp:112 msgid "Read-only" -msgstr "Тільки для читання" +msgstr "Лише читання" #: src/apprt/gtk/ui/1.2/surface.blp:316 src/apprt/gtk/ui/1.5/window.blp:203 msgid "Copy" @@ -175,7 +176,7 @@ msgstr "Нова панель праворуч" #: src/apprt/gtk/ui/1.2/surface.blp:377 msgid "Close Split" -msgstr "" +msgstr "Закрити панель" #: src/apprt/gtk/ui/1.2/surface.blp:383 msgid "Tab" @@ -203,7 +204,7 @@ msgstr "Вікно" #: src/apprt/gtk/ui/1.2/surface.blp:406 src/apprt/gtk/ui/1.5/window.blp:215 msgid "Change Window Title…" -msgstr "" +msgstr "Змінити заголовок вікна…" #: src/apprt/gtk/ui/1.2/surface.blp:411 src/apprt/gtk/ui/1.5/window.blp:220 #: src/input/command.zig:421 @@ -221,11 +222,11 @@ msgstr "Налаштування" #: src/apprt/gtk/ui/1.2/surface.blp:427 src/apprt/gtk/ui/1.5/window.blp:306 msgid "Open Configuration in OS Editor" -msgstr "" +msgstr "Відкрити налаштування в системному редакторі" #: src/apprt/gtk/ui/1.2/surface.blp:433 src/apprt/gtk/ui/1.5/window.blp:312 msgid "Open Configuration in New Window" -msgstr "" +msgstr "Відкрити налаштування в новому вікні" #: src/apprt/gtk/ui/1.5/title-dialog.blp:5 msgid "Leave blank to restore the default title." @@ -261,7 +262,7 @@ msgstr "Про Ghostty" #: src/apprt/gtk/ui/1.5/window.blp:331 src/input/command.zig:702 msgid "Quit" -msgstr "Завершити" +msgstr "Вийти" #: src/apprt/gtk/ui/1.5/command-palette.blp:17 msgid "Execute a command…" @@ -269,27 +270,27 @@ msgstr "Виконати команду…" #: src/apprt/gtk/class/application.zig:1767 msgid "The keybind was revoked by the system." -msgstr "" +msgstr "Дана привʼязка клавіш відкликана системою." #: src/apprt/gtk/class/application.zig:1769 msgid "The keybind was denied by the system." -msgstr "" +msgstr "Дана привʼязка клавіш відхилена системою." #: src/apprt/gtk/class/application.zig:1780 msgid "Global keybind unavailable" -msgstr "" +msgstr "Глобальна привʼязка клавіш недоступна" #: src/apprt/gtk/class/application.zig:2259 msgid "Export Terminal IO Events" -msgstr "" +msgstr "Експортувати події введення/виведення терміналу" #: src/apprt/gtk/class/application.zig:2262 msgid "Export" -msgstr "" +msgstr "Експортувати" #: src/apprt/gtk/class/application.zig:2783 msgid "Editing configuration file" -msgstr "" +msgstr "Редагування файлу налаштувань" #: src/apprt/gtk/class/clipboard_confirmation_dialog.zig:198 msgid "" @@ -316,12 +317,12 @@ msgid "" "Pasting this text into the terminal may be dangerous as it looks like some " "commands may be executed." msgstr "" -"Вставка цього тексту в термінал може бути небезпечною, бо схоже, що деякі " -"команди можуть бути виконані." +"Вставка цього тексту в термінал може бути небезпечною, оскільки, схоже, " +"можуть бути виконані деякі команди." #: src/apprt/gtk/class/close_confirmation_dialog.zig:184 msgid "Quit Ghostty?" -msgstr "Завершити Ghostty?" +msgstr "Вийти з Ghostty?" #: src/apprt/gtk/class/close_confirmation_dialog.zig:185 msgid "Close Tab?" @@ -375,7 +376,7 @@ msgstr "Змінити заголовок вкладки" #: src/apprt/gtk/class/title_dialog.zig:229 msgid "Change Window Title" -msgstr "" +msgstr "Змінити заголовок вікна" #: src/apprt/gtk/class/window.zig:1153 msgid "Reloaded the configuration" @@ -383,7 +384,7 @@ msgstr "Налаштування перезавантажено" #: src/apprt/gtk/class/window.zig:1830 msgid "Copied to clipboard" -msgstr "Скопійовано до буферa обміну" +msgstr "Скопійовано до буфера обміну" #: src/apprt/gtk/class/window.zig:1832 msgid "Cleared clipboard" @@ -395,710 +396,741 @@ msgstr "Розробники Ghostty" #: src/input/command.zig:149 msgid "Reset Terminal" -msgstr "" +msgstr "Скинути термінал" #: src/input/command.zig:150 msgid "Reset the terminal to a clean state." -msgstr "" +msgstr "Скинути стан терміналу." #: src/input/command.zig:155 msgid "Copy to Clipboard" -msgstr "" +msgstr "Скопіювати до буфера обміну" #: src/input/command.zig:156 msgid "" "Copy the selected text to the clipboard in both plain and styled formats." msgstr "" +"Скопіювати виділений текст до буфера обміну в звичайному і форматованому " +"виглядах одночасно." #: src/input/command.zig:159 msgid "Copy Selection as Plain Text to Clipboard" -msgstr "" +msgstr "Скопіювати виділене до буфера обміну як звичайний текст" #: src/input/command.zig:160 msgid "Copy the selected text as plain text to the clipboard." -msgstr "" +msgstr "Скопіювати виділений текст до буфера обміну як звичайний текст." #: src/input/command.zig:163 msgid "Copy Selection as ANSI Sequences to Clipboard" -msgstr "" +msgstr "Скопіювати виділене до буфера обміну як ANSI-послідовності" #: src/input/command.zig:164 msgid "Copy the selected text as ANSI escape sequences to the clipboard." -msgstr "" +msgstr "Скопіювати виділений текст до буфера обміну як ANSI-послідовності." #: src/input/command.zig:167 msgid "Copy Selection as HTML to Clipboard" -msgstr "" +msgstr "Скопіювати виділене до буфера обміну у форматі HTML" #: src/input/command.zig:168 msgid "Copy the selected text as HTML to the clipboard." -msgstr "" +msgstr "Скопіювати виділений текст до буфера обміну у форматі HTML." #: src/input/command.zig:173 msgid "Copy URL to Clipboard" -msgstr "" +msgstr "Скопіювати URL до буфера обміну" #: src/input/command.zig:174 msgid "Copy the URL under the cursor to the clipboard." -msgstr "" +msgstr "Скопіювати URL під вказівником до буфера обміну." #: src/input/command.zig:179 msgid "Copy Terminal Title to Clipboard" -msgstr "" +msgstr "Скопіювати заголовок терміналу до буфера обміну" #: src/input/command.zig:180 msgid "" "Copy the terminal title to the clipboard. If the terminal title is not set " "this has no effect." msgstr "" +"Скопіювати заголовок терміналу до буфера обміну. Якщо заголовку немає, то " +"нічого не відбудеться." #: src/input/command.zig:185 msgid "Paste from Clipboard" -msgstr "" +msgstr "Вставити з буфера обміну" #: src/input/command.zig:186 msgid "Paste the contents of the main clipboard." -msgstr "" +msgstr "Вставити вміст головного буфера обміну." #: src/input/command.zig:191 msgid "Paste from Selection" -msgstr "" +msgstr "Вставити виділене" #: src/input/command.zig:192 msgid "Paste the contents of the selection clipboard." -msgstr "" +msgstr "Вставити вміст буфера виділення." #: src/input/command.zig:197 msgid "Start Search" -msgstr "" +msgstr "Шукати" #: src/input/command.zig:198 msgid "Start a search if one isn't already active." -msgstr "" +msgstr "Почати пошук, якщо активного пошуку ще немає." #: src/input/command.zig:203 msgid "Search Selection" -msgstr "" +msgstr "Шукати виділене" #: src/input/command.zig:204 msgid "Start a search for the current text selection." -msgstr "" +msgstr "Почати пошук виділеного тексту." #: src/input/command.zig:209 msgid "End Search" -msgstr "" +msgstr "Завершити пошук" #: src/input/command.zig:210 msgid "End the current search if any and hide any GUI elements." -msgstr "" +msgstr "Завершити поточний пошук і сховати елементи інтерфейсу." #: src/input/command.zig:215 msgid "Next Search Result" -msgstr "" +msgstr "Наступна знахідка" #: src/input/command.zig:216 msgid "Navigate to the next search result, if any." -msgstr "" +msgstr "Перейти до наступної знахідки, якщо така є." #: src/input/command.zig:219 msgid "Previous Search Result" -msgstr "" +msgstr "Попередня знахідка" #: src/input/command.zig:220 msgid "Navigate to the previous search result, if any." -msgstr "" +msgstr "Перейти до попередньої знахідки, якщо така є." #: src/input/command.zig:225 msgid "Increase Font Size" -msgstr "" +msgstr "Збільшити розмір шрифту" #: src/input/command.zig:226 msgid "Increase the font size by 1 point." -msgstr "" +msgstr "Збільшити розмір шрифту на 1 пункт." #: src/input/command.zig:231 msgid "Decrease Font Size" -msgstr "" +msgstr "Зменшити розмір шрифту" #: src/input/command.zig:232 msgid "Decrease the font size by 1 point." -msgstr "" +msgstr "Зменшити розмір шрифту на 1 пункт." #: src/input/command.zig:237 msgid "Reset Font Size" -msgstr "" +msgstr "Скинути розмір шрифту" #: src/input/command.zig:238 msgid "Reset the font size to the default." -msgstr "" +msgstr "Скинути розмір шрифту до значення за замовчуванням." #: src/input/command.zig:243 msgid "Clear Screen" -msgstr "" +msgstr "Очистити екран" #: src/input/command.zig:244 msgid "Clear the screen and scrollback." -msgstr "" +msgstr "Очистити екран і буфер прокручування." #: src/input/command.zig:249 msgid "Select All" -msgstr "" +msgstr "Вибрати все" #: src/input/command.zig:250 msgid "Select all text on the screen." -msgstr "" +msgstr "Вибрати весь текст на екрані." #: src/input/command.zig:255 msgid "Scroll to Top" -msgstr "" +msgstr "Прокрутити до початку" #: src/input/command.zig:256 msgid "Scroll to the top of the screen." -msgstr "" +msgstr "Прокрутити до початку екрана." #: src/input/command.zig:261 msgid "Scroll to Bottom" -msgstr "" +msgstr "Прокрутити до кінця" #: src/input/command.zig:262 msgid "Scroll to the bottom of the screen." -msgstr "" +msgstr "Прокрутити до кінця екрана." #: src/input/command.zig:267 msgid "Scroll to Selection" -msgstr "" +msgstr "Прокрутити до виділеного" #: src/input/command.zig:268 msgid "Scroll to the selected text." -msgstr "" +msgstr "Прокрутити до виділеного тексту." #: src/input/command.zig:273 msgid "Scroll Page Up" -msgstr "" +msgstr "Прокрутити сторінку догори" #: src/input/command.zig:274 msgid "Scroll the screen up by a page." -msgstr "" +msgstr "Прокрутити екран на сторінку догори." #: src/input/command.zig:279 msgid "Scroll Page Down" -msgstr "" +msgstr "Прокрутити сторінку донизу" #: src/input/command.zig:280 msgid "Scroll the screen down by a page." -msgstr "" +msgstr "Прокрутити екран на сторінку донизу." #: src/input/command.zig:286 msgid "Copy Screen to Temporary File and Copy Path" -msgstr "" +msgstr "Зберегти екран до файлу і скопіювати шлях" #: src/input/command.zig:287 msgid "" "Copy the screen contents to a temporary file and copy the path to the " "clipboard." msgstr "" +"Зберегти вміст екрана до тимчасового файлу і скопіювати шлях до файлу до " +"буфера обміну." #: src/input/command.zig:291 msgid "Copy Screen to Temporary File and Paste Path" -msgstr "" +msgstr "Зберегти екран до файлу і вставити шлях" #: src/input/command.zig:292 msgid "" "Copy the screen contents to a temporary file and paste the path to the file." msgstr "" +"Зберегти вміст екрана до тимчасового файлу і вставити шлях до файлу." #: src/input/command.zig:296 msgid "Copy Screen to Temporary File and Open" -msgstr "" +msgstr "Зберегти екран до файлу і відкрити файл" #: src/input/command.zig:297 msgid "Copy the screen contents to a temporary file and open it." -msgstr "" +msgstr "Зберегти вміст екрана до тимчасового файлу і відкрити файл." #: src/input/command.zig:305 msgid "Copy Screen as HTML to Temporary File and Copy Path" -msgstr "" +msgstr "Зберегти екран у форматі HTML до файлу і скопіювати шлях" #: src/input/command.zig:306 msgid "" "Copy the screen contents as HTML to a temporary file and copy the path to " "the clipboard." msgstr "" +"Зберегти вміст екрана у форматі HTML до тимчасового файлу і скопіювати шлях " +"до файлу до буфера обміну." #: src/input/command.zig:313 msgid "Copy Screen as HTML to Temporary File and Paste Path" -msgstr "" +msgstr "Зберегти екран у форматі HTML до файлу і вставити шлях" #: src/input/command.zig:314 msgid "" "Copy the screen contents as HTML to a temporary file and paste the path to " "the file." msgstr "" +"Зберегти вміст екрана у форматі HTML до тимчасового файлу і вставити шлях до " +"файлу." #: src/input/command.zig:321 msgid "Copy Screen as HTML to Temporary File and Open" -msgstr "" +msgstr "Зберегти екран у форматі HTML до файлу і відкрити файл" #: src/input/command.zig:322 msgid "Copy the screen contents as HTML to a temporary file and open it." msgstr "" +"Зберегти вміст екрана у форматі HTML до тимчасового файлу і відкрити файл." #: src/input/command.zig:330 msgid "Copy Screen as ANSI Sequences to Temporary File and Copy Path" -msgstr "" +msgstr "Зберегти екран як ANSI-послідовності до файлу і скопіювати шлях" #: src/input/command.zig:331 msgid "" "Copy the screen contents as ANSI escape sequences to a temporary file and " "copy the path to the clipboard." msgstr "" +"Зберегти вміст екрана як ANSI-послідовності до тимчасового файлу і " +"скопіювати шлях до файлу до буфера обміну." #: src/input/command.zig:338 msgid "Copy Screen as ANSI Sequences to Temporary File and Paste Path" -msgstr "" +msgstr "Зберегти екран як ANSI-послідовності до файлу і вставити шлях" #: src/input/command.zig:339 msgid "" "Copy the screen contents as ANSI escape sequences to a temporary file and " "paste the path to the file." msgstr "" +"Зберегти вміст екрана як ANSI-послідовності до тимчасового файлу і вставити " +"шлях до файлу." #: src/input/command.zig:346 msgid "Copy Screen as ANSI Sequences to Temporary File and Open" -msgstr "" +msgstr "Зберегти екран як ANSI-послідовності до файлу і відкрити файл" #: src/input/command.zig:347 msgid "" "Copy the screen contents as ANSI escape sequences to a temporary file and " "open it." msgstr "" +"Зберегти вміст екрана як ANSI-послідовності до тимчасового файлу і відкрити " +"файл." #: src/input/command.zig:354 msgid "Copy Selection to Temporary File and Copy Path" -msgstr "" +msgstr "Зберегти виділене до файлу і скопіювати шлях" #: src/input/command.zig:355 msgid "" "Copy the selection contents to a temporary file and copy the path to the " "clipboard." msgstr "" +"Зберегти виділене до тимчасового файлу і скопіювати шлях до файлу до буфера " +"обміну." #: src/input/command.zig:359 msgid "Copy Selection to Temporary File and Paste Path" -msgstr "" +msgstr "Зберегти виділене до файлу і вставити шлях" #: src/input/command.zig:360 msgid "" "Copy the selection contents to a temporary file and paste the path to the " "file." -msgstr "" +msgstr "Зберегти виділене до тимчасового файлу і вставити шлях до файлу." #: src/input/command.zig:364 msgid "Copy Selection to Temporary File and Open" -msgstr "" +msgstr "Зберегти виділене до файлу і відкрити файл" #: src/input/command.zig:365 msgid "Copy the selection contents to a temporary file and open it." -msgstr "" +msgstr "Зберегти виділене до тимчасового файлу і відкрити файл." #: src/input/command.zig:373 msgid "Copy Selection as HTML to Temporary File and Copy Path" -msgstr "" +msgstr "Зберегти виділене у форматі HTML до файлу і скопіювати шлях" #: src/input/command.zig:374 msgid "" "Copy the selection contents as HTML to a temporary file and copy the path to " "the clipboard." msgstr "" +"Зберегти виділене у форматі HTML до тимчасового файлу і скопіювати шлях до " +"файлу до буфера обміну." #: src/input/command.zig:381 msgid "Copy Selection as HTML to Temporary File and Paste Path" -msgstr "" +msgstr "Зберегти виділене у форматі HTML до файлу і вставити шлях" #: src/input/command.zig:382 msgid "" "Copy the selection contents as HTML to a temporary file and paste the path " "to the file." msgstr "" +"Зберегти виділене у форматі HTML до тимчасового файлу і вставити шлях до " +"файлу." #: src/input/command.zig:389 msgid "Copy Selection as HTML to Temporary File and Open" -msgstr "" +msgstr "Зберегти виділене у форматі HTML до файлу і відкрити файл" #: src/input/command.zig:390 msgid "Copy the selection contents as HTML to a temporary file and open it." -msgstr "" +msgstr "Зберегти виділене у форматі HTML до тимчасового файлу і відкрити файл." #: src/input/command.zig:398 msgid "Copy Selection as ANSI Sequences to Temporary File and Copy Path" -msgstr "" +msgstr "Зберегти виділене як ANSI-послідовності до файлу і скопіювати шлях" #: src/input/command.zig:399 msgid "" "Copy the selection contents as ANSI escape sequences to a temporary file and " "copy the path to the clipboard." msgstr "" +"Зберегти виділене як ANSI-послідовності до тимчасового файлу і скопіювати " +"шлях до файлу до буфера обміну." #: src/input/command.zig:406 msgid "Copy Selection as ANSI Sequences to Temporary File and Paste Path" -msgstr "" +msgstr "Зберегти виділене як ANSI-послідовності до файлу і вставити шлях" #: src/input/command.zig:407 msgid "" "Copy the selection contents as ANSI escape sequences to a temporary file and " "paste the path to the file." msgstr "" +"Зберегти виділене як ANSI-послідовності до тимчасового файлу і вставити шлях " +"до файлу." #: src/input/command.zig:414 msgid "Copy Selection as ANSI Sequences to Temporary File and Open" -msgstr "" +msgstr "Зберегти виділене як ANSI-послідовності до файлу і відкрити файл" #: src/input/command.zig:415 msgid "" "Copy the selection contents as ANSI escape sequences to a temporary file and " "open it." msgstr "" +"Зберегти виділене як ANSI-послідовності до тимчасового файлу і відкрити файл." #: src/input/command.zig:422 msgid "Open a new window." -msgstr "" +msgstr "Створити нове вікно." #: src/input/command.zig:428 msgid "Open a new tab." -msgstr "" +msgstr "Створити нову вкладку." #: src/input/command.zig:434 msgid "Move Tab Left" -msgstr "" +msgstr "Пересунути вкладку ліворуч" #: src/input/command.zig:435 msgid "Move the current tab to the left." -msgstr "" +msgstr "Пересунути поточну вкладку ліворуч." #: src/input/command.zig:439 msgid "Move Tab Right" -msgstr "" +msgstr "Пересунути вкладку праворуч" #: src/input/command.zig:440 msgid "Move the current tab to the right." -msgstr "" +msgstr "Пересунути поточну вкладку праворуч." #: src/input/command.zig:446 msgid "Move Tab to New Window" -msgstr "" +msgstr "Винести вкладку до нового вікна" #: src/input/command.zig:447 msgid "Move the current tab to a new window." -msgstr "" +msgstr "Винести поточну вкладку до нового вікна." #: src/input/command.zig:452 msgid "Toggle Tab Overview" -msgstr "" +msgstr "Перемкнути огляд вкладок" #: src/input/command.zig:453 msgid "Toggle the tab overview." -msgstr "" +msgstr "Перемкнути огляд вкладок." #: src/input/command.zig:458 msgid "Change Terminal Title…" -msgstr "" +msgstr "Змінити заголовок терміналу…" #: src/input/command.zig:459 msgid "Prompt for a new title for the current terminal." -msgstr "" +msgstr "Спитати новий заголовок для поточного терміналу." #: src/input/command.zig:465 msgid "Prompt for a new title for the current tab." -msgstr "" +msgstr "Спитати новий заголовок для поточної вкладки." #: src/input/command.zig:478 msgid "Split the terminal to the left." -msgstr "" +msgstr "Створити нову панель ліворуч." #: src/input/command.zig:483 msgid "Split the terminal to the right." -msgstr "" +msgstr "Створити нову панель праворуч." #: src/input/command.zig:488 msgid "Split the terminal up." -msgstr "" +msgstr "Створити нову панель зверху." #: src/input/command.zig:493 msgid "Split the terminal down." -msgstr "" +msgstr "Створити нову панель знизу." #: src/input/command.zig:500 msgid "Focus Split: Previous" -msgstr "" +msgstr "Перейти до попередньої панелі" #: src/input/command.zig:501 msgid "Focus the previous split, if any." -msgstr "" +msgstr "Перейти до попередньої панелі, якщо така є." #: src/input/command.zig:505 msgid "Focus Split: Next" -msgstr "" +msgstr "Перейти до наступної панелі" #: src/input/command.zig:506 msgid "Focus the next split, if any." -msgstr "" +msgstr "Перейти до наступної панелі, якщо така є." #: src/input/command.zig:510 msgid "Focus Split: Left" -msgstr "" +msgstr "Перейти до панелі ліворуч" #: src/input/command.zig:511 msgid "Focus the split to the left, if it exists." -msgstr "" +msgstr "Перейти до панелі ліворуч, якщо така є." #: src/input/command.zig:515 msgid "Focus Split: Right" -msgstr "" +msgstr "Перейти до панелі праворуч" #: src/input/command.zig:516 msgid "Focus the split to the right, if it exists." -msgstr "" +msgstr "Перейти до панелі праворуч, якщо така є." #: src/input/command.zig:520 msgid "Focus Split: Up" -msgstr "" +msgstr "Перейти до панелі зверху" #: src/input/command.zig:521 msgid "Focus the split above, if it exists." -msgstr "" +msgstr "Перейти до панелі зверху, якщо така є." #: src/input/command.zig:525 msgid "Focus Split: Down" -msgstr "" +msgstr "Перейти до панелі знизу" #: src/input/command.zig:526 msgid "Focus the split below, if it exists." -msgstr "" +msgstr "Перейти до панелі знизу, якщо така є." #: src/input/command.zig:533 msgid "Focus Window: Previous" -msgstr "" +msgstr "Перейти до попереднього вікна" #: src/input/command.zig:534 msgid "Focus the previous window, if any." -msgstr "" +msgstr "Перейти до попереднього вікна, якщо таке є." #: src/input/command.zig:538 msgid "Focus Window: Next" -msgstr "" +msgstr "Перейти до наступного вікна" #: src/input/command.zig:539 msgid "Focus the next window, if any." -msgstr "" +msgstr "Перейти до наступного вікна, якщо таке є." #: src/input/command.zig:545 msgid "Toggle Split Zoom" -msgstr "" +msgstr "Перемкнути масштабування панелі" #: src/input/command.zig:546 msgid "Toggle the zoom state of the current split." -msgstr "" +msgstr "Перемкнути режим масштабування поточної панелі." #: src/input/command.zig:551 msgid "Toggle Read-Only Mode" -msgstr "" +msgstr "Перемкнути режим «лише читання»" #: src/input/command.zig:552 msgid "Toggle read-only mode for the current surface." -msgstr "" +msgstr "Перемкнути режим «лише читання» для поточної панелі." #: src/input/command.zig:557 msgid "Equalize Splits" -msgstr "" +msgstr "Вирівняти панелі" #: src/input/command.zig:558 msgid "Equalize the size of all splits." -msgstr "" +msgstr "Вирівняти розміри всіх панелей." #: src/input/command.zig:563 msgid "Reset Window Size" -msgstr "" +msgstr "Скинути розмір вікна" #: src/input/command.zig:564 msgid "Reset the window size to the default." -msgstr "" +msgstr "Скинути розмір вікна до значення за замовчуванням." #: src/input/command.zig:569 msgid "Toggle Inspector" -msgstr "" +msgstr "Перемкнути термінальний інспектор" #: src/input/command.zig:570 msgid "Toggle the inspector." -msgstr "" +msgstr "Перемкнути термінальний інспектор." #: src/input/command.zig:575 msgid "Show the GTK Inspector" -msgstr "" +msgstr "Показати інспектор GTK" #: src/input/command.zig:576 msgid "Show the GTK inspector." -msgstr "" +msgstr "Показати інспектор GTK." #: src/input/command.zig:581 msgid "Show On-Screen Keyboard" -msgstr "" +msgstr "Показати екранну клавіатуру" #: src/input/command.zig:582 msgid "Show the on-screen keyboard if present." -msgstr "" +msgstr "Показати екранну клавіатуру, якщо така є." #: src/input/command.zig:588 msgid "Open Config Using OS editor" -msgstr "" +msgstr "Відкрити налаштування в системному редакторі" #: src/input/command.zig:589 msgid "Open the config file with the OS's default editor." -msgstr "" +msgstr "Відкрити файл налаштувань у системному редакторі за замовчуванням." #: src/input/command.zig:593 msgid "Open Config in New Terminal Window" -msgstr "" +msgstr "Відкрити налаштування в новому вікні" #: src/input/command.zig:594 msgid "Open the config file in a new window using $EDITOR or $VISUAL." msgstr "" +"Відкрити файл налаштувань в новому вікні за допомогою $EDITOR або $VISUAL." #: src/input/command.zig:600 msgid "Reload Config" -msgstr "" +msgstr "Перезавантажити налаштування" #: src/input/command.zig:601 msgid "Reload the config file." -msgstr "" +msgstr "Перезавантажити файл налаштувань." #: src/input/command.zig:606 msgid "Close Terminal" -msgstr "" +msgstr "Закрити термінал" #: src/input/command.zig:607 msgid "Close the current terminal." -msgstr "" +msgstr "Закрити поточний термінал." #: src/input/command.zig:614 msgid "Close the current tab." -msgstr "" +msgstr "Закрити поточну вкладку." #: src/input/command.zig:618 msgid "Close Other Tabs" -msgstr "" +msgstr "Закрити інші вкладки" #: src/input/command.zig:619 msgid "Close all tabs in this window except the current one." -msgstr "" +msgstr "Закрити всі вкладки в цьому вікні, окрім поточної." #: src/input/command.zig:623 msgid "Close Tabs to the Right" -msgstr "" +msgstr "Закрити вкладки праворуч" #: src/input/command.zig:624 msgid "Close all tabs to the right of the current one." -msgstr "" +msgstr "Закрити всі вкладки праворуч поточної." #: src/input/command.zig:631 msgid "Close the current window." -msgstr "" +msgstr "Закрити поточне вікно." #: src/input/command.zig:636 msgid "Close All Windows" -msgstr "" +msgstr "Закрити всі вікна" #: src/input/command.zig:637 msgid "Close all windows." -msgstr "" +msgstr "Закрити всі вікна." #: src/input/command.zig:642 msgid "Toggle Maximize" -msgstr "" +msgstr "Перемкнути розгортання" #: src/input/command.zig:643 msgid "Toggle the maximized state of the current window." -msgstr "" +msgstr "Перемкнути розгортання поточного вікна." #: src/input/command.zig:648 msgid "Toggle Fullscreen" -msgstr "" +msgstr "Перемкнути повноекранний режим" #: src/input/command.zig:649 msgid "Toggle the fullscreen state of the current window." -msgstr "" +msgstr "Перемкнути повноекранний режим поточного вікна." #: src/input/command.zig:654 msgid "Toggle Window Decorations" -msgstr "" +msgstr "Перемкнути оформлення вікон" #: src/input/command.zig:655 msgid "Toggle the window decorations." -msgstr "" +msgstr "Перемкнути оформлення вікон." #: src/input/command.zig:660 msgid "Toggle Float on Top" -msgstr "" +msgstr "Перемкнути режим «поверх інших вікон»" #: src/input/command.zig:661 msgid "Toggle the float on top state of the current window." -msgstr "" +msgstr "Перемкнути режим «поверх інших вікон» для поточного вікна." #: src/input/command.zig:666 msgid "Toggle Secure Input" -msgstr "" +msgstr "Перемкнути безпечне введення" #: src/input/command.zig:667 msgid "Toggle secure input mode." -msgstr "" +msgstr "Перемкнути режим безпечного введення." #: src/input/command.zig:672 msgid "Toggle Mouse Reporting" -msgstr "" +msgstr "Перемкнути передавання подій миші" #: src/input/command.zig:673 msgid "Toggle whether mouse events are reported to terminal applications." -msgstr "" +msgstr "Перемкнути передавання подій миші до термінальних застосунків." #: src/input/command.zig:678 msgid "Toggle Background Opacity" -msgstr "" +msgstr "Перемкнути непрозорість фону" #: src/input/command.zig:679 msgid "Toggle the background opacity of a window that started transparent." msgstr "" +"Перемкнути непрозорість фону вікна, фон якого спочатку був прозорим." #: src/input/command.zig:684 msgid "Check for Updates" -msgstr "" +msgstr "Перевірити наявність оновлень" #: src/input/command.zig:685 msgid "Check for updates to the application." -msgstr "" +msgstr "Перевірити наявність оновлень застосунку." #: src/input/command.zig:690 msgid "Undo" -msgstr "" +msgstr "Скасувати" #: src/input/command.zig:691 msgid "Undo the last action." -msgstr "" +msgstr "Скасувати останню дію." #: src/input/command.zig:696 msgid "Redo" -msgstr "" +msgstr "Повторити" #: src/input/command.zig:697 msgid "Redo the last undone action." -msgstr "" +msgstr "Повторити останню скасовану дію." #: src/input/command.zig:703 msgid "Quit the application." -msgstr "" +msgstr "Вийти із застосунку." #: src/input/command.zig:708 msgid "Ghostty" -msgstr "" +msgstr "Привид Ghostty" #: src/input/command.zig:709 msgid "Put a little Ghostty in your terminal." -msgstr "" +msgstr "Вставити маленького Ghostty у ваш термінал." diff --git a/src/Surface.zig b/src/Surface.zig index 8eddc14c8..775292127 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -2378,31 +2378,29 @@ fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void { fn setSelectionAndCopy(self: *Surface, sel: terminal.Selection) !void { try self.setSelection(sel); - // If copy on select is false then exit early. - if (self.config.copy_on_select == .false) return; - switch (self.config.copy_on_select) { - .false => unreachable, // handled above with an early exit + .none => {}, + + // The selection clipboard is set if supported, otherwise nothing is copied. + .primary => try self.copySelectionToClipboards( + sel, + &.{.selection}, + .mixed, + ), + + // Only the standard clipboard is set. + .clipboard => try self.copySelectionToClipboards( + sel, + &.{.standard}, + .mixed, + ), // Both standard and selection clipboards are set. - .clipboard => try self.copySelectionToClipboards( + .both => try self.copySelectionToClipboards( sel, &.{ .standard, .selection }, .mixed, ), - - // The selection clipboard is set if supported, otherwise the standard. - .true => { - const clipboard: apprt.Clipboard = if (self.rt_surface.supportsClipboard(.selection)) - .selection - else - .standard; - try self.copySelectionToClipboards( - sel, - &.{clipboard}, - .mixed, - ); - }, } } @@ -3891,7 +3889,7 @@ pub fn mouseButtonCallback( // The selection clipboard is only updated for left-click drag when // the left button is released. This is to avoid the clipboard // being updated on every mouse move which would be noisy. - if (self.config.copy_on_select != .false) { + if (self.config.copy_on_select != .none) { const prev_ = self.io.terminal.screens.active.selection; if (prev_) |prev| { try self.setSelectionAndCopy(terminal.Selection.init( @@ -4049,22 +4047,16 @@ pub fn mouseButtonCallback( } } - // Middle-click paste source follows copy-on-select: when copy-on-select - // targets the selection clipboard, middle-click reads from it; when - // copy-on-select targets the system clipboard, middle-click reads from - // that instead. Falls back to the standard clipboard on platforms that - // do not support the selection clipboard. + // Middle-click action, either ignore, or paste from clipboard or paste from the selection clipboard if supported. if (button == .middle and action == .press) switch (self.config.middle_click_action) { .ignore => {}, + .@"clipboard-paste" => { + _ = try self.startClipboardRequest(.standard, .{ .paste = .standard }); + }, .@"primary-paste" => { - const clipboard: apprt.Clipboard = switch (self.config.copy_on_select) { - .clipboard => .standard, - .true, .false => if (self.rt_surface.supportsClipboard(.selection)) - .selection - else - .standard, - }; - _ = try self.startClipboardRequest(clipboard, .{ .paste = clipboard }); + if (self.rt_surface.supportsClipboard(.selection)) { + _ = try self.startClipboardRequest(.selection, .{ .paste = .selection }); + } }, }; diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig index e6cfe5c50..9d45bc6ce 100644 --- a/src/apprt/gtk/class/surface.zig +++ b/src/apprt/gtk/class/surface.zig @@ -853,14 +853,24 @@ pub const Surface = extern struct { pub fn bindIsSplit(self: *Self, tree: *SplitTree) void { const priv = self.private(); - if (priv.is_split_binding) |bind| bind.unbind(); + if (priv.is_split_binding) |binding| { + binding.unbind(); + binding.unref(); + priv.is_split_binding = null; + } - priv.is_split_binding = tree.as(gobject.Object).bindProperty( + const binding = tree.as(gobject.Object).bindProperty( "is-split", self.as(gobject.Object), "is-split", .{ .sync_create = true }, ); + // The ref created by bindProperty is owned by the binding itself. + // We need another ref to prevent the binding object from being + // freed if the source object (SplitTree) is finalized. Otherwise + // our pointer to the binding could become stale. + binding.ref(); + priv.is_split_binding = binding; } /// Callback used to determine whether unfocused-split-fill / unfocused-split-opacity @@ -1567,7 +1577,11 @@ pub const Surface = extern struct { // https://gitlab.gnome.org/GNOME/libadwaita/-/commit/a7738a4d269bfdf4d8d5429ca73ccdd9b2450421 // https://gitlab.gnome.org/GNOME/libadwaita/-/commit/9759d3fd81129608dd78116001928f2aed974ead if (gtk_xft_dpi <= 0) { - log.warn("gtk-xft-dpi has invalid value ({}), using default", .{gtk_xft_dpi}); + // -1 is a valid value which specifies default scale. + // https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html + if (gtk_xft_dpi != -1) { + log.warn("gtk-xft-dpi has invalid value ({}), using default", .{gtk_xft_dpi}); + } break :xft_scale 1.0; } @@ -1881,6 +1895,12 @@ pub const Surface = extern struct { priv.config = null; } + if (priv.is_split_binding) |binding| { + binding.unbind(); + binding.unref(); + priv.is_split_binding = null; + } + if (priv.vadj_signal_group) |group| { group.setTarget(null); group.as(gobject.Object).unref(); diff --git a/src/apprt/gtk/class/surface_scrolled_window.zig b/src/apprt/gtk/class/surface_scrolled_window.zig index 6ccb0a0d2..ad9a4cc7e 100644 --- a/src/apprt/gtk/class/surface_scrolled_window.zig +++ b/src/apprt/gtk/class/surface_scrolled_window.zig @@ -89,7 +89,8 @@ pub const SurfaceScrolledWindow = extern struct { const priv = self.private(); if (priv.config_binding) |binding| { - binding.as(gobject.Object).unref(); + binding.unbind(); + binding.unref(); priv.config_binding = null; } @@ -168,18 +169,23 @@ pub const SurfaceScrolledWindow = extern struct { // Unbind old config binding if it exists if (priv.config_binding) |binding| { - binding.as(gobject.Object).unref(); + binding.unbind(); + binding.unref(); priv.config_binding = null; } // Bind config from surface to our config property if (priv.surface) |surface| { - priv.config_binding = surface.as(gobject.Object).bindProperty( + const binding = surface.as(gobject.Object).bindProperty( properties.config.name, self.as(gobject.Object), properties.config.name, .{ .sync_create = true }, ); + // Keep another ref, otherwise the binding would be freed and + // our pointer become stale if the surface gets finalized. + binding.ref(); + priv.config_binding = binding; } } diff --git a/src/config/Config.zig b/src/config/Config.zig index f40614a39..bf4e75f86 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -100,6 +100,12 @@ pub const compatibility = std.StaticStringMap( // Ghostty 1.4 renamed `scrollback-limit` to `scrollback-limit-bytes` // when `scrollback-limit-lines` was added so the units are explicit. .{ "scrollback-limit", cli.compatibilityRenamed(Config, "scrollback-limit-bytes") }, + + // Ghostty 1.4 updated "copy-on-select", allow copying to the selection + // clipboard (on supported operating systems), the system clipboard, or + // both. The semantics also changed but this is the correct mapping. + // See: https://github.com/ghostty-org/ghostty/pull/12604 + .{ "copy-on-select", compatCopyOnSelect }, }); /// Set Ghostty's graphical user interface language to a language other than the @@ -2517,25 +2523,28 @@ keybind: Keybinds = .{}, /// limit per surface is double. @"image-storage-limit": u32 = 320 * 1000 * 1000, -/// Whether to automatically copy selected text to the clipboard. `true` -/// will prefer to copy to the selection clipboard, otherwise it will copy to -/// the system clipboard. +/// Whether to automatically copy selected text to the clipboard. /// -/// The value `clipboard` will always copy text to the selection clipboard -/// as well as the system clipboard. +/// Valid values: /// -/// Middle-click primary paste (see `middle-click-action`) is enabled by -/// default even if this is `false`. The clipboard it pastes from follows -/// this setting: with `true` (or `false`) it reads from the selection -/// clipboard (falling back to the system clipboard on platforms without a -/// selection clipboard); with `clipboard` it reads from the system -/// clipboard. +/// * `none` - Do not copy selected text automatically. /// -/// The default value is true on Linux and macOS. +/// * `primary` - On Linux, copy to the selection clipboard only. This has no +/// effect on macOS. (Available since: 1.4.0) +/// +/// * `clipboard` - Copy text to the system clipboard only. +/// +/// * `both` - Copy to both clipboards on Linux, and only the system clipboard +/// on macOS. (Available since: 1.4.0) +/// +/// For backward compatibility and convenience, a value of `true` is the same as +/// `primary` on Linux and `clipboard` on macOS, and `false` is an alias for +/// `none`. +/// +/// The default value is `primary` on Linux and `none` otherwise. @"copy-on-select": CopyOnSelect = switch (builtin.os.tag) { - .linux => .true, - .macos => .true, - else => .false, + .linux => .primary, + else => .none, }, /// The action to take when the user right-clicks on the terminal surface. @@ -2554,8 +2563,9 @@ keybind: Keybinds = .{}, /// The action to take when the user middle-clicks on the terminal surface. /// /// Valid values: -/// * `primary-paste` - Paste from the selection (or system) clipboard per -/// `copy-on-select`. +/// * `primary-paste` - Paste from the selection clipboard on Linux. +/// Does nothing on macOS. +/// * `clipboard-paste` - Paste from the system clipboard. /// * `ignore` - Do nothing, ignore the middle click. /// /// The default value is `primary-paste`. @@ -5047,6 +5057,31 @@ fn compatMacOSDockDropBehavior( return false; } +fn compatCopyOnSelect( + self: *Config, + alloc: Allocator, + key: []const u8, + value: ?[]const u8, +) bool { + _ = alloc; + assert(std.mem.eql(u8, key, "copy-on-select")); + + if (std.mem.eql(u8, value orelse "", "true")) { + self.@"copy-on-select" = switch (builtin.os.tag) { + .linux, .freebsd => .primary, + else => .clipboard, + }; + return true; + } + + if (std.mem.eql(u8, value orelse "", "false")) { + self.@"copy-on-select" = .none; + return true; + } + + return false; +} + /// Add a diagnostic message to the config with the given string. /// This is always added with a location of "none". pub fn addDiagnosticFmt( @@ -8728,16 +8763,20 @@ pub const RepeatableLink = struct { /// Options for copy on select behavior. pub const CopyOnSelect = enum { - /// Disables copy on select entirely. - false, + /// Disables copy on select entirely. This is the default on macOS. + none, /// Copy on select is enabled, but goes to the selection clipboard. - /// This is not supported on platforms such as macOS. This is the default. - true, + /// This is not supported on platforms such as macOS. This is the default + /// on Linux. + primary, + + /// Copy on select is enabled and goes to the system clipboard. + clipboard, /// Copy on select is enabled and goes to both the system clipboard /// and the selection clipboard (for Linux). - clipboard, + both, }; /// Options for right-click actions. @@ -8761,8 +8800,10 @@ pub const RightClickAction = enum { /// Options for middle-click actions. pub const MiddleClickAction = enum { - /// Paste from the selection/standard clipboard per `copy-on-select`. + /// Paste from the selection clipboard. @"primary-paste", + /// Paste from the standard clipboard. + @"clipboard-paste", /// No action is taken on middle click. ignore, diff --git a/src/main_ghostty.zig b/src/main_ghostty.zig index 86e779899..f4fa74133 100644 --- a/src/main_ghostty.zig +++ b/src/main_ghostty.zig @@ -228,7 +228,7 @@ pub const std_options: std.Options = .{ .allow_stack_tracing = if (builtin.target.os.tag.isDarwin() and builtin.target.os.tag != .macos) false else - builtin.strip_debug_info, + !builtin.strip_debug_info, }; test { diff --git a/src/renderer/generic.zig b/src/renderer/generic.zig index 2c9fb1093..2a81f42b4 100644 --- a/src/renderer/generic.zig +++ b/src/renderer/generic.zig @@ -1194,8 +1194,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type { const should_run = // Non-visible windows never vsync self.visible and - // Non-focused windows only render on-demand - self.focused and // Only vsync if we have cell changes or animation (self.cells_rebuilt or self.animationWake() != null); diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index 489b672e3..945a127db 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -210,6 +210,15 @@ pub const LoadingImage = struct { return error.UnsupportedMedium; } + // POSIX shared memory names must begin with a slash, contain at + // least one character after it, contain no other slashes, and fit + // within NAME_MAX. Some shm_open implementations accept names + // without the leading slash, but the Kitty protocol does not. + if (!validSharedMemoryName(path, posix.NAME_MAX)) { + log.warn("invalid shared memory name", .{}); + return error.InvalidData; + } + // Since we're only supporting posix then max_path_bytes should // be enough to stack allocate the path. var buf: [std.fs.max_path_bytes]u8 = undefined; @@ -831,6 +840,16 @@ pub const Rect = struct { } }; +/// Returns whether a name follows the POSIX shared memory name format. +fn validSharedMemoryName(name: []const u8, name_max: usize) bool { + if (name.len < 2 or name.len > name_max or name[0] != '/') return false; + for (name[1..]) |c| { + if (c == '/' or c == 0) return false; + } + + return true; +} + /// Returns true if `path` is `dir` or is contained within it, requiring a /// path-separator boundary so similarly prefixed directories do not match. fn isPathInDir(dir: []const u8, path: []const u8) bool { @@ -852,6 +871,53 @@ test "temporary file path must be inside directory" { try testing.expect(!isPathInDir("/custom/tmp", "/custom/tmp-suffix/tty-graphics-protocol-image.data")); } +test "shared memory names follow POSIX rules" { + const testing = std.testing; + + try testing.expect(validSharedMemoryName("/kitty", 8)); + try testing.expect(validSharedMemoryName("/1234567", 8)); + + try testing.expect(!validSharedMemoryName("", 8)); + try testing.expect(!validSharedMemoryName("/", 8)); + try testing.expect(!validSharedMemoryName("kitty", 8)); + try testing.expect(!validSharedMemoryName("/kitty/image", 16)); + try testing.expect(!validSharedMemoryName("/kitty\x00image", 16)); + try testing.expect(!validSharedMemoryName("/12345678", 8)); +} + +test "image load rejects invalid POSIX shared memory names" { + if (comptime builtin.abi.isAndroid() or + builtin.target.os.tag == .windows or + !builtin.link_libc) + { + return error.SkipZigTest; + } + + const testing = std.testing; + const alloc = testing.allocator; + + var cmd: command.Command = .{ + .control = .{ .transmit = .{ + .format = .rgb, + .medium = .shared_memory, + .width = 1, + .height = 1, + .image_id = 31, + } }, + .data = try alloc.dupe(u8, "kitty-without-leading-slash"), + }; + defer cmd.deinit(alloc); + + try testing.expectError( + error.InvalidData, + LoadingImage.init(testing.io, alloc, &cmd, .{ + .file = false, + .temporary_file = .disabled, + .shared_memory = true, + }), + ); +} + test "shared memory range with offset and size" { const testing = std.testing; diff --git a/src/terminal/ref_counted_set.zig b/src/terminal/ref_counted_set.zig index 97fa87088..a8e64fcc7 100644 --- a/src/terminal/ref_counted_set.zig +++ b/src/terminal/ref_counted_set.zig @@ -317,6 +317,19 @@ pub fn RefCountedSet( if (id < self.next_id) { if (items[id].meta.ref == 0) { + // Requested ID is dead, but if the value exists not under + // another ID then ref count that and increase the ID. + if (self.lookupContext(base, value, ctx)) |existing_id| { + // Notify the context that the value is "deleted" + // because we're reusing the existing value in the + // set. This allows callers to clean up any + // resources associated with the value. + if (comptime @hasDecl(Context, "deleted")) ctx.deleted(value); + + items[existing_id].meta.ref += 1; + return existing_id; + } + // See comment in `addContext` for details. if (self.psl_stats[self.psl_stats.len - 1] > 0) { @branchHint(.cold); @@ -324,8 +337,7 @@ pub fn RefCountedSet( } self.deleteItem(base, id, ctx); - - const added_id = self.upsert(base, value, id, ctx); + const added_id = self.insert(base, value, id, ctx); items[added_id].meta.ref += 1; @@ -589,24 +601,6 @@ pub fn RefCountedSet( return null; } - /// Find the provided value in the hash table, or add a new item - /// for it if not present. If a new item is added, `new_id` will - /// be used as the ID. If an existing item is found, the `new_id` - /// is ignored and the existing item's ID is returned. - fn upsert(self: *Self, base: anytype, value: T, new_id: Id, ctx: Context) Id { - // If the item already exists, return it. - if (self.lookupContext(base, value, ctx)) |id| { - // Notify the context that the value is "deleted" because - // we're reusing the existing value in the set. This allows - // callers to clean up any resources associated with the value. - if (comptime @hasDecl(Context, "deleted")) ctx.deleted(value); - - return id; - } - - return self.insert(base, value, new_id, ctx); - } - /// Insert the given value into the hash table with the given ID. /// /// If runtime safety is enabled, asserts that @@ -764,6 +758,65 @@ pub fn RefCountedSet( }; } +test "addWithId dead id resolving to an existing value" { + const alloc = testing.allocator; + const TestSet = RefCountedSet( + u32, + u16, + u16, + struct { + pub fn hash(_: *const @This(), value: u32) u64 { + return std.hash.int(value); + } + + pub fn eql(_: *const @This(), a: u32, b: u32) bool { + return a == b; + } + }, + ); + + const layout: TestSet.Layout = .init(8); + const buf = try alloc.alignedAlloc( + u8, + TestSet.base_align, + layout.total_size, + ); + defer alloc.free(buf); + + var set: TestSet = .init(.init(buf), layout, .{}); + + // Create a dead item between two live ones so that it can't + // be reaped by the trim loop in `add`, then release it. This + // mirrors a page style set after a styled run is erased. + const live = try set.add(buf, 11); + const released = try set.add(buf, 22); + const last = try set.add(buf, 33); + set.release(buf, released); + try testing.expectEqual(@as(usize, 2), set.count()); + + // Request the dead ID for a value that is already live under + // a different ID: we must resolve to the existing item and + // take a reference, without changing the living count. + const resolved = (try set.addWithId(buf, 11, released)).?; + try testing.expectEqual(live, resolved); + try testing.expectEqual(@as(u16, 2), set.refCount(buf, live)); + try testing.expectEqual(@as(usize, 2), set.count()); + + // The living count must agree with the iterator. + var it = set.iterator(buf); + var iterated: usize = 0; + while (it.next()) |_| iterated += 1; + try testing.expectEqual(set.count(), iterated); + + // The dead slot is still reusable for a value that is not + // in the set: the requested ID must be used (null return). + try testing.expectEqual(@as(?u16, null), try set.addWithId(buf, 44, released)); + try testing.expectEqual(@as(u16, 1), set.refCount(buf, released)); + try testing.expectEqual(@as(usize, 3), set.count()); + + _ = last; +} + test "iterator visits live entries in ID order" { const alloc = testing.allocator; const TestSet = RefCountedSet(