From 41dd733ca7123c9ac4bfc6c781697a660e95b052 Mon Sep 17 00:00:00 2001 From: Can Celik Date: Sat, 4 Apr 2026 19:22:04 +0300 Subject: feat: finish ghostty pane backend integration --- vendor/libghostty-vt/src/App.zig | 623 + vendor/libghostty-vt/src/Command.zig | 903 ++ vendor/libghostty-vt/src/Surface.zig | 6660 +++++++++ vendor/libghostty-vt/src/apprt.zig | 59 + vendor/libghostty-vt/src/apprt/action.zig | 1007 ++ vendor/libghostty-vt/src/apprt/browser.zig | 4 + vendor/libghostty-vt/src/apprt/embedded.zig | 2231 +++ vendor/libghostty-vt/src/apprt/gtk.zig | 17 + vendor/libghostty-vt/src/apprt/gtk/App.zig | 93 + vendor/libghostty-vt/src/apprt/gtk/Surface.zig | 104 + vendor/libghostty-vt/src/apprt/gtk/adw_version.zig | 122 + .../src/apprt/gtk/build/blueprint.zig | 174 + .../src/apprt/gtk/build/gresource.zig | 277 + vendor/libghostty-vt/src/apprt/gtk/build/info.zig | 18 + vendor/libghostty-vt/src/apprt/gtk/cgroup.zig | 115 + vendor/libghostty-vt/src/apprt/gtk/class.zig | 367 + .../src/apprt/gtk/class/application.zig | 2945 ++++ .../gtk/class/clipboard_confirmation_dialog.zig | 352 + .../apprt/gtk/class/close_confirmation_dialog.zig | 204 + .../src/apprt/gtk/class/command_palette.zig | 789 + .../libghostty-vt/src/apprt/gtk/class/config.zig | 175 + .../src/apprt/gtk/class/config_errors_dialog.zig | 161 + .../src/apprt/gtk/class/debug_warning.zig | 50 + .../libghostty-vt/src/apprt/gtk/class/dialog.zig | 91 + .../src/apprt/gtk/class/global_shortcuts.zig | 633 + .../src/apprt/gtk/class/imgui_widget.zig | 551 + .../src/apprt/gtk/class/inspector_widget.zig | 245 + .../src/apprt/gtk/class/inspector_window.zig | 217 + .../src/apprt/gtk/class/key_state_overlay.zig | 342 + .../src/apprt/gtk/class/resize_overlay.zig | 338 + .../src/apprt/gtk/class/search_overlay.zig | 493 + .../src/apprt/gtk/class/split_tree.zig | 1290 ++ .../libghostty-vt/src/apprt/gtk/class/surface.zig | 4049 +++++ .../src/apprt/gtk/class/surface_child_exited.zig | 264 + .../apprt/gtk/class/surface_scrolled_window.zig | 228 + vendor/libghostty-vt/src/apprt/gtk/class/tab.zig | 592 + .../src/apprt/gtk/class/title_dialog.zig | 234 + .../libghostty-vt/src/apprt/gtk/class/window.zig | 2111 +++ .../libghostty-vt/src/apprt/gtk/css/style-dark.css | 8 + .../src/apprt/gtk/css/style-hc-dark.css | 3 + .../libghostty-vt/src/apprt/gtk/css/style-hc.css | 3 + vendor/libghostty-vt/src/apprt/gtk/css/style.css | 183 + vendor/libghostty-vt/src/apprt/gtk/ext.zig | 70 + vendor/libghostty-vt/src/apprt/gtk/ext/actions.zig | 198 + vendor/libghostty-vt/src/apprt/gtk/ext/slice.zig | 111 + vendor/libghostty-vt/src/apprt/gtk/flatpak.zig | 29 + vendor/libghostty-vt/src/apprt/gtk/gsettings.zig | 101 + vendor/libghostty-vt/src/apprt/gtk/gtk_version.zig | 140 + vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig | 196 + .../libghostty-vt/src/apprt/gtk/ipc/new_window.zig | 62 + vendor/libghostty-vt/src/apprt/gtk/key.zig | 535 + vendor/libghostty-vt/src/apprt/gtk/media.zig | 102 + vendor/libghostty-vt/src/apprt/gtk/portal.zig | 105 + .../libghostty-vt/src/apprt/gtk/portal/OpenURI.zig | 565 + vendor/libghostty-vt/src/apprt/gtk/post_fork.zig | 121 + vendor/libghostty-vt/src/apprt/gtk/pre_exec.zig | 81 + .../gtk/ui/1.0/clipboard-confirmation-dialog.blp | 86 + .../apprt/gtk/ui/1.2/close-confirmation-dialog.blp | 12 + .../src/apprt/gtk/ui/1.2/config-errors-dialog.blp | 28 + .../src/apprt/gtk/ui/1.2/debug-warning.blp | 11 + .../src/apprt/gtk/ui/1.2/key-state-overlay.blp | 58 + .../src/apprt/gtk/ui/1.2/resize-overlay.blp | 33 + .../src/apprt/gtk/ui/1.2/search-overlay.blp | 94 + .../libghostty-vt/src/apprt/gtk/ui/1.2/surface.blp | 371 + .../src/apprt/gtk/ui/1.3/debug-warning.blp | 9 + .../src/apprt/gtk/ui/1.3/surface-child-exited.blp | 22 + .../gtk/ui/1.4/clipboard-confirmation-dialog.blp | 96 + .../src/apprt/gtk/ui/1.5/command-palette.blp | 110 + .../src/apprt/gtk/ui/1.5/imgui-widget.blp | 51 + .../src/apprt/gtk/ui/1.5/inspector-widget.blp | 10 + .../src/apprt/gtk/ui/1.5/inspector-window.blp | 38 + .../src/apprt/gtk/ui/1.5/split-tree-split.blp | 20 + .../src/apprt/gtk/ui/1.5/split-tree.blp | 25 + .../apprt/gtk/ui/1.5/surface-scrolled-window.blp | 11 + vendor/libghostty-vt/src/apprt/gtk/ui/1.5/tab.blp | 18 + .../src/apprt/gtk/ui/1.5/title-dialog.blp | 18 + .../libghostty-vt/src/apprt/gtk/ui/1.5/window.blp | 323 + vendor/libghostty-vt/src/apprt/gtk/weak_ref.zig | 44 + vendor/libghostty-vt/src/apprt/gtk/winproto.zig | 155 + .../src/apprt/gtk/winproto/BlurRegion.zig | 187 + .../libghostty-vt/src/apprt/gtk/winproto/noop.zig | 73 + .../src/apprt/gtk/winproto/wayland.zig | 486 + .../src/apprt/gtk/winproto/wayland/Globals.zig | 252 + .../libghostty-vt/src/apprt/gtk/winproto/x11.zig | 498 + vendor/libghostty-vt/src/apprt/ipc.zig | 192 + vendor/libghostty-vt/src/apprt/none.zig | 19 + vendor/libghostty-vt/src/apprt/runtime.zig | 29 + vendor/libghostty-vt/src/apprt/structs.zig | 114 + vendor/libghostty-vt/src/apprt/surface.zig | 197 + vendor/libghostty-vt/src/benchmark/Benchmark.zig | 178 + vendor/libghostty-vt/src/benchmark/CApi.zig | 34 + .../libghostty-vt/src/benchmark/CodepointWidth.zig | 207 + .../libghostty-vt/src/benchmark/GraphemeBreak.zig | 152 + vendor/libghostty-vt/src/benchmark/IsSymbol.zig | 151 + vendor/libghostty-vt/src/benchmark/OscParser.zig | 118 + vendor/libghostty-vt/src/benchmark/ScreenClone.zig | 193 + .../libghostty-vt/src/benchmark/TerminalParser.zig | 108 + .../libghostty-vt/src/benchmark/TerminalStream.zig | 161 + vendor/libghostty-vt/src/benchmark/cli.zig | 100 + vendor/libghostty-vt/src/benchmark/main.zig | 13 + vendor/libghostty-vt/src/benchmark/options.zig | 20 + vendor/libghostty-vt/src/build/Config.zig | 666 + vendor/libghostty-vt/src/build/GhosttyBench.zig | 55 + vendor/libghostty-vt/src/build/GhosttyDist.zig | 257 + vendor/libghostty-vt/src/build/GhosttyDocs.zig | 124 + vendor/libghostty-vt/src/build/GhosttyExe.zig | 127 + .../libghostty-vt/src/build/GhosttyFrameData.zig | 75 + vendor/libghostty-vt/src/build/GhosttyI18n.zig | 193 + vendor/libghostty-vt/src/build/GhosttyLib.zig | 199 + vendor/libghostty-vt/src/build/GhosttyLibVt.zig | 310 + .../libghostty-vt/src/build/GhosttyResources.zig | 442 + vendor/libghostty-vt/src/build/GhosttyWebdata.zig | 119 + .../libghostty-vt/src/build/GhosttyXCFramework.zig | 104 + .../libghostty-vt/src/build/GhosttyXcodebuild.zig | 203 + vendor/libghostty-vt/src/build/GhosttyZig.zig | 102 + vendor/libghostty-vt/src/build/GitVersion.zig | 83 + vendor/libghostty-vt/src/build/HelpStrings.zig | 56 + vendor/libghostty-vt/src/build/LibtoolStep.zig | 78 + vendor/libghostty-vt/src/build/LipoStep.zig | 42 + vendor/libghostty-vt/src/build/MetallibStep.zig | 84 + vendor/libghostty-vt/src/build/SharedDeps.zig | 974 ++ vendor/libghostty-vt/src/build/UnicodeTables.zig | 92 + vendor/libghostty-vt/src/build/XCFrameworkStep.zig | 77 + .../src/build/docker/debian/Dockerfile | 43 + .../src/build/docker/lib-c-docs/Dockerfile | 38 + .../src/build/docker/lib-c-docs/entrypoint.sh | 32 + .../src/build/framegen/frames/frame_001.txt | 41 + .../src/build/framegen/frames/frame_002.txt | 41 + .../src/build/framegen/frames/frame_003.txt | 41 + .../src/build/framegen/frames/frame_004.txt | 41 + .../src/build/framegen/frames/frame_005.txt | 41 + .../src/build/framegen/frames/frame_006.txt | 41 + .../src/build/framegen/frames/frame_007.txt | 41 + .../src/build/framegen/frames/frame_008.txt | 41 + .../src/build/framegen/frames/frame_009.txt | 41 + .../src/build/framegen/frames/frame_010.txt | 41 + .../src/build/framegen/frames/frame_011.txt | 41 + .../src/build/framegen/frames/frame_012.txt | 41 + .../src/build/framegen/frames/frame_013.txt | 41 + .../src/build/framegen/frames/frame_014.txt | 41 + .../src/build/framegen/frames/frame_015.txt | 41 + .../src/build/framegen/frames/frame_016.txt | 41 + .../src/build/framegen/frames/frame_017.txt | 41 + .../src/build/framegen/frames/frame_018.txt | 41 + .../src/build/framegen/frames/frame_019.txt | 41 + .../src/build/framegen/frames/frame_020.txt | 41 + .../src/build/framegen/frames/frame_021.txt | 41 + .../src/build/framegen/frames/frame_022.txt | 41 + .../src/build/framegen/frames/frame_023.txt | 41 + .../src/build/framegen/frames/frame_024.txt | 41 + .../src/build/framegen/frames/frame_025.txt | 41 + .../src/build/framegen/frames/frame_026.txt | 41 + .../src/build/framegen/frames/frame_027.txt | 41 + .../src/build/framegen/frames/frame_028.txt | 41 + .../src/build/framegen/frames/frame_029.txt | 41 + .../src/build/framegen/frames/frame_030.txt | 41 + .../src/build/framegen/frames/frame_031.txt | 41 + .../src/build/framegen/frames/frame_032.txt | 41 + .../src/build/framegen/frames/frame_033.txt | 41 + .../src/build/framegen/frames/frame_034.txt | 41 + .../src/build/framegen/frames/frame_035.txt | 41 + .../src/build/framegen/frames/frame_036.txt | 41 + .../src/build/framegen/frames/frame_037.txt | 41 + .../src/build/framegen/frames/frame_038.txt | 41 + .../src/build/framegen/frames/frame_039.txt | 41 + .../src/build/framegen/frames/frame_040.txt | 41 + .../src/build/framegen/frames/frame_041.txt | 41 + .../src/build/framegen/frames/frame_042.txt | 41 + .../src/build/framegen/frames/frame_043.txt | 41 + .../src/build/framegen/frames/frame_044.txt | 41 + .../src/build/framegen/frames/frame_045.txt | 41 + .../src/build/framegen/frames/frame_046.txt | 41 + .../src/build/framegen/frames/frame_047.txt | 41 + .../src/build/framegen/frames/frame_048.txt | 41 + .../src/build/framegen/frames/frame_049.txt | 41 + .../src/build/framegen/frames/frame_050.txt | 41 + .../src/build/framegen/frames/frame_051.txt | 41 + .../src/build/framegen/frames/frame_052.txt | 41 + .../src/build/framegen/frames/frame_053.txt | 41 + .../src/build/framegen/frames/frame_054.txt | 41 + .../src/build/framegen/frames/frame_055.txt | 41 + .../src/build/framegen/frames/frame_056.txt | 41 + .../src/build/framegen/frames/frame_057.txt | 41 + .../src/build/framegen/frames/frame_058.txt | 41 + .../src/build/framegen/frames/frame_059.txt | 41 + .../src/build/framegen/frames/frame_060.txt | 41 + .../src/build/framegen/frames/frame_061.txt | 41 + .../src/build/framegen/frames/frame_062.txt | 41 + .../src/build/framegen/frames/frame_063.txt | 41 + .../src/build/framegen/frames/frame_064.txt | 41 + .../src/build/framegen/frames/frame_065.txt | 41 + .../src/build/framegen/frames/frame_066.txt | 41 + .../src/build/framegen/frames/frame_067.txt | 41 + .../src/build/framegen/frames/frame_068.txt | 41 + .../src/build/framegen/frames/frame_069.txt | 41 + .../src/build/framegen/frames/frame_070.txt | 41 + .../src/build/framegen/frames/frame_071.txt | 41 + .../src/build/framegen/frames/frame_072.txt | 41 + .../src/build/framegen/frames/frame_073.txt | 41 + .../src/build/framegen/frames/frame_074.txt | 41 + .../src/build/framegen/frames/frame_075.txt | 41 + .../src/build/framegen/frames/frame_076.txt | 41 + .../src/build/framegen/frames/frame_077.txt | 41 + .../src/build/framegen/frames/frame_078.txt | 41 + .../src/build/framegen/frames/frame_079.txt | 41 + .../src/build/framegen/frames/frame_080.txt | 41 + .../src/build/framegen/frames/frame_081.txt | 41 + .../src/build/framegen/frames/frame_082.txt | 41 + .../src/build/framegen/frames/frame_083.txt | 41 + .../src/build/framegen/frames/frame_084.txt | 41 + .../src/build/framegen/frames/frame_085.txt | 41 + .../src/build/framegen/frames/frame_086.txt | 41 + .../src/build/framegen/frames/frame_087.txt | 41 + .../src/build/framegen/frames/frame_088.txt | 41 + .../src/build/framegen/frames/frame_089.txt | 41 + .../src/build/framegen/frames/frame_090.txt | 41 + .../src/build/framegen/frames/frame_091.txt | 41 + .../src/build/framegen/frames/frame_092.txt | 41 + .../src/build/framegen/frames/frame_093.txt | 41 + .../src/build/framegen/frames/frame_094.txt | 41 + .../src/build/framegen/frames/frame_095.txt | 41 + .../src/build/framegen/frames/frame_096.txt | 41 + .../src/build/framegen/frames/frame_097.txt | 41 + .../src/build/framegen/frames/frame_098.txt | 41 + .../src/build/framegen/frames/frame_099.txt | 41 + .../src/build/framegen/frames/frame_100.txt | 41 + .../src/build/framegen/frames/frame_101.txt | 41 + .../src/build/framegen/frames/frame_102.txt | 41 + .../src/build/framegen/frames/frame_103.txt | 41 + .../src/build/framegen/frames/frame_104.txt | 41 + .../src/build/framegen/frames/frame_105.txt | 41 + .../src/build/framegen/frames/frame_106.txt | 41 + .../src/build/framegen/frames/frame_107.txt | 41 + .../src/build/framegen/frames/frame_108.txt | 41 + .../src/build/framegen/frames/frame_109.txt | 41 + .../src/build/framegen/frames/frame_110.txt | 41 + .../src/build/framegen/frames/frame_111.txt | 41 + .../src/build/framegen/frames/frame_112.txt | 41 + .../src/build/framegen/frames/frame_113.txt | 41 + .../src/build/framegen/frames/frame_114.txt | 41 + .../src/build/framegen/frames/frame_115.txt | 41 + .../src/build/framegen/frames/frame_116.txt | 41 + .../src/build/framegen/frames/frame_117.txt | 41 + .../src/build/framegen/frames/frame_118.txt | 41 + .../src/build/framegen/frames/frame_119.txt | 41 + .../src/build/framegen/frames/frame_120.txt | 41 + .../src/build/framegen/frames/frame_121.txt | 41 + .../src/build/framegen/frames/frame_122.txt | 41 + .../src/build/framegen/frames/frame_123.txt | 41 + .../src/build/framegen/frames/frame_124.txt | 41 + .../src/build/framegen/frames/frame_125.txt | 41 + .../src/build/framegen/frames/frame_126.txt | 41 + .../src/build/framegen/frames/frame_127.txt | 41 + .../src/build/framegen/frames/frame_128.txt | 41 + .../src/build/framegen/frames/frame_129.txt | 41 + .../src/build/framegen/frames/frame_130.txt | 41 + .../src/build/framegen/frames/frame_131.txt | 41 + .../src/build/framegen/frames/frame_132.txt | 41 + .../src/build/framegen/frames/frame_133.txt | 41 + .../src/build/framegen/frames/frame_134.txt | 41 + .../src/build/framegen/frames/frame_135.txt | 41 + .../src/build/framegen/frames/frame_136.txt | 41 + .../src/build/framegen/frames/frame_137.txt | 41 + .../src/build/framegen/frames/frame_138.txt | 41 + .../src/build/framegen/frames/frame_139.txt | 41 + .../src/build/framegen/frames/frame_140.txt | 41 + .../src/build/framegen/frames/frame_141.txt | 41 + .../src/build/framegen/frames/frame_142.txt | 41 + .../src/build/framegen/frames/frame_143.txt | 41 + .../src/build/framegen/frames/frame_144.txt | 41 + .../src/build/framegen/frames/frame_145.txt | 41 + .../src/build/framegen/frames/frame_146.txt | 41 + .../src/build/framegen/frames/frame_147.txt | 41 + .../src/build/framegen/frames/frame_148.txt | 41 + .../src/build/framegen/frames/frame_149.txt | 41 + .../src/build/framegen/frames/frame_150.txt | 41 + .../src/build/framegen/frames/frame_151.txt | 41 + .../src/build/framegen/frames/frame_152.txt | 41 + .../src/build/framegen/frames/frame_153.txt | 41 + .../src/build/framegen/frames/frame_154.txt | 41 + .../src/build/framegen/frames/frame_155.txt | 41 + .../src/build/framegen/frames/frame_156.txt | 41 + .../src/build/framegen/frames/frame_157.txt | 41 + .../src/build/framegen/frames/frame_158.txt | 41 + .../src/build/framegen/frames/frame_159.txt | 41 + .../src/build/framegen/frames/frame_160.txt | 41 + .../src/build/framegen/frames/frame_161.txt | 41 + .../src/build/framegen/frames/frame_162.txt | 41 + .../src/build/framegen/frames/frame_163.txt | 41 + .../src/build/framegen/frames/frame_164.txt | 41 + .../src/build/framegen/frames/frame_165.txt | 41 + .../src/build/framegen/frames/frame_166.txt | 41 + .../src/build/framegen/frames/frame_167.txt | 41 + .../src/build/framegen/frames/frame_168.txt | 41 + .../src/build/framegen/frames/frame_169.txt | 41 + .../src/build/framegen/frames/frame_170.txt | 41 + .../src/build/framegen/frames/frame_171.txt | 41 + .../src/build/framegen/frames/frame_172.txt | 41 + .../src/build/framegen/frames/frame_173.txt | 41 + .../src/build/framegen/frames/frame_174.txt | 41 + .../src/build/framegen/frames/frame_175.txt | 41 + .../src/build/framegen/frames/frame_176.txt | 41 + .../src/build/framegen/frames/frame_177.txt | 41 + .../src/build/framegen/frames/frame_178.txt | 41 + .../src/build/framegen/frames/frame_179.txt | 41 + .../src/build/framegen/frames/frame_180.txt | 41 + .../src/build/framegen/frames/frame_181.txt | 41 + .../src/build/framegen/frames/frame_182.txt | 41 + .../src/build/framegen/frames/frame_183.txt | 41 + .../src/build/framegen/frames/frame_184.txt | 41 + .../src/build/framegen/frames/frame_185.txt | 41 + .../src/build/framegen/frames/frame_186.txt | 41 + .../src/build/framegen/frames/frame_187.txt | 41 + .../src/build/framegen/frames/frame_188.txt | 41 + .../src/build/framegen/frames/frame_189.txt | 41 + .../src/build/framegen/frames/frame_190.txt | 41 + .../src/build/framegen/frames/frame_191.txt | 41 + .../src/build/framegen/frames/frame_192.txt | 41 + .../src/build/framegen/frames/frame_193.txt | 41 + .../src/build/framegen/frames/frame_194.txt | 41 + .../src/build/framegen/frames/frame_195.txt | 41 + .../src/build/framegen/frames/frame_196.txt | 41 + .../src/build/framegen/frames/frame_197.txt | 41 + .../src/build/framegen/frames/frame_198.txt | 41 + .../src/build/framegen/frames/frame_199.txt | 41 + .../src/build/framegen/frames/frame_200.txt | 41 + .../src/build/framegen/frames/frame_201.txt | 41 + .../src/build/framegen/frames/frame_202.txt | 41 + .../src/build/framegen/frames/frame_203.txt | 41 + .../src/build/framegen/frames/frame_204.txt | 41 + .../src/build/framegen/frames/frame_205.txt | 41 + .../src/build/framegen/frames/frame_206.txt | 41 + .../src/build/framegen/frames/frame_207.txt | 41 + .../src/build/framegen/frames/frame_208.txt | 41 + .../src/build/framegen/frames/frame_209.txt | 41 + .../src/build/framegen/frames/frame_210.txt | 41 + .../src/build/framegen/frames/frame_211.txt | 41 + .../src/build/framegen/frames/frame_212.txt | 41 + .../src/build/framegen/frames/frame_213.txt | 41 + .../src/build/framegen/frames/frame_214.txt | 41 + .../src/build/framegen/frames/frame_215.txt | 41 + .../src/build/framegen/frames/frame_216.txt | 41 + .../src/build/framegen/frames/frame_217.txt | 41 + .../src/build/framegen/frames/frame_218.txt | 41 + .../src/build/framegen/frames/frame_219.txt | 41 + .../src/build/framegen/frames/frame_220.txt | 41 + .../src/build/framegen/frames/frame_221.txt | 41 + .../src/build/framegen/frames/frame_222.txt | 41 + .../src/build/framegen/frames/frame_223.txt | 41 + .../src/build/framegen/frames/frame_224.txt | 41 + .../src/build/framegen/frames/frame_225.txt | 41 + .../src/build/framegen/frames/frame_226.txt | 41 + .../src/build/framegen/frames/frame_227.txt | 41 + .../src/build/framegen/frames/frame_228.txt | 41 + .../src/build/framegen/frames/frame_229.txt | 41 + .../src/build/framegen/frames/frame_230.txt | 41 + .../src/build/framegen/frames/frame_231.txt | 41 + .../src/build/framegen/frames/frame_232.txt | 41 + .../src/build/framegen/frames/frame_233.txt | 41 + .../src/build/framegen/frames/frame_234.txt | 41 + .../src/build/framegen/frames/frame_235.txt | 41 + vendor/libghostty-vt/src/build/framegen/main.c | 168 + vendor/libghostty-vt/src/build/gtk.zig | 27 + vendor/libghostty-vt/src/build/main.zig | 34 + .../src/build/mdgen/ghostty_1_footer.md | 64 + .../src/build/mdgen/ghostty_1_header.md | 23 + .../src/build/mdgen/ghostty_5_footer.md | 43 + .../src/build/mdgen/ghostty_5_header.md | 122 + .../src/build/mdgen/main_ghostty_1.zig | 16 + .../src/build/mdgen/main_ghostty_5.zig | 16 + vendor/libghostty-vt/src/build/mdgen/mdgen.zig | 117 + vendor/libghostty-vt/src/build/uucode_config.zig | 109 + .../src/build/wasm_patch_growable_table.zig | 269 + .../src/build/webgen/main_actions.zig | 9 + .../src/build/webgen/main_commands.zig | 53 + .../libghostty-vt/src/build/webgen/main_config.zig | 134 + vendor/libghostty-vt/src/build/xcframework.zig | 3 + vendor/libghostty-vt/src/build/zig.zig | 18 + vendor/libghostty-vt/src/build_config.zig | 105 + vendor/libghostty-vt/src/cli.zig | 14 + vendor/libghostty-vt/src/cli/CommaSplitter.zig | 483 + vendor/libghostty-vt/src/cli/Pager.zig | 93 + vendor/libghostty-vt/src/cli/README.md | 13 + vendor/libghostty-vt/src/cli/action.zig | 277 + vendor/libghostty-vt/src/cli/args.zig | 1627 ++ vendor/libghostty-vt/src/cli/boo.zig | 236 + vendor/libghostty-vt/src/cli/crash_report.zig | 93 + vendor/libghostty-vt/src/cli/diagnostics.zig | 197 + vendor/libghostty-vt/src/cli/edit_config.zig | 180 + vendor/libghostty-vt/src/cli/explain_config.zig | 151 + vendor/libghostty-vt/src/cli/ghostty.zig | 308 + vendor/libghostty-vt/src/cli/help.zig | 78 + vendor/libghostty-vt/src/cli/list_actions.zig | 52 + vendor/libghostty-vt/src/cli/list_colors.zig | 209 + vendor/libghostty-vt/src/cli/list_fonts.zig | 165 + vendor/libghostty-vt/src/cli/list_keybinds.zig | 435 + vendor/libghostty-vt/src/cli/list_themes.zig | 1741 +++ vendor/libghostty-vt/src/cli/lorem_ipsum.txt | 45 + vendor/libghostty-vt/src/cli/new_window.zig | 231 + vendor/libghostty-vt/src/cli/show_config.zig | 92 + vendor/libghostty-vt/src/cli/show_face.zig | 239 + .../libghostty-vt/src/cli/ssh-cache/DiskCache.zig | 579 + vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig | 156 + vendor/libghostty-vt/src/cli/ssh_cache.zig | 228 + vendor/libghostty-vt/src/cli/tui.zig | 6 + vendor/libghostty-vt/src/cli/validate_config.zig | 78 + vendor/libghostty-vt/src/cli/version.zig | 76 + vendor/libghostty-vt/src/config.zig | 56 + vendor/libghostty-vt/src/config/CApi.zig | 280 + .../src/config/ClipboardCodepointMap.zig | 44 + vendor/libghostty-vt/src/config/Config.zig | 10887 ++++++++++++++ vendor/libghostty-vt/src/config/ErrorList.zig | 23 + .../src/config/RepeatableStringMap.zig | 198 + vendor/libghostty-vt/src/config/Wasm.zig | 54 + vendor/libghostty-vt/src/config/c_get.zig | 240 + vendor/libghostty-vt/src/config/command.zig | 330 + vendor/libghostty-vt/src/config/conditional.zig | 94 + vendor/libghostty-vt/src/config/config-template | 52 + vendor/libghostty-vt/src/config/edit.zig | 104 + vendor/libghostty-vt/src/config/file_load.zig | 166 + vendor/libghostty-vt/src/config/formatter.zig | 232 + vendor/libghostty-vt/src/config/formatter_file.zig | 110 + vendor/libghostty-vt/src/config/io.zig | 254 + vendor/libghostty-vt/src/config/key.zig | 57 + vendor/libghostty-vt/src/config/path.zig | 510 + vendor/libghostty-vt/src/config/string.zig | 146 + .../libghostty-vt/src/config/testdata/theme_dark | 1 + .../libghostty-vt/src/config/testdata/theme_light | 1 + .../libghostty-vt/src/config/testdata/theme_simple | 2 + vendor/libghostty-vt/src/config/theme.zig | 277 + vendor/libghostty-vt/src/config/url.zig | 523 + vendor/libghostty-vt/src/crash/dir.zig | 66 + vendor/libghostty-vt/src/crash/main.zig | 21 + vendor/libghostty-vt/src/crash/sentry.zig | 317 + vendor/libghostty-vt/src/crash/sentry_envelope.zig | 597 + .../src/datastruct/array_list_collection.zig | 44 + .../src/datastruct/blocking_queue.zig | 248 + .../libghostty-vt/src/datastruct/cache_table.zig | 181 + vendor/libghostty-vt/src/datastruct/circ_buf.zig | 866 ++ vendor/libghostty-vt/src/datastruct/comparison.zig | 430 + .../src/datastruct/intrusive_linked_list.zig | 181 + vendor/libghostty-vt/src/datastruct/lru.zig | 349 + vendor/libghostty-vt/src/datastruct/main.zig | 24 + .../libghostty-vt/src/datastruct/message_data.zig | 124 + .../src/datastruct/segmented_pool.zig | 95 + vendor/libghostty-vt/src/datastruct/split_tree.zig | 2367 +++ vendor/libghostty-vt/src/extra/bash.zig | 337 + vendor/libghostty-vt/src/extra/fish.zig | 202 + vendor/libghostty-vt/src/extra/sublime.zig | 50 + vendor/libghostty-vt/src/extra/vim.zig | 121 + vendor/libghostty-vt/src/extra/zsh.zig | 231 + vendor/libghostty-vt/src/fastmem.zig | 78 + vendor/libghostty-vt/src/file_type.zig | 102 + vendor/libghostty-vt/src/font/Atlas.zig | 889 ++ vendor/libghostty-vt/src/font/CodepointMap.zig | 125 + .../libghostty-vt/src/font/CodepointResolver.zig | 562 + vendor/libghostty-vt/src/font/Collection.zig | 1523 ++ vendor/libghostty-vt/src/font/DeferredFace.zig | 463 + vendor/libghostty-vt/src/font/Glyph.zig | 19 + vendor/libghostty-vt/src/font/Metrics.zig | 816 ++ vendor/libghostty-vt/src/font/SharedGrid.zig | 540 + vendor/libghostty-vt/src/font/SharedGridSet.zig | 834 ++ vendor/libghostty-vt/src/font/backend.zig | 118 + vendor/libghostty-vt/src/font/discovery.zig | 1049 ++ vendor/libghostty-vt/src/font/embedded.zig | 55 + vendor/libghostty-vt/src/font/face.zig | 707 + vendor/libghostty-vt/src/font/face/coretext.zig | 1162 ++ vendor/libghostty-vt/src/font/face/freetype.zig | 1439 ++ vendor/libghostty-vt/src/font/face/web_canvas.zig | 561 + vendor/libghostty-vt/src/font/library.zig | 63 + vendor/libghostty-vt/src/font/main.zig | 82 + .../src/font/nerd_font_attributes.zig | 2774 ++++ vendor/libghostty-vt/src/font/nerd_font_codegen.py | 576 + .../src/font/nerd_font_codepoint_tables.py | 10449 +++++++++++++ vendor/libghostty-vt/src/font/opentype.zig | 17 + vendor/libghostty-vt/src/font/opentype/head.zig | 179 + vendor/libghostty-vt/src/font/opentype/hhea.zig | 116 + vendor/libghostty-vt/src/font/opentype/os2.zig | 583 + vendor/libghostty-vt/src/font/opentype/post.zig | 82 + vendor/libghostty-vt/src/font/opentype/sfnt.zig | 312 + vendor/libghostty-vt/src/font/opentype/svg.zig | 114 + vendor/libghostty-vt/src/font/shape.zig | 101 + vendor/libghostty-vt/src/font/shaper/Cache.zig | 106 + vendor/libghostty-vt/src/font/shaper/coretext.zig | 2678 ++++ vendor/libghostty-vt/src/font/shaper/feature.zig | 354 + vendor/libghostty-vt/src/font/shaper/harfbuzz.zig | 2172 +++ vendor/libghostty-vt/src/font/shaper/noop.zig | 143 + vendor/libghostty-vt/src/font/shaper/run.zig | 409 + .../src/font/shaper/testdata/arabic.txt | 3 + .../libghostty-vt/src/font/shaper/web_canvas.zig | 305 + vendor/libghostty-vt/src/font/sprite.zig | 45 + vendor/libghostty-vt/src/font/sprite/Face.zig | 550 + vendor/libghostty-vt/src/font/sprite/canvas.zig | 512 + .../libghostty-vt/src/font/sprite/draw/README.md | 55 + .../libghostty-vt/src/font/sprite/draw/block.zig | 177 + vendor/libghostty-vt/src/font/sprite/draw/box.zig | 929 ++ .../libghostty-vt/src/font/sprite/draw/braille.zig | 148 + .../libghostty-vt/src/font/sprite/draw/branch.zig | 504 + .../libghostty-vt/src/font/sprite/draw/common.zig | 374 + .../src/font/sprite/draw/geometric_shapes.zig | 198 + .../libghostty-vt/src/font/sprite/draw/octants.txt | 234 + .../src/font/sprite/draw/powerline.zig | 394 + .../libghostty-vt/src/font/sprite/draw/special.zig | 372 + .../sprite/draw/symbols_for_legacy_computing.zig | 1413 ++ .../symbols_for_legacy_computing_supplement.zig | 633 + .../sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png | Bin 0 -> 1032 bytes .../sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png | Bin 0 -> 1295 bytes .../sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png | Bin 0 -> 2193 bytes .../sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png | Bin 0 -> 794 bytes .../sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png | Bin 0 -> 1280 bytes .../sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png | Bin 0 -> 1870 bytes .../sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png | Bin 0 -> 3411 bytes .../sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png | Bin 0 -> 1103 bytes .../sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png | Bin 0 -> 1006 bytes .../sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png | Bin 0 -> 1255 bytes .../sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png | Bin 0 -> 2247 bytes .../sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png | Bin 0 -> 751 bytes .../sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png | Bin 0 -> 5427 bytes .../sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png | Bin 0 -> 5724 bytes .../sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png | Bin 0 -> 9975 bytes .../sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png | Bin 0 -> 4341 bytes .../sprite/testdata/U+2500...U+25FF-11x21+2.png | Bin 0 -> 2225 bytes .../sprite/testdata/U+2500...U+25FF-12x24+3.png | Bin 0 -> 2635 bytes .../sprite/testdata/U+2500...U+25FF-18x36+4.png | Bin 0 -> 4570 bytes .../sprite/testdata/U+2500...U+25FF-9x17+1.png | Bin 0 -> 1853 bytes .../sprite/testdata/U+2800...U+28FF-11x21+2.png | Bin 0 -> 1022 bytes .../sprite/testdata/U+2800...U+28FF-12x24+3.png | Bin 0 -> 1541 bytes .../sprite/testdata/U+2800...U+28FF-18x36+4.png | Bin 0 -> 2501 bytes .../sprite/testdata/U+2800...U+28FF-9x17+1.png | Bin 0 -> 917 bytes .../sprite/testdata/U+E000...U+E0FF-11x21+2.png | Bin 0 -> 1102 bytes .../sprite/testdata/U+E000...U+E0FF-12x24+3.png | Bin 0 -> 1255 bytes .../sprite/testdata/U+E000...U+E0FF-18x36+4.png | Bin 0 -> 2217 bytes .../sprite/testdata/U+E000...U+E0FF-9x17+1.png | Bin 0 -> 894 bytes .../sprite/testdata/U+F500...U+F5FF-11x21+2.png | Bin 0 -> 1114 bytes .../sprite/testdata/U+F500...U+F5FF-12x24+3.png | Bin 0 -> 1421 bytes .../sprite/testdata/U+F500...U+F5FF-18x36+4.png | Bin 0 -> 2473 bytes .../sprite/testdata/U+F500...U+F5FF-9x17+1.png | Bin 0 -> 872 bytes .../sprite/testdata/U+F600...U+F6FF-11x21+2.png | Bin 0 -> 495 bytes .../sprite/testdata/U+F600...U+F6FF-12x24+3.png | Bin 0 -> 637 bytes .../sprite/testdata/U+F600...U+F6FF-18x36+4.png | Bin 0 -> 1210 bytes .../sprite/testdata/U+F600...U+F6FF-9x17+1.png | Bin 0 -> 393 bytes vendor/libghostty-vt/src/global.zig | 235 + vendor/libghostty-vt/src/helpgen.zig | 190 + vendor/libghostty-vt/src/input.zig | 48 + vendor/libghostty-vt/src/input/Binding.zig | 4847 ++++++ vendor/libghostty-vt/src/input/KeymapDarwin.zig | 289 + vendor/libghostty-vt/src/input/KeymapNoop.zig | 39 + vendor/libghostty-vt/src/input/Link.zig | 79 + vendor/libghostty-vt/src/input/command.zig | 739 + vendor/libghostty-vt/src/input/config.zig | 8 + vendor/libghostty-vt/src/input/function_keys.zig | 312 + vendor/libghostty-vt/src/input/helpgen_actions.zig | 112 + vendor/libghostty-vt/src/input/key.zig | 878 ++ vendor/libghostty-vt/src/input/key_encode.zig | 2424 +++ vendor/libghostty-vt/src/input/key_mods.zig | 914 ++ vendor/libghostty-vt/src/input/keyboard.zig | 58 + vendor/libghostty-vt/src/input/keycodes.zig | 673 + vendor/libghostty-vt/src/input/kitty.zig | 134 + vendor/libghostty-vt/src/input/mouse.zig | 110 + vendor/libghostty-vt/src/input/mouse_encode.zig | 781 + vendor/libghostty-vt/src/input/paste.zig | 228 + vendor/libghostty-vt/src/inspector/AGENTS.md | 12 + vendor/libghostty-vt/src/inspector/Inspector.zig | 131 + vendor/libghostty-vt/src/inspector/main.zig | 8 + vendor/libghostty-vt/src/inspector/units.zig | 3 + vendor/libghostty-vt/src/inspector/widgets.zig | 227 + vendor/libghostty-vt/src/inspector/widgets/key.zig | 535 + .../libghostty-vt/src/inspector/widgets/page.zig | 428 + .../src/inspector/widgets/pagelist.zig | 852 ++ .../src/inspector/widgets/renderer.zig | 123 + .../libghostty-vt/src/inspector/widgets/screen.zig | 400 + .../libghostty-vt/src/inspector/widgets/style.zig | 125 + .../src/inspector/widgets/surface.zig | 503 + .../src/inspector/widgets/terminal.zig | 726 + .../libghostty-vt/src/inspector/widgets/termio.zig | 811 + vendor/libghostty-vt/src/lib/allocator.zig | 264 + .../src/lib/allocator/convenience.zig | 59 + vendor/libghostty-vt/src/lib/enum.zig | 145 + vendor/libghostty-vt/src/lib/main.zig | 19 + vendor/libghostty-vt/src/lib/string.zig | 15 + vendor/libghostty-vt/src/lib/struct.zig | 105 + vendor/libghostty-vt/src/lib/target.zig | 6 + vendor/libghostty-vt/src/lib/types.zig | 13 + vendor/libghostty-vt/src/lib/union.zig | 171 + vendor/libghostty-vt/src/lib_vt.zig | 272 + vendor/libghostty-vt/src/main.zig | 26 + vendor/libghostty-vt/src/main_bench.zig | 3 + vendor/libghostty-vt/src/main_build_data.zig | 51 + vendor/libghostty-vt/src/main_c.zig | 243 + vendor/libghostty-vt/src/main_gen.zig | 3 + vendor/libghostty-vt/src/main_ghostty.zig | 213 + vendor/libghostty-vt/src/main_wasm.zig | 26 + vendor/libghostty-vt/src/math.zig | 15 + vendor/libghostty-vt/src/os/TempDir.zig | 95 + vendor/libghostty-vt/src/os/args.zig | 130 + vendor/libghostty-vt/src/os/cf_release_thread.zig | 191 + vendor/libghostty-vt/src/os/cgroup.zig | 26 + vendor/libghostty-vt/src/os/dbus.zig | 21 + vendor/libghostty-vt/src/os/desktop.zig | 160 + vendor/libghostty-vt/src/os/env.zig | 178 + vendor/libghostty-vt/src/os/file.zig | 79 + vendor/libghostty-vt/src/os/flatpak.zig | 507 + vendor/libghostty-vt/src/os/homedir.zig | 189 + vendor/libghostty-vt/src/os/hostname.zig | 147 + vendor/libghostty-vt/src/os/i18n.zig | 193 + vendor/libghostty-vt/src/os/i18n_locales.zig | 60 + vendor/libghostty-vt/src/os/kernel_info.zig | 27 + vendor/libghostty-vt/src/os/locale.c | 1 + vendor/libghostty-vt/src/os/locale.zig | 219 + vendor/libghostty-vt/src/os/mach.zig | 150 + vendor/libghostty-vt/src/os/macos.zig | 177 + vendor/libghostty-vt/src/os/main.zig | 81 + vendor/libghostty-vt/src/os/mouse.zig | 25 + vendor/libghostty-vt/src/os/open.zig | 97 + vendor/libghostty-vt/src/os/passwd.zig | 157 + vendor/libghostty-vt/src/os/path.zig | 89 + vendor/libghostty-vt/src/os/pipe.zig | 21 + vendor/libghostty-vt/src/os/resourcesdir.zig | 139 + vendor/libghostty-vt/src/os/shell.zig | 196 + vendor/libghostty-vt/src/os/string_encoding.zig | 307 + vendor/libghostty-vt/src/os/systemd.zig | 198 + vendor/libghostty-vt/src/os/uri.zig | 204 + vendor/libghostty-vt/src/os/wasm.zig | 25 + vendor/libghostty-vt/src/os/wasm/log.zig | 45 + vendor/libghostty-vt/src/os/wasm/target.zig | 20 + vendor/libghostty-vt/src/os/windows.zig | 133 + vendor/libghostty-vt/src/os/xdg.zig | 312 + vendor/libghostty-vt/src/pty.c | 40 + vendor/libghostty-vt/src/pty.zig | 506 + vendor/libghostty-vt/src/quirks.zig | 57 + vendor/libghostty-vt/src/renderer.zig | 66 + vendor/libghostty-vt/src/renderer/Metal.zig | 458 + vendor/libghostty-vt/src/renderer/OpenGL.zig | 461 + vendor/libghostty-vt/src/renderer/Options.zig | 24 + vendor/libghostty-vt/src/renderer/Overlay.zig | 415 + vendor/libghostty-vt/src/renderer/State.zig | 123 + vendor/libghostty-vt/src/renderer/Thread.zig | 715 + vendor/libghostty-vt/src/renderer/WebGL.zig | 2 + vendor/libghostty-vt/src/renderer/backend.zig | 23 + vendor/libghostty-vt/src/renderer/cell.zig | 680 + vendor/libghostty-vt/src/renderer/cursor.zig | 154 + vendor/libghostty-vt/src/renderer/generic.zig | 3374 +++++ vendor/libghostty-vt/src/renderer/image.zig | 958 ++ vendor/libghostty-vt/src/renderer/link.zig | 322 + vendor/libghostty-vt/src/renderer/message.zig | 111 + vendor/libghostty-vt/src/renderer/metal/Frame.zig | 131 + .../src/renderer/metal/IOSurfaceLayer.zig | 183 + .../libghostty-vt/src/renderer/metal/Pipeline.zig | 204 + .../src/renderer/metal/RenderPass.zig | 229 + .../libghostty-vt/src/renderer/metal/Sampler.zig | 64 + vendor/libghostty-vt/src/renderer/metal/Target.zig | 108 + .../libghostty-vt/src/renderer/metal/Texture.zig | 202 + vendor/libghostty-vt/src/renderer/metal/api.zig | 452 + vendor/libghostty-vt/src/renderer/metal/buffer.zig | 185 + .../libghostty-vt/src/renderer/metal/shaders.zig | 454 + vendor/libghostty-vt/src/renderer/opengl/Frame.zig | 71 + .../libghostty-vt/src/renderer/opengl/Pipeline.zig | 164 + .../src/renderer/opengl/RenderPass.zig | 144 + .../libghostty-vt/src/renderer/opengl/Sampler.zig | 45 + .../libghostty-vt/src/renderer/opengl/Target.zig | 60 + .../libghostty-vt/src/renderer/opengl/Texture.zig | 104 + .../libghostty-vt/src/renderer/opengl/buffer.zig | 126 + .../libghostty-vt/src/renderer/opengl/shaders.zig | 376 + vendor/libghostty-vt/src/renderer/row.zig | 63 + .../src/renderer/shaders/glsl/bg_color.f.glsl | 13 + .../src/renderer/shaders/glsl/bg_image.f.glsl | 63 + .../src/renderer/shaders/glsl/bg_image.v.glsl | 145 + .../src/renderer/shaders/glsl/cell_bg.f.glsl | 61 + .../src/renderer/shaders/glsl/cell_text.f.glsl | 100 + .../src/renderer/shaders/glsl/cell_text.v.glsl | 146 + .../src/renderer/shaders/glsl/common.glsl | 156 + .../src/renderer/shaders/glsl/full_screen.v.glsl | 24 + .../src/renderer/shaders/glsl/image.f.glsl | 21 + .../src/renderer/shaders/glsl/image.v.glsl | 47 + .../src/renderer/shaders/shaders.metal | 853 ++ .../src/renderer/shaders/shadertoy_prefix.glsl | 52 + .../src/renderer/shaders/test_shadertoy_crt.glsl | 59 + .../src/renderer/shaders/test_shadertoy_focus.glsl | 41 + .../renderer/shaders/test_shadertoy_invalid.glsl | 12 + vendor/libghostty-vt/src/renderer/shadertoy.zig | 427 + vendor/libghostty-vt/src/renderer/size.zig | 458 + .../libghostty-vt/src/shell-integration/README.md | 117 + .../src/shell-integration/bash/bash-preexec.sh | 382 + .../src/shell-integration/bash/ghostty.bash | 324 + .../elvish/lib/ghostty-integration.elv | 190 + .../vendor_conf.d/ghostty-shell-integration.fish | 245 + .../nushell/vendor/autoload/ghostty.nu | 110 + .../src/shell-integration/zsh/.zshenv | 61 + .../src/shell-integration/zsh/ghostty-integration | 454 + vendor/libghostty-vt/src/simd/base64.cpp | 20 + vendor/libghostty-vt/src/simd/base64.zig | 87 + vendor/libghostty-vt/src/simd/base64_scalar.zig | 147 + vendor/libghostty-vt/src/simd/codepoint_width.cpp | 488 + vendor/libghostty-vt/src/simd/codepoint_width.zig | 52 + vendor/libghostty-vt/src/simd/index_of.cpp | 53 + vendor/libghostty-vt/src/simd/index_of.h | 98 + vendor/libghostty-vt/src/simd/index_of.zig | 35 + vendor/libghostty-vt/src/simd/main.zig | 14 + vendor/libghostty-vt/src/simd/vt.cpp | 169 + vendor/libghostty-vt/src/simd/vt.h | 30 + vendor/libghostty-vt/src/simd/vt.zig | 196 + vendor/libghostty-vt/src/stb/main.zig | 10 + vendor/libghostty-vt/src/stb/stb.c | 13 + vendor/libghostty-vt/src/stb/stb_image.h | 7987 ++++++++++ vendor/libghostty-vt/src/stb/stb_image_resize.h | 2634 ++++ vendor/libghostty-vt/src/surface_mouse.zig | 336 + vendor/libghostty-vt/src/synthetic/Bytes.zig | 119 + vendor/libghostty-vt/src/synthetic/Generator.zig | 42 + vendor/libghostty-vt/src/synthetic/Osc.zig | 366 + vendor/libghostty-vt/src/synthetic/Utf8.zig | 108 + vendor/libghostty-vt/src/synthetic/cli.zig | 115 + vendor/libghostty-vt/src/synthetic/cli/Ascii.zig | 68 + vendor/libghostty-vt/src/synthetic/cli/Osc.zig | 82 + vendor/libghostty-vt/src/synthetic/cli/Utf8.zig | 58 + vendor/libghostty-vt/src/synthetic/main.zig | 25 + vendor/libghostty-vt/src/terminal/PageList.zig | 14645 +++++++++++++++++++ vendor/libghostty-vt/src/terminal/Parser.zig | 1099 ++ vendor/libghostty-vt/src/terminal/Screen.zig | 10354 +++++++++++++ vendor/libghostty-vt/src/terminal/ScreenSet.zig | 107 + vendor/libghostty-vt/src/terminal/Selection.zig | 1598 ++ vendor/libghostty-vt/src/terminal/StringMap.zig | 298 + vendor/libghostty-vt/src/terminal/Tabstops.zig | 271 + vendor/libghostty-vt/src/terminal/Terminal.zig | 13089 +++++++++++++++++ vendor/libghostty-vt/src/terminal/UTF8Decoder.zig | 142 + vendor/libghostty-vt/src/terminal/ansi.zig | 106 + vendor/libghostty-vt/src/terminal/apc.zig | 186 + .../src/terminal/bitmap_allocator.zig | 1021 ++ .../libghostty-vt/src/terminal/build_options.zig | 80 + vendor/libghostty-vt/src/terminal/c/AGENTS.md | 22 + vendor/libghostty-vt/src/terminal/c/allocator.zig | 67 + vendor/libghostty-vt/src/terminal/c/build_info.zig | 162 + vendor/libghostty-vt/src/terminal/c/cell.zig | 178 + vendor/libghostty-vt/src/terminal/c/color.zig | 13 + vendor/libghostty-vt/src/terminal/c/focus.zig | 55 + vendor/libghostty-vt/src/terminal/c/formatter.zig | 417 + vendor/libghostty-vt/src/terminal/c/grid_ref.zig | 156 + vendor/libghostty-vt/src/terminal/c/key_encode.zig | 356 + vendor/libghostty-vt/src/terminal/c/key_event.zig | 268 + vendor/libghostty-vt/src/terminal/c/main.zig | 178 + vendor/libghostty-vt/src/terminal/c/modes.zig | 104 + .../libghostty-vt/src/terminal/c/mouse_encode.zig | 530 + .../libghostty-vt/src/terminal/c/mouse_event.zig | 155 + vendor/libghostty-vt/src/terminal/c/osc.zig | 140 + vendor/libghostty-vt/src/terminal/c/paste.zig | 129 + vendor/libghostty-vt/src/terminal/c/render.zig | 1382 ++ vendor/libghostty-vt/src/terminal/c/result.zig | 8 + vendor/libghostty-vt/src/terminal/c/row.zig | 132 + vendor/libghostty-vt/src/terminal/c/sgr.zig | 179 + .../libghostty-vt/src/terminal/c/size_report.zig | 82 + vendor/libghostty-vt/src/terminal/c/style.zig | 134 + vendor/libghostty-vt/src/terminal/c/terminal.zig | 2358 +++ vendor/libghostty-vt/src/terminal/c/types.zig | 215 + vendor/libghostty-vt/src/terminal/charsets.zig | 115 + vendor/libghostty-vt/src/terminal/color.zig | 1146 ++ vendor/libghostty-vt/src/terminal/csi.zig | 55 + vendor/libghostty-vt/src/terminal/cursor.zig | 15 + vendor/libghostty-vt/src/terminal/dcs.zig | 430 + .../src/terminal/device_attributes.zig | 227 + .../libghostty-vt/src/terminal/device_status.zig | 74 + vendor/libghostty-vt/src/terminal/focus.zig | 39 + vendor/libghostty-vt/src/terminal/formatter.zig | 6279 ++++++++ vendor/libghostty-vt/src/terminal/hash_map.zig | 1540 ++ vendor/libghostty-vt/src/terminal/highlight.zig | 213 + vendor/libghostty-vt/src/terminal/hyperlink.zig | 240 + vendor/libghostty-vt/src/terminal/kitty.zig | 15 + vendor/libghostty-vt/src/terminal/kitty/color.zig | 77 + .../libghostty-vt/src/terminal/kitty/graphics.zig | 36 + .../src/terminal/kitty/graphics_command.zig | 1325 ++ .../src/terminal/kitty/graphics_exec.zig | 570 + .../src/terminal/kitty/graphics_image.zig | 835 ++ .../src/terminal/kitty/graphics_render.zig | 27 + .../src/terminal/kitty/graphics_storage.zig | 1359 ++ .../src/terminal/kitty/graphics_unicode.zig | 1347 ++ vendor/libghostty-vt/src/terminal/kitty/key.zig | 169 + .../src/terminal/kitty/testdata/dog.png | Bin 0 -> 237387 bytes .../image-png-none-50x76-2147483647-raw.data | Bin 0 -> 86 bytes .../image-rgb-none-20x15-2147483647-raw.data | Bin 0 -> 900 bytes ...age-rgb-zlib_deflate-128x96-2147483647-raw.data | Bin 0 -> 30455 bytes vendor/libghostty-vt/src/terminal/lib.zig | 22 + vendor/libghostty-vt/src/terminal/main.zig | 87 + vendor/libghostty-vt/src/terminal/modes.zig | 381 + vendor/libghostty-vt/src/terminal/mouse.zig | 168 + vendor/libghostty-vt/src/terminal/osc.zig | 826 ++ vendor/libghostty-vt/src/terminal/osc/encoding.zig | 38 + vendor/libghostty-vt/src/terminal/osc/parsers.zig | 21 + .../terminal/osc/parsers/change_window_icon.zig | 33 + .../terminal/osc/parsers/change_window_title.zig | 119 + .../terminal/osc/parsers/clipboard_operation.zig | 106 + .../src/terminal/osc/parsers/color.zig | 780 + .../src/terminal/osc/parsers/context_signal.zig | 541 + .../src/terminal/osc/parsers/hyperlink.zig | 164 + .../src/terminal/osc/parsers/iterm2.zig | 435 + .../osc/parsers/kitty_clipboard_protocol.zig | 702 + .../src/terminal/osc/parsers/kitty_color.zig | 212 + .../src/terminal/osc/parsers/kitty_text_sizing.zig | 255 + .../src/terminal/osc/parsers/mouse_shape.zig | 39 + .../src/terminal/osc/parsers/osc9.zig | 1141 ++ .../src/terminal/osc/parsers/report_pwd.zig | 48 + .../src/terminal/osc/parsers/rxvt_extension.zig | 59 + .../src/terminal/osc/parsers/semantic_prompt.zig | 1264 ++ vendor/libghostty-vt/src/terminal/page.zig | 3918 +++++ vendor/libghostty-vt/src/terminal/parse_table.zig | 388 + vendor/libghostty-vt/src/terminal/point.zig | 94 + .../libghostty-vt/src/terminal/ref_counted_set.zig | 714 + vendor/libghostty-vt/src/terminal/render.zig | 1465 ++ vendor/libghostty-vt/src/terminal/res/glitch.txt | 1 + vendor/libghostty-vt/src/terminal/res/rgb.txt | 782 + vendor/libghostty-vt/src/terminal/search.zig | 22 + .../libghostty-vt/src/terminal/search/Thread.zig | 905 ++ .../libghostty-vt/src/terminal/search/active.zig | 175 + .../libghostty-vt/src/terminal/search/pagelist.zig | 441 + .../libghostty-vt/src/terminal/search/screen.zig | 1552 ++ .../src/terminal/search/sliding_window.zig | 1676 +++ .../libghostty-vt/src/terminal/search/viewport.zig | 384 + vendor/libghostty-vt/src/terminal/sgr.zig | 1103 ++ vendor/libghostty-vt/src/terminal/size.zig | 227 + vendor/libghostty-vt/src/terminal/size_report.zig | 160 + vendor/libghostty-vt/src/terminal/stream.zig | 3449 +++++ .../libghostty-vt/src/terminal/stream_terminal.zig | 2071 +++ vendor/libghostty-vt/src/terminal/style.zig | 1078 ++ vendor/libghostty-vt/src/terminal/tmux.zig | 13 + vendor/libghostty-vt/src/terminal/tmux/control.zig | 839 ++ vendor/libghostty-vt/src/terminal/tmux/layout.zig | 638 + vendor/libghostty-vt/src/terminal/tmux/output.zig | 590 + vendor/libghostty-vt/src/terminal/tmux/viewer.zig | 2283 +++ vendor/libghostty-vt/src/terminal/x11_color.zig | 69 + vendor/libghostty-vt/src/terminfo/Source.zig | 243 + vendor/libghostty-vt/src/terminfo/ghostty.zig | 397 + vendor/libghostty-vt/src/terminfo/main.zig | 13 + vendor/libghostty-vt/src/termio.zig | 39 + vendor/libghostty-vt/src/termio/Exec.zig | 1761 +++ vendor/libghostty-vt/src/termio/Options.zig | 41 + vendor/libghostty-vt/src/termio/Termio.zig | 773 + vendor/libghostty-vt/src/termio/Thread.zig | 531 + vendor/libghostty-vt/src/termio/backend.zig | 129 + vendor/libghostty-vt/src/termio/mailbox.zig | 106 + vendor/libghostty-vt/src/termio/message.zig | 108 + .../libghostty-vt/src/termio/shell_integration.zig | 1032 ++ vendor/libghostty-vt/src/termio/stream_handler.zig | 1556 ++ vendor/libghostty-vt/src/tripwire.zig | 288 + vendor/libghostty-vt/src/unicode/grapheme.zig | 180 + vendor/libghostty-vt/src/unicode/lut.zig | 188 + vendor/libghostty-vt/src/unicode/main.zig | 10 + vendor/libghostty-vt/src/unicode/props.zig | 53 + vendor/libghostty-vt/src/unicode/props_table.zig | 18 + vendor/libghostty-vt/src/unicode/props_uucode.zig | 85 + vendor/libghostty-vt/src/unicode/symbols_table.zig | 17 + .../libghostty-vt/src/unicode/symbols_uucode.zig | 68 + 848 files changed, 281838 insertions(+) create mode 100644 vendor/libghostty-vt/src/App.zig create mode 100644 vendor/libghostty-vt/src/Command.zig create mode 100644 vendor/libghostty-vt/src/Surface.zig create mode 100644 vendor/libghostty-vt/src/apprt.zig create mode 100644 vendor/libghostty-vt/src/apprt/action.zig create mode 100644 vendor/libghostty-vt/src/apprt/browser.zig create mode 100644 vendor/libghostty-vt/src/apprt/embedded.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/App.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/Surface.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/adw_version.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/build/blueprint.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/build/gresource.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/build/info.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/cgroup.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/application.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/clipboard_confirmation_dialog.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/close_confirmation_dialog.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/command_palette.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/config.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/config_errors_dialog.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/debug_warning.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/dialog.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/global_shortcuts.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/imgui_widget.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/inspector_widget.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/inspector_window.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/key_state_overlay.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/resize_overlay.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/search_overlay.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/split_tree.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/surface.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/surface_child_exited.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/surface_scrolled_window.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/tab.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/title_dialog.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/class/window.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/css/style-dark.css create mode 100644 vendor/libghostty-vt/src/apprt/gtk/css/style-hc-dark.css create mode 100644 vendor/libghostty-vt/src/apprt/gtk/css/style-hc.css create mode 100644 vendor/libghostty-vt/src/apprt/gtk/css/style.css create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ext.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ext/actions.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ext/slice.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/flatpak.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/gsettings.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/gtk_version.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ipc/new_window.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/key.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/media.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/portal.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/portal/OpenURI.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/post_fork.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/pre_exec.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.0/clipboard-confirmation-dialog.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.2/close-confirmation-dialog.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.2/config-errors-dialog.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.2/debug-warning.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.2/key-state-overlay.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.2/resize-overlay.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.2/search-overlay.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.2/surface.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.3/debug-warning.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.3/surface-child-exited.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.4/clipboard-confirmation-dialog.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/command-palette.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/imgui-widget.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-widget.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-window.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree-split.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/surface-scrolled-window.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/tab.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/title-dialog.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/ui/1.5/window.blp create mode 100644 vendor/libghostty-vt/src/apprt/gtk/weak_ref.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/winproto.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/winproto/BlurRegion.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/winproto/noop.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/winproto/wayland.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/winproto/wayland/Globals.zig create mode 100644 vendor/libghostty-vt/src/apprt/gtk/winproto/x11.zig create mode 100644 vendor/libghostty-vt/src/apprt/ipc.zig create mode 100644 vendor/libghostty-vt/src/apprt/none.zig create mode 100644 vendor/libghostty-vt/src/apprt/runtime.zig create mode 100644 vendor/libghostty-vt/src/apprt/structs.zig create mode 100644 vendor/libghostty-vt/src/apprt/surface.zig create mode 100644 vendor/libghostty-vt/src/benchmark/Benchmark.zig create mode 100644 vendor/libghostty-vt/src/benchmark/CApi.zig create mode 100644 vendor/libghostty-vt/src/benchmark/CodepointWidth.zig create mode 100644 vendor/libghostty-vt/src/benchmark/GraphemeBreak.zig create mode 100644 vendor/libghostty-vt/src/benchmark/IsSymbol.zig create mode 100644 vendor/libghostty-vt/src/benchmark/OscParser.zig create mode 100644 vendor/libghostty-vt/src/benchmark/ScreenClone.zig create mode 100644 vendor/libghostty-vt/src/benchmark/TerminalParser.zig create mode 100644 vendor/libghostty-vt/src/benchmark/TerminalStream.zig create mode 100644 vendor/libghostty-vt/src/benchmark/cli.zig create mode 100644 vendor/libghostty-vt/src/benchmark/main.zig create mode 100644 vendor/libghostty-vt/src/benchmark/options.zig create mode 100644 vendor/libghostty-vt/src/build/Config.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyBench.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyDist.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyDocs.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyExe.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyFrameData.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyI18n.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyLib.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyLibVt.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyResources.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyWebdata.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyXCFramework.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyXcodebuild.zig create mode 100644 vendor/libghostty-vt/src/build/GhosttyZig.zig create mode 100644 vendor/libghostty-vt/src/build/GitVersion.zig create mode 100644 vendor/libghostty-vt/src/build/HelpStrings.zig create mode 100644 vendor/libghostty-vt/src/build/LibtoolStep.zig create mode 100644 vendor/libghostty-vt/src/build/LipoStep.zig create mode 100644 vendor/libghostty-vt/src/build/MetallibStep.zig create mode 100644 vendor/libghostty-vt/src/build/SharedDeps.zig create mode 100644 vendor/libghostty-vt/src/build/UnicodeTables.zig create mode 100644 vendor/libghostty-vt/src/build/XCFrameworkStep.zig create mode 100644 vendor/libghostty-vt/src/build/docker/debian/Dockerfile create mode 100644 vendor/libghostty-vt/src/build/docker/lib-c-docs/Dockerfile create mode 100755 vendor/libghostty-vt/src/build/docker/lib-c-docs/entrypoint.sh create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_001.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_002.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_003.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_004.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_005.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_006.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_007.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_008.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_009.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_010.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_011.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_012.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_013.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_014.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_015.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_016.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_017.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_018.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_019.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_020.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_021.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_022.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_023.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_024.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_025.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_026.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_027.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_028.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_029.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_030.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_031.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_032.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_033.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_034.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_035.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_036.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_037.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_038.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_039.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_040.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_041.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_042.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_043.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_044.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_045.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_046.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_047.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_048.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_049.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_050.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_051.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_052.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_053.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_054.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_055.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_056.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_057.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_058.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_059.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_060.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_061.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_062.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_063.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_064.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_065.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_066.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_067.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_068.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_069.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_070.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_071.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_072.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_073.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_074.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_075.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_076.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_077.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_078.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_079.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_080.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_081.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_082.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_083.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_084.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_085.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_086.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_087.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_088.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_089.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_090.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_091.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_092.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_093.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_094.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_095.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_096.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_097.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_098.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_099.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_100.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_101.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_102.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_103.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_104.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_105.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_106.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_107.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_108.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_109.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_110.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_111.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_112.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_113.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_114.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_115.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_116.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_117.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_118.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_119.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_120.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_121.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_122.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_123.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_124.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_125.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_126.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_127.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_128.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_129.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_130.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_131.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_132.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_133.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_134.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_135.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_136.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_137.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_138.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_139.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_140.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_141.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_142.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_143.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_144.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_145.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_146.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_147.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_148.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_149.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_150.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_151.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_152.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_153.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_154.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_155.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_156.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_157.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_158.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_159.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_160.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_161.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_162.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_163.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_164.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_165.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_166.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_167.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_168.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_169.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_170.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_171.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_172.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_173.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_174.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_175.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_176.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_177.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_178.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_179.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_180.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_181.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_182.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_183.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_184.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_185.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_186.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_187.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_188.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_189.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_190.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_191.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_192.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_193.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_194.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_195.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_196.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_197.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_198.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_199.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_200.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_201.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_202.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_203.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_204.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_205.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_206.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_207.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_208.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_209.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_210.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_211.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_212.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_213.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_214.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_215.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_216.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_217.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_218.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_219.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_220.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_221.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_222.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_223.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_224.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_225.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_226.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_227.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_228.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_229.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_230.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_231.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_232.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_233.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_234.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/frames/frame_235.txt create mode 100644 vendor/libghostty-vt/src/build/framegen/main.c create mode 100644 vendor/libghostty-vt/src/build/gtk.zig create mode 100644 vendor/libghostty-vt/src/build/main.zig create mode 100644 vendor/libghostty-vt/src/build/mdgen/ghostty_1_footer.md create mode 100644 vendor/libghostty-vt/src/build/mdgen/ghostty_1_header.md create mode 100644 vendor/libghostty-vt/src/build/mdgen/ghostty_5_footer.md create mode 100644 vendor/libghostty-vt/src/build/mdgen/ghostty_5_header.md create mode 100644 vendor/libghostty-vt/src/build/mdgen/main_ghostty_1.zig create mode 100644 vendor/libghostty-vt/src/build/mdgen/main_ghostty_5.zig create mode 100644 vendor/libghostty-vt/src/build/mdgen/mdgen.zig create mode 100644 vendor/libghostty-vt/src/build/uucode_config.zig create mode 100644 vendor/libghostty-vt/src/build/wasm_patch_growable_table.zig create mode 100644 vendor/libghostty-vt/src/build/webgen/main_actions.zig create mode 100644 vendor/libghostty-vt/src/build/webgen/main_commands.zig create mode 100644 vendor/libghostty-vt/src/build/webgen/main_config.zig create mode 100644 vendor/libghostty-vt/src/build/xcframework.zig create mode 100644 vendor/libghostty-vt/src/build/zig.zig create mode 100644 vendor/libghostty-vt/src/build_config.zig create mode 100644 vendor/libghostty-vt/src/cli.zig create mode 100644 vendor/libghostty-vt/src/cli/CommaSplitter.zig create mode 100644 vendor/libghostty-vt/src/cli/Pager.zig create mode 100644 vendor/libghostty-vt/src/cli/README.md create mode 100644 vendor/libghostty-vt/src/cli/action.zig create mode 100644 vendor/libghostty-vt/src/cli/args.zig create mode 100644 vendor/libghostty-vt/src/cli/boo.zig create mode 100644 vendor/libghostty-vt/src/cli/crash_report.zig create mode 100644 vendor/libghostty-vt/src/cli/diagnostics.zig create mode 100644 vendor/libghostty-vt/src/cli/edit_config.zig create mode 100644 vendor/libghostty-vt/src/cli/explain_config.zig create mode 100644 vendor/libghostty-vt/src/cli/ghostty.zig create mode 100644 vendor/libghostty-vt/src/cli/help.zig create mode 100644 vendor/libghostty-vt/src/cli/list_actions.zig create mode 100644 vendor/libghostty-vt/src/cli/list_colors.zig create mode 100644 vendor/libghostty-vt/src/cli/list_fonts.zig create mode 100644 vendor/libghostty-vt/src/cli/list_keybinds.zig create mode 100644 vendor/libghostty-vt/src/cli/list_themes.zig create mode 100644 vendor/libghostty-vt/src/cli/lorem_ipsum.txt create mode 100644 vendor/libghostty-vt/src/cli/new_window.zig create mode 100644 vendor/libghostty-vt/src/cli/show_config.zig create mode 100644 vendor/libghostty-vt/src/cli/show_face.zig create mode 100644 vendor/libghostty-vt/src/cli/ssh-cache/DiskCache.zig create mode 100644 vendor/libghostty-vt/src/cli/ssh-cache/Entry.zig create mode 100644 vendor/libghostty-vt/src/cli/ssh_cache.zig create mode 100644 vendor/libghostty-vt/src/cli/tui.zig create mode 100644 vendor/libghostty-vt/src/cli/validate_config.zig create mode 100644 vendor/libghostty-vt/src/cli/version.zig create mode 100644 vendor/libghostty-vt/src/config.zig create mode 100644 vendor/libghostty-vt/src/config/CApi.zig create mode 100644 vendor/libghostty-vt/src/config/ClipboardCodepointMap.zig create mode 100644 vendor/libghostty-vt/src/config/Config.zig create mode 100644 vendor/libghostty-vt/src/config/ErrorList.zig create mode 100644 vendor/libghostty-vt/src/config/RepeatableStringMap.zig create mode 100644 vendor/libghostty-vt/src/config/Wasm.zig create mode 100644 vendor/libghostty-vt/src/config/c_get.zig create mode 100644 vendor/libghostty-vt/src/config/command.zig create mode 100644 vendor/libghostty-vt/src/config/conditional.zig create mode 100644 vendor/libghostty-vt/src/config/config-template create mode 100644 vendor/libghostty-vt/src/config/edit.zig create mode 100644 vendor/libghostty-vt/src/config/file_load.zig create mode 100644 vendor/libghostty-vt/src/config/formatter.zig create mode 100644 vendor/libghostty-vt/src/config/formatter_file.zig create mode 100644 vendor/libghostty-vt/src/config/io.zig create mode 100644 vendor/libghostty-vt/src/config/key.zig create mode 100644 vendor/libghostty-vt/src/config/path.zig create mode 100644 vendor/libghostty-vt/src/config/string.zig create mode 100644 vendor/libghostty-vt/src/config/testdata/theme_dark create mode 100644 vendor/libghostty-vt/src/config/testdata/theme_light create mode 100644 vendor/libghostty-vt/src/config/testdata/theme_simple create mode 100644 vendor/libghostty-vt/src/config/theme.zig create mode 100644 vendor/libghostty-vt/src/config/url.zig create mode 100644 vendor/libghostty-vt/src/crash/dir.zig create mode 100644 vendor/libghostty-vt/src/crash/main.zig create mode 100644 vendor/libghostty-vt/src/crash/sentry.zig create mode 100644 vendor/libghostty-vt/src/crash/sentry_envelope.zig create mode 100644 vendor/libghostty-vt/src/datastruct/array_list_collection.zig create mode 100644 vendor/libghostty-vt/src/datastruct/blocking_queue.zig create mode 100644 vendor/libghostty-vt/src/datastruct/cache_table.zig create mode 100644 vendor/libghostty-vt/src/datastruct/circ_buf.zig create mode 100644 vendor/libghostty-vt/src/datastruct/comparison.zig create mode 100644 vendor/libghostty-vt/src/datastruct/intrusive_linked_list.zig create mode 100644 vendor/libghostty-vt/src/datastruct/lru.zig create mode 100644 vendor/libghostty-vt/src/datastruct/main.zig create mode 100644 vendor/libghostty-vt/src/datastruct/message_data.zig create mode 100644 vendor/libghostty-vt/src/datastruct/segmented_pool.zig create mode 100644 vendor/libghostty-vt/src/datastruct/split_tree.zig create mode 100644 vendor/libghostty-vt/src/extra/bash.zig create mode 100644 vendor/libghostty-vt/src/extra/fish.zig create mode 100644 vendor/libghostty-vt/src/extra/sublime.zig create mode 100644 vendor/libghostty-vt/src/extra/vim.zig create mode 100644 vendor/libghostty-vt/src/extra/zsh.zig create mode 100644 vendor/libghostty-vt/src/fastmem.zig create mode 100644 vendor/libghostty-vt/src/file_type.zig create mode 100644 vendor/libghostty-vt/src/font/Atlas.zig create mode 100644 vendor/libghostty-vt/src/font/CodepointMap.zig create mode 100644 vendor/libghostty-vt/src/font/CodepointResolver.zig create mode 100644 vendor/libghostty-vt/src/font/Collection.zig create mode 100644 vendor/libghostty-vt/src/font/DeferredFace.zig create mode 100644 vendor/libghostty-vt/src/font/Glyph.zig create mode 100644 vendor/libghostty-vt/src/font/Metrics.zig create mode 100644 vendor/libghostty-vt/src/font/SharedGrid.zig create mode 100644 vendor/libghostty-vt/src/font/SharedGridSet.zig create mode 100644 vendor/libghostty-vt/src/font/backend.zig create mode 100644 vendor/libghostty-vt/src/font/discovery.zig create mode 100644 vendor/libghostty-vt/src/font/embedded.zig create mode 100644 vendor/libghostty-vt/src/font/face.zig create mode 100644 vendor/libghostty-vt/src/font/face/coretext.zig create mode 100644 vendor/libghostty-vt/src/font/face/freetype.zig create mode 100644 vendor/libghostty-vt/src/font/face/web_canvas.zig create mode 100644 vendor/libghostty-vt/src/font/library.zig create mode 100644 vendor/libghostty-vt/src/font/main.zig create mode 100644 vendor/libghostty-vt/src/font/nerd_font_attributes.zig create mode 100644 vendor/libghostty-vt/src/font/nerd_font_codegen.py create mode 100644 vendor/libghostty-vt/src/font/nerd_font_codepoint_tables.py create mode 100644 vendor/libghostty-vt/src/font/opentype.zig create mode 100644 vendor/libghostty-vt/src/font/opentype/head.zig create mode 100644 vendor/libghostty-vt/src/font/opentype/hhea.zig create mode 100644 vendor/libghostty-vt/src/font/opentype/os2.zig create mode 100644 vendor/libghostty-vt/src/font/opentype/post.zig create mode 100644 vendor/libghostty-vt/src/font/opentype/sfnt.zig create mode 100644 vendor/libghostty-vt/src/font/opentype/svg.zig create mode 100644 vendor/libghostty-vt/src/font/shape.zig create mode 100644 vendor/libghostty-vt/src/font/shaper/Cache.zig create mode 100644 vendor/libghostty-vt/src/font/shaper/coretext.zig create mode 100644 vendor/libghostty-vt/src/font/shaper/feature.zig create mode 100644 vendor/libghostty-vt/src/font/shaper/harfbuzz.zig create mode 100644 vendor/libghostty-vt/src/font/shaper/noop.zig create mode 100644 vendor/libghostty-vt/src/font/shaper/run.zig create mode 100644 vendor/libghostty-vt/src/font/shaper/testdata/arabic.txt create mode 100644 vendor/libghostty-vt/src/font/shaper/web_canvas.zig create mode 100644 vendor/libghostty-vt/src/font/sprite.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/Face.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/canvas.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/README.md create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/block.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/box.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/braille.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/branch.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/common.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/geometric_shapes.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/octants.txt create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/powerline.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/special.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/symbols_for_legacy_computing.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png create mode 100644 vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png create mode 100644 vendor/libghostty-vt/src/global.zig create mode 100644 vendor/libghostty-vt/src/helpgen.zig create mode 100644 vendor/libghostty-vt/src/input.zig create mode 100644 vendor/libghostty-vt/src/input/Binding.zig create mode 100644 vendor/libghostty-vt/src/input/KeymapDarwin.zig create mode 100644 vendor/libghostty-vt/src/input/KeymapNoop.zig create mode 100644 vendor/libghostty-vt/src/input/Link.zig create mode 100644 vendor/libghostty-vt/src/input/command.zig create mode 100644 vendor/libghostty-vt/src/input/config.zig create mode 100644 vendor/libghostty-vt/src/input/function_keys.zig create mode 100644 vendor/libghostty-vt/src/input/helpgen_actions.zig create mode 100644 vendor/libghostty-vt/src/input/key.zig create mode 100644 vendor/libghostty-vt/src/input/key_encode.zig create mode 100644 vendor/libghostty-vt/src/input/key_mods.zig create mode 100644 vendor/libghostty-vt/src/input/keyboard.zig create mode 100644 vendor/libghostty-vt/src/input/keycodes.zig create mode 100644 vendor/libghostty-vt/src/input/kitty.zig create mode 100644 vendor/libghostty-vt/src/input/mouse.zig create mode 100644 vendor/libghostty-vt/src/input/mouse_encode.zig create mode 100644 vendor/libghostty-vt/src/input/paste.zig create mode 100644 vendor/libghostty-vt/src/inspector/AGENTS.md create mode 100644 vendor/libghostty-vt/src/inspector/Inspector.zig create mode 100644 vendor/libghostty-vt/src/inspector/main.zig create mode 100644 vendor/libghostty-vt/src/inspector/units.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/key.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/page.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/pagelist.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/renderer.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/screen.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/style.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/surface.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/terminal.zig create mode 100644 vendor/libghostty-vt/src/inspector/widgets/termio.zig create mode 100644 vendor/libghostty-vt/src/lib/allocator.zig create mode 100644 vendor/libghostty-vt/src/lib/allocator/convenience.zig create mode 100644 vendor/libghostty-vt/src/lib/enum.zig create mode 100644 vendor/libghostty-vt/src/lib/main.zig create mode 100644 vendor/libghostty-vt/src/lib/string.zig create mode 100644 vendor/libghostty-vt/src/lib/struct.zig create mode 100644 vendor/libghostty-vt/src/lib/target.zig create mode 100644 vendor/libghostty-vt/src/lib/types.zig create mode 100644 vendor/libghostty-vt/src/lib/union.zig create mode 100644 vendor/libghostty-vt/src/lib_vt.zig create mode 100644 vendor/libghostty-vt/src/main.zig create mode 100644 vendor/libghostty-vt/src/main_bench.zig create mode 100644 vendor/libghostty-vt/src/main_build_data.zig create mode 100644 vendor/libghostty-vt/src/main_c.zig create mode 100644 vendor/libghostty-vt/src/main_gen.zig create mode 100644 vendor/libghostty-vt/src/main_ghostty.zig create mode 100644 vendor/libghostty-vt/src/main_wasm.zig create mode 100644 vendor/libghostty-vt/src/math.zig create mode 100644 vendor/libghostty-vt/src/os/TempDir.zig create mode 100644 vendor/libghostty-vt/src/os/args.zig create mode 100644 vendor/libghostty-vt/src/os/cf_release_thread.zig create mode 100644 vendor/libghostty-vt/src/os/cgroup.zig create mode 100644 vendor/libghostty-vt/src/os/dbus.zig create mode 100644 vendor/libghostty-vt/src/os/desktop.zig create mode 100644 vendor/libghostty-vt/src/os/env.zig create mode 100644 vendor/libghostty-vt/src/os/file.zig create mode 100644 vendor/libghostty-vt/src/os/flatpak.zig create mode 100644 vendor/libghostty-vt/src/os/homedir.zig create mode 100644 vendor/libghostty-vt/src/os/hostname.zig create mode 100644 vendor/libghostty-vt/src/os/i18n.zig create mode 100644 vendor/libghostty-vt/src/os/i18n_locales.zig create mode 100644 vendor/libghostty-vt/src/os/kernel_info.zig create mode 100644 vendor/libghostty-vt/src/os/locale.c create mode 100644 vendor/libghostty-vt/src/os/locale.zig create mode 100644 vendor/libghostty-vt/src/os/mach.zig create mode 100644 vendor/libghostty-vt/src/os/macos.zig create mode 100644 vendor/libghostty-vt/src/os/main.zig create mode 100644 vendor/libghostty-vt/src/os/mouse.zig create mode 100644 vendor/libghostty-vt/src/os/open.zig create mode 100644 vendor/libghostty-vt/src/os/passwd.zig create mode 100644 vendor/libghostty-vt/src/os/path.zig create mode 100644 vendor/libghostty-vt/src/os/pipe.zig create mode 100644 vendor/libghostty-vt/src/os/resourcesdir.zig create mode 100644 vendor/libghostty-vt/src/os/shell.zig create mode 100644 vendor/libghostty-vt/src/os/string_encoding.zig create mode 100644 vendor/libghostty-vt/src/os/systemd.zig create mode 100644 vendor/libghostty-vt/src/os/uri.zig create mode 100644 vendor/libghostty-vt/src/os/wasm.zig create mode 100644 vendor/libghostty-vt/src/os/wasm/log.zig create mode 100644 vendor/libghostty-vt/src/os/wasm/target.zig create mode 100644 vendor/libghostty-vt/src/os/windows.zig create mode 100644 vendor/libghostty-vt/src/os/xdg.zig create mode 100644 vendor/libghostty-vt/src/pty.c create mode 100644 vendor/libghostty-vt/src/pty.zig create mode 100644 vendor/libghostty-vt/src/quirks.zig create mode 100644 vendor/libghostty-vt/src/renderer.zig create mode 100644 vendor/libghostty-vt/src/renderer/Metal.zig create mode 100644 vendor/libghostty-vt/src/renderer/OpenGL.zig create mode 100644 vendor/libghostty-vt/src/renderer/Options.zig create mode 100644 vendor/libghostty-vt/src/renderer/Overlay.zig create mode 100644 vendor/libghostty-vt/src/renderer/State.zig create mode 100644 vendor/libghostty-vt/src/renderer/Thread.zig create mode 100644 vendor/libghostty-vt/src/renderer/WebGL.zig create mode 100644 vendor/libghostty-vt/src/renderer/backend.zig create mode 100644 vendor/libghostty-vt/src/renderer/cell.zig create mode 100644 vendor/libghostty-vt/src/renderer/cursor.zig create mode 100644 vendor/libghostty-vt/src/renderer/generic.zig create mode 100644 vendor/libghostty-vt/src/renderer/image.zig create mode 100644 vendor/libghostty-vt/src/renderer/link.zig create mode 100644 vendor/libghostty-vt/src/renderer/message.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/Frame.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/IOSurfaceLayer.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/Pipeline.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/RenderPass.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/Sampler.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/Target.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/Texture.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/api.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/buffer.zig create mode 100644 vendor/libghostty-vt/src/renderer/metal/shaders.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/Frame.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/Pipeline.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/RenderPass.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/Sampler.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/Target.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/Texture.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/buffer.zig create mode 100644 vendor/libghostty-vt/src/renderer/opengl/shaders.zig create mode 100644 vendor/libghostty-vt/src/renderer/row.zig create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/bg_color.f.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.f.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/bg_image.v.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/cell_bg.f.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.f.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/cell_text.v.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/common.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/full_screen.v.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/image.f.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/glsl/image.v.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/shaders.metal create mode 100644 vendor/libghostty-vt/src/renderer/shaders/shadertoy_prefix.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/test_shadertoy_crt.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/test_shadertoy_focus.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shaders/test_shadertoy_invalid.glsl create mode 100644 vendor/libghostty-vt/src/renderer/shadertoy.zig create mode 100644 vendor/libghostty-vt/src/renderer/size.zig create mode 100644 vendor/libghostty-vt/src/shell-integration/README.md create mode 100644 vendor/libghostty-vt/src/shell-integration/bash/bash-preexec.sh create mode 100644 vendor/libghostty-vt/src/shell-integration/bash/ghostty.bash create mode 100644 vendor/libghostty-vt/src/shell-integration/elvish/lib/ghostty-integration.elv create mode 100644 vendor/libghostty-vt/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish create mode 100644 vendor/libghostty-vt/src/shell-integration/nushell/vendor/autoload/ghostty.nu create mode 100644 vendor/libghostty-vt/src/shell-integration/zsh/.zshenv create mode 100644 vendor/libghostty-vt/src/shell-integration/zsh/ghostty-integration create mode 100644 vendor/libghostty-vt/src/simd/base64.cpp create mode 100644 vendor/libghostty-vt/src/simd/base64.zig create mode 100644 vendor/libghostty-vt/src/simd/base64_scalar.zig create mode 100644 vendor/libghostty-vt/src/simd/codepoint_width.cpp create mode 100644 vendor/libghostty-vt/src/simd/codepoint_width.zig create mode 100644 vendor/libghostty-vt/src/simd/index_of.cpp create mode 100644 vendor/libghostty-vt/src/simd/index_of.h create mode 100644 vendor/libghostty-vt/src/simd/index_of.zig create mode 100644 vendor/libghostty-vt/src/simd/main.zig create mode 100644 vendor/libghostty-vt/src/simd/vt.cpp create mode 100644 vendor/libghostty-vt/src/simd/vt.h create mode 100644 vendor/libghostty-vt/src/simd/vt.zig create mode 100644 vendor/libghostty-vt/src/stb/main.zig create mode 100644 vendor/libghostty-vt/src/stb/stb.c create mode 100644 vendor/libghostty-vt/src/stb/stb_image.h create mode 100644 vendor/libghostty-vt/src/stb/stb_image_resize.h create mode 100644 vendor/libghostty-vt/src/surface_mouse.zig create mode 100644 vendor/libghostty-vt/src/synthetic/Bytes.zig create mode 100644 vendor/libghostty-vt/src/synthetic/Generator.zig create mode 100644 vendor/libghostty-vt/src/synthetic/Osc.zig create mode 100644 vendor/libghostty-vt/src/synthetic/Utf8.zig create mode 100644 vendor/libghostty-vt/src/synthetic/cli.zig create mode 100644 vendor/libghostty-vt/src/synthetic/cli/Ascii.zig create mode 100644 vendor/libghostty-vt/src/synthetic/cli/Osc.zig create mode 100644 vendor/libghostty-vt/src/synthetic/cli/Utf8.zig create mode 100644 vendor/libghostty-vt/src/synthetic/main.zig create mode 100644 vendor/libghostty-vt/src/terminal/PageList.zig create mode 100644 vendor/libghostty-vt/src/terminal/Parser.zig create mode 100644 vendor/libghostty-vt/src/terminal/Screen.zig create mode 100644 vendor/libghostty-vt/src/terminal/ScreenSet.zig create mode 100644 vendor/libghostty-vt/src/terminal/Selection.zig create mode 100644 vendor/libghostty-vt/src/terminal/StringMap.zig create mode 100644 vendor/libghostty-vt/src/terminal/Tabstops.zig create mode 100644 vendor/libghostty-vt/src/terminal/Terminal.zig create mode 100644 vendor/libghostty-vt/src/terminal/UTF8Decoder.zig create mode 100644 vendor/libghostty-vt/src/terminal/ansi.zig create mode 100644 vendor/libghostty-vt/src/terminal/apc.zig create mode 100644 vendor/libghostty-vt/src/terminal/bitmap_allocator.zig create mode 100644 vendor/libghostty-vt/src/terminal/build_options.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/AGENTS.md create mode 100644 vendor/libghostty-vt/src/terminal/c/allocator.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/build_info.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/cell.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/color.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/focus.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/formatter.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/grid_ref.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/key_encode.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/key_event.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/main.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/modes.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/mouse_encode.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/mouse_event.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/osc.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/paste.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/render.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/result.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/row.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/sgr.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/size_report.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/style.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/terminal.zig create mode 100644 vendor/libghostty-vt/src/terminal/c/types.zig create mode 100644 vendor/libghostty-vt/src/terminal/charsets.zig create mode 100644 vendor/libghostty-vt/src/terminal/color.zig create mode 100644 vendor/libghostty-vt/src/terminal/csi.zig create mode 100644 vendor/libghostty-vt/src/terminal/cursor.zig create mode 100644 vendor/libghostty-vt/src/terminal/dcs.zig create mode 100644 vendor/libghostty-vt/src/terminal/device_attributes.zig create mode 100644 vendor/libghostty-vt/src/terminal/device_status.zig create mode 100644 vendor/libghostty-vt/src/terminal/focus.zig create mode 100644 vendor/libghostty-vt/src/terminal/formatter.zig create mode 100644 vendor/libghostty-vt/src/terminal/hash_map.zig create mode 100644 vendor/libghostty-vt/src/terminal/highlight.zig create mode 100644 vendor/libghostty-vt/src/terminal/hyperlink.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/color.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/graphics.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/graphics_command.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/graphics_exec.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/graphics_image.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/graphics_render.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/graphics_storage.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/graphics_unicode.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/key.zig create mode 100644 vendor/libghostty-vt/src/terminal/kitty/testdata/dog.png create mode 100644 vendor/libghostty-vt/src/terminal/kitty/testdata/image-png-none-50x76-2147483647-raw.data create mode 100644 vendor/libghostty-vt/src/terminal/kitty/testdata/image-rgb-none-20x15-2147483647-raw.data create mode 100644 vendor/libghostty-vt/src/terminal/kitty/testdata/image-rgb-zlib_deflate-128x96-2147483647-raw.data create mode 100644 vendor/libghostty-vt/src/terminal/lib.zig create mode 100644 vendor/libghostty-vt/src/terminal/main.zig create mode 100644 vendor/libghostty-vt/src/terminal/modes.zig create mode 100644 vendor/libghostty-vt/src/terminal/mouse.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/encoding.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/change_window_icon.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/change_window_title.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/clipboard_operation.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/color.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/context_signal.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/hyperlink.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/iterm2.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/kitty_clipboard_protocol.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/kitty_color.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/kitty_text_sizing.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/mouse_shape.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/osc9.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/report_pwd.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/rxvt_extension.zig create mode 100644 vendor/libghostty-vt/src/terminal/osc/parsers/semantic_prompt.zig create mode 100644 vendor/libghostty-vt/src/terminal/page.zig create mode 100644 vendor/libghostty-vt/src/terminal/parse_table.zig create mode 100644 vendor/libghostty-vt/src/terminal/point.zig create mode 100644 vendor/libghostty-vt/src/terminal/ref_counted_set.zig create mode 100644 vendor/libghostty-vt/src/terminal/render.zig create mode 100644 vendor/libghostty-vt/src/terminal/res/glitch.txt create mode 100644 vendor/libghostty-vt/src/terminal/res/rgb.txt create mode 100644 vendor/libghostty-vt/src/terminal/search.zig create mode 100644 vendor/libghostty-vt/src/terminal/search/Thread.zig create mode 100644 vendor/libghostty-vt/src/terminal/search/active.zig create mode 100644 vendor/libghostty-vt/src/terminal/search/pagelist.zig create mode 100644 vendor/libghostty-vt/src/terminal/search/screen.zig create mode 100644 vendor/libghostty-vt/src/terminal/search/sliding_window.zig create mode 100644 vendor/libghostty-vt/src/terminal/search/viewport.zig create mode 100644 vendor/libghostty-vt/src/terminal/sgr.zig create mode 100644 vendor/libghostty-vt/src/terminal/size.zig create mode 100644 vendor/libghostty-vt/src/terminal/size_report.zig create mode 100644 vendor/libghostty-vt/src/terminal/stream.zig create mode 100644 vendor/libghostty-vt/src/terminal/stream_terminal.zig create mode 100644 vendor/libghostty-vt/src/terminal/style.zig create mode 100644 vendor/libghostty-vt/src/terminal/tmux.zig create mode 100644 vendor/libghostty-vt/src/terminal/tmux/control.zig create mode 100644 vendor/libghostty-vt/src/terminal/tmux/layout.zig create mode 100644 vendor/libghostty-vt/src/terminal/tmux/output.zig create mode 100644 vendor/libghostty-vt/src/terminal/tmux/viewer.zig create mode 100644 vendor/libghostty-vt/src/terminal/x11_color.zig create mode 100644 vendor/libghostty-vt/src/terminfo/Source.zig create mode 100644 vendor/libghostty-vt/src/terminfo/ghostty.zig create mode 100644 vendor/libghostty-vt/src/terminfo/main.zig create mode 100644 vendor/libghostty-vt/src/termio.zig create mode 100644 vendor/libghostty-vt/src/termio/Exec.zig create mode 100644 vendor/libghostty-vt/src/termio/Options.zig create mode 100644 vendor/libghostty-vt/src/termio/Termio.zig create mode 100644 vendor/libghostty-vt/src/termio/Thread.zig create mode 100644 vendor/libghostty-vt/src/termio/backend.zig create mode 100644 vendor/libghostty-vt/src/termio/mailbox.zig create mode 100644 vendor/libghostty-vt/src/termio/message.zig create mode 100644 vendor/libghostty-vt/src/termio/shell_integration.zig create mode 100644 vendor/libghostty-vt/src/termio/stream_handler.zig create mode 100644 vendor/libghostty-vt/src/tripwire.zig create mode 100644 vendor/libghostty-vt/src/unicode/grapheme.zig create mode 100644 vendor/libghostty-vt/src/unicode/lut.zig create mode 100644 vendor/libghostty-vt/src/unicode/main.zig create mode 100644 vendor/libghostty-vt/src/unicode/props.zig create mode 100644 vendor/libghostty-vt/src/unicode/props_table.zig create mode 100644 vendor/libghostty-vt/src/unicode/props_uucode.zig create mode 100644 vendor/libghostty-vt/src/unicode/symbols_table.zig create mode 100644 vendor/libghostty-vt/src/unicode/symbols_uucode.zig (limited to 'vendor/libghostty-vt/src') diff --git a/vendor/libghostty-vt/src/App.zig b/vendor/libghostty-vt/src/App.zig new file mode 100644 index 00000000..5446f4bf --- /dev/null +++ b/vendor/libghostty-vt/src/App.zig @@ -0,0 +1,623 @@ +//! App is the primary GUI application for ghostty. This builds the window, +//! sets up the renderer, etc. The primary run loop is started by calling +//! the "run" function. +const App = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const assert = @import("quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const apprt = @import("apprt.zig"); +const Surface = @import("Surface.zig"); +const input = @import("input.zig"); +const configpkg = @import("config.zig"); +const Config = configpkg.Config; +const BlockingQueue = @import("datastruct/main.zig").BlockingQueue; +const renderer = @import("renderer.zig"); +const font = @import("font/main.zig"); + +const log = std.log.scoped(.app); + +const SurfaceList = std.ArrayListUnmanaged(*apprt.Surface); + +/// General purpose allocator +alloc: Allocator, + +/// The list of surfaces that are currently active. +surfaces: SurfaceList, + +/// This is true if the app that Ghostty is in is focused. This may +/// mean that no surfaces (terminals) are focused but the app is still +/// focused, i.e. may an about window. On macOS, this concept is known +/// as the "active" app while focused windows are known as the +/// "main" window. +/// +/// This is used to determine if keyboard shortcuts that are non-global +/// should be processed. If the app is not focused, then we don't want +/// to process keyboard shortcuts that are not global. +/// +/// This defaults to true since we assume that the app is focused when +/// Ghostty is initialized but a well behaved apprt should call +/// focusEvent to set this to the correct value right away. +focused: bool = true, + +/// The last focused surface. This surface may not be valid; +/// you must always call hasSurface to validate it. +focused_surface: ?*Surface = null, + +/// The mailbox that can be used to send this thread messages. Note +/// this is a blocking queue so if it is full you will get errors (or block). +mailbox: Mailbox.Queue, + +/// The set of font GroupCache instances shared by surfaces with the +/// same font configuration. +font_grid_set: font.SharedGridSet, + +// Used to rate limit desktop notifications. Some platforms (notably macOS) will +// run out of resources if desktop notifications are sent too fast and the OS +// will kill Ghostty. +last_notification_time: ?std.time.Instant = null, +last_notification_digest: u64 = 0, + +/// The conditional state of the configuration. See the equivalent field +/// in the Surface struct for more information. In this case, this applies +/// to the app-level config and as a default for new surfaces. +config_conditional_state: configpkg.ConditionalState, + +/// Set to false once we've created at least one surface. This +/// never goes true again. This can be used by surfaces to determine +/// if they are the first surface. +first: bool = true, + +pub const CreateError = Allocator.Error || font.SharedGridSet.InitError; + +/// Create a new app instance. This returns a stable pointer to the app +/// instance which is required for callbacks. +pub fn create(alloc: Allocator) CreateError!*App { + var app = try alloc.create(App); + errdefer alloc.destroy(app); + try app.init(alloc); + return app; +} + +/// Initialize the main app instance. This creates the main window, sets +/// up the renderer state, compiles the shaders, etc. This is the primary +/// "startup" logic. +/// +/// After calling this function, well behaved apprts should then call +/// `focusEvent` to set the initial focus state of the app. +pub fn init( + self: *App, + alloc: Allocator, +) CreateError!void { + var font_grid_set = try font.SharedGridSet.init(alloc); + errdefer font_grid_set.deinit(); + + self.* = .{ + .alloc = alloc, + .surfaces = .{}, + .mailbox = .{}, + .font_grid_set = font_grid_set, + .config_conditional_state = .{}, + }; +} + +pub fn deinit(self: *App) void { + // Clean up all our surfaces + for (self.surfaces.items) |surface| surface.deinit(); + self.surfaces.deinit(self.alloc); + + // Clean up our font group cache + // We should have zero items in the grid set at this point because + // destroy only gets called when the app is shutting down and this + // should gracefully close all surfaces. + assert(self.font_grid_set.count() == 0); + self.font_grid_set.deinit(); +} + +pub fn destroy(self: *App) void { + // Deinitialize the app + self.deinit(); + + // Free the app memory + self.alloc.destroy(self); +} + +/// Tick ticks the app loop. This will drain our mailbox and process those +/// events. This should be called by the application runtime on every loop +/// tick. +pub fn tick(self: *App, rt_app: *apprt.App) !void { + // Drain our mailbox + try self.drainMailbox(rt_app); +} + +/// Update the configuration associated with the app. This can only be +/// called from the main thread. The caller owns the config memory. The +/// memory can be freed immediately when this returns. +pub fn updateConfig(self: *App, rt_app: *apprt.App, config: *const Config) !void { + // Go through and update all of the surface configurations. + for (self.surfaces.items) |surface| { + try surface.core().handleMessage(.{ .change_config = config }); + } + + // Apply our conditional state. If we fail to apply the conditional state + // then we log and attempt to move forward with the old config. + // We only apply this to the app-level config because the surface + // config applies its own conditional state. + var applied_: ?configpkg.Config = config.changeConditionalState( + self.config_conditional_state, + ) catch |err| err: { + log.warn("failed to apply conditional state to config err={}", .{err}); + break :err null; + }; + defer if (applied_) |*c| c.deinit(); + const applied: *const configpkg.Config = if (applied_) |*c| c else config; + + // Notify the apprt that the app has changed configuration. + _ = try rt_app.performAction( + .app, + .config_change, + .{ .config = applied }, + ); +} + +/// Add an initialized surface. This is really only for the runtime +/// implementations to call and should NOT be called by general app users. +/// The surface must be from the pool. +pub fn addSurface( + self: *App, + rt_surface: *apprt.Surface, +) Allocator.Error!void { + try self.surfaces.append(self.alloc, rt_surface); + + // Since we have non-zero surfaces, we can cancel the quit timer. + // It is up to the apprt if there is a quit timer at all and if it + // should be canceled. + _ = rt_surface.rtApp().performAction( + .app, + .quit_timer, + .stop, + ) catch |err| { + log.warn("error stopping quit timer err={}", .{err}); + }; +} + +/// Delete the surface from the known surface list. This will NOT call the +/// destructor or free the memory. +pub fn deleteSurface(self: *App, rt_surface: *apprt.Surface) void { + // If this surface is the focused surface then we need to clear it. + // There was a bug where we relied on hasSurface to return false and + // just let focused surface be but the allocator was reusing addresses + // after free and giving false positives, so we must clear it. + if (self.focused_surface) |focused| { + if (focused == rt_surface.core()) { + self.focused_surface = null; + } + } + + var i: usize = 0; + while (i < self.surfaces.items.len) { + if (self.surfaces.items[i] == rt_surface) { + _ = self.surfaces.swapRemove(i); + continue; + } + + i += 1; + } + + // If we have no surfaces, we can start the quit timer. It is up to the + // apprt to determine if this is necessary. + if (self.surfaces.items.len == 0) _ = rt_surface.rtApp().performAction( + .app, + .quit_timer, + .start, + ) catch |err| { + log.warn("error starting quit timer err={}", .{err}); + }; +} + +/// The last focused surface. This is only valid while on the main thread +/// before tick is called. +pub fn focusedSurface(self: *const App) ?*Surface { + const surface = self.focused_surface orelse return null; + if (!self.hasSurface(surface)) return null; + return surface; +} + +/// Returns true if confirmation is needed to quit the app. It is up to +/// the apprt to call this. +pub fn needsConfirmQuit(self: *const App) bool { + for (self.surfaces.items) |v| { + if (v.core().needsConfirmQuit()) return true; + } + + return false; +} + +/// Drain the mailbox. +fn drainMailbox(self: *App, rt_app: *apprt.App) !void { + while (self.mailbox.pop()) |message| { + if (comptime std.log.logEnabled(.debug, .app)) { + switch (message) { + // these tend to be way too verbose for normal debugging + .redraw_surface => {}, + else => log.debug("mailbox message={t}", .{message}), + } + } + switch (message) { + .open_config => try self.performAction(rt_app, .open_config), + .new_window => |msg| try self.newWindow(rt_app, msg), + .close => |surface| self.closeSurface(surface), + .surface_message => |msg| try self.surfaceMessage(msg.surface, msg.message), + .redraw_surface => |surface| try self.redrawSurface(rt_app, surface), + + // If we're quitting, then we set the quit flag and stop + // draining the mailbox immediately. This lets us defer + // mailbox processing to the next tick so that the apprt + // can try to quit as quickly as possible. + .quit => { + log.info("quit message received, short circuiting mailbox drain", .{}); + try self.performAction(rt_app, .quit); + return; + }, + } + } +} + +pub fn closeSurface(self: *App, surface: *Surface) void { + if (!self.hasSurface(surface)) return; + surface.close(); +} + +pub fn focusSurface(self: *App, surface: *Surface) void { + if (!self.hasSurface(surface)) return; + self.focused_surface = surface; +} + +fn redrawSurface( + self: *App, + rt_app: *apprt.App, + surface: *apprt.Surface, +) !void { + if (!self.hasRtSurface(surface)) return; + + _ = try rt_app.performAction( + .{ .surface = surface.core() }, + .render, + {}, + ); +} + +/// Create a new window +pub fn newWindow(self: *App, rt_app: *apprt.App, msg: Message.NewWindow) !void { + const target: apprt.Target = target: { + const parent = msg.parent orelse break :target .app; + if (self.hasSurface(parent)) break :target .{ .surface = parent }; + break :target .app; + }; + + _ = try rt_app.performAction( + target, + .new_window, + {}, + ); +} + +/// Handle an app-level focus event. This should be called whenever +/// the focus state of the entire app containing Ghostty changes. +/// This is separate from surface focus events. See the `focused` +/// field for more information. +pub fn focusEvent(self: *App, focused: bool) void { + // Prevent redundant focus events + if (self.focused == focused) return; + + log.debug("focus event focused={}", .{focused}); + self.focused = focused; +} + +/// Returns true if the given key event would trigger a keybinding +/// if it were to be processed. This is useful for determining if +/// a key event should be sent to the terminal or not. +pub fn keyEventIsBinding( + self: *App, + rt_app: *apprt.App, + event: input.KeyEvent, +) bool { + _ = self; + + switch (event.action) { + .release => return false, + .press, .repeat => {}, + } + + // If we have a keybinding for this event then we return true. + return rt_app.config.keybind.set.getEvent(event) != null; +} + +/// Handle a key event at the app-scope. If this key event is used, +/// this will return true and the caller shouldn't continue processing +/// the event. If the event is not used, this will return false. +/// +/// If the app currently has focus then all key events are processed. +/// If the app does not have focus then only global key events are +/// processed. +pub fn keyEvent( + self: *App, + rt_app: *apprt.App, + event: input.KeyEvent, +) bool { + switch (event.action) { + // We don't care about key release events. + .release => return false, + + // Continue processing key press events. + .press, .repeat => {}, + } + + // Get the keybind entry for this event. We don't support key sequences + // so we can look directly in the top-level set. + const entry = rt_app.config.keybind.set.getEvent(event) orelse return false; + const leaf: input.Binding.Set.GenericLeaf = switch (entry.value_ptr.*) { + // Sequences aren't supported. Our configuration parser verifies + // this for global keybinds but we may still get an entry for + // a non-global keybind. + .leader => return false, + + // Leaf entries are good + inline .leaf, .leaf_chained => |leaf| leaf.generic(), + }; + const actions: []const input.Binding.Action = leaf.actionsSlice(); + assert(actions.len > 0); + + // If we aren't focused, then we only process global keybinds. + if (!self.focused and !leaf.flags.global) return false; + + // Global keybinds are done using performAll so that they + // can target all surfaces too. + if (leaf.flags.global) { + self.performAllChainedAction(rt_app, actions); + return true; + } + + // Must be focused to process non-global keybinds + assert(self.focused); + assert(!leaf.flags.global); + + // If we are focused, then we process keybinds only if they are + // app-scoped. Otherwise, we do nothing. Surface-scoped should + // be processed by Surface.keyEvent. For chained actions, all + // actions must be app-scoped. + for (actions) |action| if (action.scoped(.app) == null) return false; + for (actions) |action| { + self.performAction( + rt_app, + action.scoped(.app).?, + ) catch |err| { + log.warn("error performing app keybind action action={s} err={}", .{ + @tagName(action), + err, + }); + }; + } + + return true; +} + +/// Call to notify Ghostty that the color scheme for the app has changed. +/// "Color scheme" in this case refers to system themes such as "light/dark". +pub fn colorSchemeEvent( + self: *App, + rt_app: *apprt.App, + scheme: apprt.ColorScheme, +) !void { + const new_scheme: configpkg.ConditionalState.Theme = switch (scheme) { + .light => .light, + .dark => .dark, + }; + + // If our scheme didn't change, then we don't do anything. + if (self.config_conditional_state.theme == new_scheme) return; + + // Setup our conditional state which has the current color theme. + self.config_conditional_state.theme = new_scheme; + + // Request our configuration be reloaded because the new scheme may + // impact the colors of the app. + _ = try rt_app.performAction( + .app, + .reload_config, + .{ .soft = true }, + ); +} + +/// Perform a binding action. This only accepts actions that are scoped +/// to the app. Callers can use performAllAction to perform any action +/// and any non-app-scoped actions will be performed on all surfaces. +pub fn performAction( + self: *App, + rt_app: *apprt.App, + action: input.Binding.Action.Scoped(.app), +) !void { + switch (action) { + .unbind => unreachable, + .ignore => {}, + .quit => _ = try rt_app.performAction(.app, .quit, {}), + .new_window => _ = try self.newWindow(rt_app, .{ .parent = null }), + .open_config => _ = try rt_app.performAction(.app, .open_config, {}), + .reload_config => _ = try rt_app.performAction(.app, .reload_config, .{}), + .close_all_windows => _ = try rt_app.performAction(.app, .close_all_windows, {}), + .toggle_quick_terminal => _ = try rt_app.performAction(.app, .toggle_quick_terminal, {}), + .toggle_visibility => _ = try rt_app.performAction(.app, .toggle_visibility, {}), + .check_for_updates => _ = try rt_app.performAction(.app, .check_for_updates, {}), + .show_gtk_inspector => _ = try rt_app.performAction(.app, .show_gtk_inspector, {}), + .undo => _ = try rt_app.performAction(.app, .undo, {}), + + .redo => _ = try rt_app.performAction(.app, .redo, {}), + } +} + +/// Performs a chained action. We will continue executing each action +/// even if there is a failure in a prior action. +pub fn performAllChainedAction( + self: *App, + rt_app: *apprt.App, + actions: []const input.Binding.Action, +) void { + for (actions) |action| { + self.performAllAction(rt_app, action) catch |err| { + log.warn("error performing chained action action={s} err={}", .{ + @tagName(action), + err, + }); + }; + } +} + +/// Perform an app-wide binding action. If the action is surface-specific +/// then it will be performed on all surfaces. To perform only app-scoped +/// actions, use performAction. +pub fn performAllAction( + self: *App, + rt_app: *apprt.App, + action: input.Binding.Action, +) !void { + switch (action.scope()) { + // App-scoped actions are handled by the app so that they aren't + // repeated for each surface (since each surface forwards + // app-scoped actions back up). + .app => try self.performAction( + rt_app, + action.scoped(.app).?, // asserted through the scope match + ), + + // Surface-scoped actions are performed on all surfaces. Errors + // are logged but processing continues. + .surface => for (self.surfaces.items) |surface| { + _ = surface.core().performBindingAction(action) catch |err| { + log.warn("error performing binding action on surface ptr={X} err={}", .{ + @intFromPtr(surface), + err, + }); + }; + }, + } +} + +/// Handle a window message +fn surfaceMessage(self: *App, surface: *Surface, msg: apprt.surface.Message) !void { + // We want to ensure our window is still active. Window messages + // are quite rare and we normally don't have many windows so we do + // a simple linear search here. + if (self.hasSurface(surface)) { + try surface.handleMessage(msg); + } + + // Window was not found, it probably quit before we handled the message. + // Not a problem. +} + +fn hasSurface(self: *const App, surface: *const Surface) bool { + for (self.surfaces.items) |v| { + if (v.core() == surface) return true; + } + + return false; +} + +/// Search for a surface by a 64 bit unique ID. +pub fn findSurfaceByID(self: *const App, id: u64) ?*Surface { + for (self.surfaces.items) |v| { + const surface: *Surface = v.core(); + if (surface.id == id) return surface; + } + + return null; +} + +fn hasRtSurface(self: *const App, surface: *apprt.Surface) bool { + for (self.surfaces.items) |v| { + if (v == surface) return true; + } + + return false; +} + +/// The message types that can be sent to the app thread. +pub const Message = union(enum) { + // Open the configuration file + open_config: void, + + /// Create a new terminal window. + new_window: NewWindow, + + /// Close a surface. This notifies the runtime that a surface + /// should close. + close: *Surface, + + /// Quit + quit: void, + + /// A message for a specific surface. + surface_message: struct { + surface: *Surface, + message: apprt.surface.Message, + }, + + /// Redraw a surface. This only has an effect for runtimes that + /// use single-threaded draws. To redraw a surface for all runtimes, + /// wake up the renderer thread. The renderer thread will send this + /// message if it needs to. + redraw_surface: *apprt.Surface, + + const NewWindow = struct { + /// The parent surface + parent: ?*Surface = null, + }; +}; + +/// Mailbox is the way that other threads send the app thread messages. +pub const Mailbox = struct { + /// The type used for sending messages to the app thread. + pub const Queue = BlockingQueue(Message, 64); + + rt_app: *apprt.App, + mailbox: *Queue, + + /// Send a message to the surface. + pub fn push(self: Mailbox, msg: Message, timeout: Queue.Timeout) Queue.Size { + const result = self.mailbox.push(msg, timeout); + + // Wake up our app loop + self.rt_app.wakeup(); + + return result; + } +}; + +// Wasm API. +pub const Wasm = if (!builtin.target.isWasm()) struct {} else struct { + const wasm = @import("os/wasm.zig"); + const alloc = wasm.alloc; + + // export fn app_new(config: *Config) ?*App { + // return app_new_(config) catch |err| { log.err("error initializing app err={}", .{err}); + // return null; + // }; + // } + // + // fn app_new_(config: *Config) !*App { + // const app = try App.create(alloc, config); + // errdefer app.destroy(); + // + // const result = try alloc.create(App); + // result.* = app; + // return result; + // } + // + // export fn app_free(ptr: ?*App) void { + // if (ptr) |v| { + // v.destroy(); + // alloc.destroy(v); + // } + // } +}; diff --git a/vendor/libghostty-vt/src/Command.zig b/vendor/libghostty-vt/src/Command.zig new file mode 100644 index 00000000..2b381912 --- /dev/null +++ b/vendor/libghostty-vt/src/Command.zig @@ -0,0 +1,903 @@ +//! Command launches sub-processes. This is an alternate implementation to the +//! Zig std.process.Child since at the time of authoring this, std.process.Child +//! didn't support the options necessary to spawn a shell attached to a pty. +//! +//! Consequently, I didn't implement a lot of features that std.process.Child +//! supports because we didn't need them. Cross-platform subprocessing is not +//! a trivial thing to implement (I've done it in three separate languages now) +//! so if we want to replatform onto std.process.Child I'd love to do that. +//! This was just the fastest way to get something built. +//! +//! Issues with std.process.Child: +//! +//! * No pre_exec callback for logic after fork but before exec. +//! * posix_spawn is used for Mac, but doesn't support the necessary +//! features for tty setup. +//! +const Command = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const configpkg = @import("config.zig"); +const global_state = &@import("global.zig").state; +const internal_os = @import("os/main.zig"); +const windows = internal_os.windows; +const TempDir = internal_os.TempDir; +const mem = std.mem; +const linux = std.os.linux; +const posix = std.posix; +const debug = std.debug; +const testing = std.testing; +const Allocator = std.mem.Allocator; +const File = std.fs.File; +const EnvMap = std.process.EnvMap; +const apprt = @import("apprt.zig"); + +/// Function prototype for a function executed /in the child process/ after the +/// fork, but before exec'ing the command. If the function returns a u8, the +/// child process will be exited with that error code. +const PreExecFn = fn (*Command) ?u8; + +/// Allowable set of errors that can be returned by a post fork function. Any +/// errors will result in the failure to create the surface. +pub const PostForkError = error{PostForkError}; + +/// Function prototype for a function executed /in the parent process/ +/// after the fork. +const PostForkFn = fn (*Command) PostForkError!void; + +/// Path to the command to run. This doesn't have to be an absolute path, +/// because use exec functions that search the PATH, if necessary. +/// +/// This field is null-terminated to avoid a copy for the sake of +/// adding a null terminator since POSIX systems are so common. +path: [:0]const u8, + +/// Command-line arguments. It is the responsibility of the caller to set +/// args[0] to the command. If args is empty then args[0] will automatically +/// be set to equal path. +args: []const [:0]const u8, + +/// Environment variables for the child process. If this is null, inherits +/// the environment variables from this process. These are the exact +/// environment variables to set; these are /not/ merged. +env: ?*const EnvMap = null, + +/// Working directory to change to in the child process. If not set, the +/// working directory of the calling process is preserved. +cwd: ?[]const u8 = null, + +/// The file handle to set for stdin/out/err. If this isn't set, we do +/// nothing explicitly so it is up to the behavior of the operating system. +stdin: ?File = null, +stdout: ?File = null, +stderr: ?File = null, + +/// If set, this will be executed /in the child process/ after fork but +/// before exec. This is useful to setup some state in the child before the +/// exec process takes over, such as signal handlers, setsid, setuid, etc. +os_pre_exec: ?*const PreExecFn, + +/// If set, this will be executed /in the child process/ after fork but +/// before exec. This is useful to setup some state in the child before the +/// exec process takes over, such as signal handlers, setsid, setuid, etc. +rt_pre_exec: ?*const PreExecFn, + +/// Configuration information needed by the apprt pre exec function. Note +/// that this should be a trivially copyable struct and not require any +/// allocation/deallocation. +rt_pre_exec_info: RtPreExecInfo, + +/// If set, this will be executed in the /in the parent process/ after the fork. +rt_post_fork: ?*const PostForkFn, + +/// Configuration information needed by the apprt post fork function. Note +/// that this should be a trivially copyable struct and not require any +/// allocation/deallocation. +rt_post_fork_info: RtPostForkInfo, + +/// If set, then the process will be created attached to this pseudo console. +/// `stdin`, `stdout`, and `stderr` will be ignored if set. +pseudo_console: if (builtin.os.tag == .windows) ?windows.exp.HPCON else void = + if (builtin.os.tag == .windows) null else {}, + +/// User data that is sent to the callback. Set with setData and getData +/// for a more user-friendly API. +data: ?*anyopaque = null, + +/// Process ID is set after start is called. +pid: ?posix.pid_t = null, + +/// The various methods a process may exit. +pub const Exit = if (builtin.os.tag == .windows) union(enum) { + Exited: u32, +} else union(enum) { + /// Exited by normal exit call, value is exit status + Exited: u8, + + /// Exited by a signal, value is the signal + Signal: u32, + + /// Exited by a stop signal, value is signal + Stopped: u32, + + /// Unknown exit reason, value is the status from waitpid + Unknown: u32, + + pub fn init(status: u32) Exit { + return if (posix.W.IFEXITED(status)) + Exit{ .Exited = posix.W.EXITSTATUS(status) } + else if (posix.W.IFSIGNALED(status)) + Exit{ .Signal = posix.W.TERMSIG(status) } + else if (posix.W.IFSTOPPED(status)) + Exit{ .Stopped = posix.W.STOPSIG(status) } + else + Exit{ .Unknown = status }; + } +}; + +/// Configuration information needed by the apprt pre exec function. Note +/// that this should be a trivially copyable struct and not require any +/// allocation/deallocation. +pub const RtPreExecInfo = if (@hasDecl(apprt.runtime, "pre_exec")) apprt.runtime.pre_exec.PreExecInfo else struct { + pub inline fn init(_: *const configpkg.Config) @This() { + return .{}; + } +}; + +/// Configuration information needed by the apprt post fork function. Note +/// that this should be a trivially copyable struct and not require any +/// allocation/deallocation. +pub const RtPostForkInfo = if (@hasDecl(apprt.runtime, "post_fork")) apprt.runtime.post_fork.PostForkInfo else struct { + pub inline fn init(_: *const configpkg.Config) @This() { + return .{}; + } +}; + +/// Start the subprocess. This returns immediately once the child is started. +/// +/// After this is successful, self.pid is available. +pub fn start(self: *Command, alloc: Allocator) !void { + // Use an arena allocator for the temporary allocations we need in this func. + // IMPORTANT: do all allocation prior to the fork(). I believe it is undefined + // behavior if you malloc between fork and exec. The source of the Zig + // stdlib seems to verify this as well as Go. + var arena_allocator = std.heap.ArenaAllocator.init(alloc); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + switch (builtin.os.tag) { + .windows => try self.startWindows(arena), + else => try self.startPosix(arena), + } +} + +fn startPosix(self: *Command, arena: Allocator) !void { + // Null-terminate all our arguments + const argsZ = try arena.allocSentinel(?[*:0]const u8, self.args.len, null); + for (self.args, 0..) |arg, i| argsZ[i] = arg.ptr; + + // Determine our env vars + const envp = if (self.env) |env_map| + (try createNullDelimitedEnvMap(arena, env_map)).ptr + else if (builtin.link_libc) + std.c.environ + else + @compileError("missing env vars"); + + // Fork. + const pid = try posix.fork(); + + if (pid != 0) { + // Parent, return immediately. + self.pid = @intCast(pid); + if (self.rt_post_fork) |f| try f(self); + return; + } + + // We are the child. + + // Setup our file descriptors for std streams. + if (self.stdin) |f| setupFd(f.handle, posix.STDIN_FILENO) catch + return error.ExecFailedInChild; + if (self.stdout) |f| setupFd(f.handle, posix.STDOUT_FILENO) catch + return error.ExecFailedInChild; + if (self.stderr) |f| setupFd(f.handle, posix.STDERR_FILENO) catch + return error.ExecFailedInChild; + + // Setup our working directory + if (self.cwd) |cwd| posix.chdir(cwd) catch { + // This can fail if we don't have permission to go to + // this directory or if due to race conditions it doesn't + // exist or any various other reasons. We don't want to + // crash the entire process if this fails so we ignore it. + // We don't log because that'll show up in the output. + }; + + // Restore any rlimits that were set by Ghostty. This might fail but + // any failures are ignored (its best effort). + global_state.rlimits.restore(); + + // If there are pre exec callbacks, call them now. + if (self.os_pre_exec) |f| if (f(self)) |exitcode| posix.exit(exitcode); + if (self.rt_pre_exec) |f| if (f(self)) |exitcode| posix.exit(exitcode); + + // Finally, replace our process. + // Note: we must use the "p"-variant of exec here because we + // do not guarantee our command is looked up already in the path. + const err = posix.execvpeZ(self.path, argsZ, envp); + + // If we are executing this code, the exec failed. We're in the + // child process so there isn't much we can do. We try to output + // something reasonable. Its important to note we MUST NOT return + // any other error condition from here on out. + var stderr_buf: [1024]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&stderr_buf); + const stderr = &stderr_writer.interface; + switch (err) { + error.FileNotFound => stderr.print( + \\Requested executable not found. Please verify the command is on + \\the PATH and try again. + \\ + , + .{}, + ) catch {}, + + else => stderr.print( + \\exec syscall failed with unexpected error: {} + \\ + , + .{err}, + ) catch {}, + } + stderr.flush() catch {}; + + // We return a very specific error that can be detected to determine + // we're in the child. + return error.ExecFailedInChild; +} + +fn startWindows(self: *Command, arena: Allocator) !void { + const application_w = try std.unicode.utf8ToUtf16LeAllocZ(arena, self.path); + const cwd_w = if (self.cwd) |cwd| try std.unicode.utf8ToUtf16LeAllocZ(arena, cwd) else null; + const command_line_w = if (self.args.len > 0) b: { + const command_line = try windowsCreateCommandLine(arena, self.args); + break :b try std.unicode.utf8ToUtf16LeAllocZ(arena, command_line); + } else null; + const env_w = if (self.env) |env_map| try createWindowsEnvBlock(arena, env_map) else null; + + const any_null_fd = self.stdin == null or self.stdout == null or self.stderr == null; + const null_fd = if (any_null_fd) try windows.OpenFile( + &[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, + .{ + .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, + .share_access = windows.FILE_SHARE_READ, + .creation = windows.OPEN_EXISTING, + }, + ) else null; + defer if (null_fd) |fd| posix.close(fd); + + // TODO: In the case of having FDs instead of pty, need to set up + // attributes such that the child process only inherits these handles, + // then set bInheritsHandles below. + + const attribute_list, const stdin, const stdout, const stderr = if (self.pseudo_console) |pseudo_console| b: { + var attribute_list_size: usize = undefined; + _ = windows.exp.kernel32.InitializeProcThreadAttributeList( + null, + 1, + 0, + &attribute_list_size, + ); + + const attribute_list_buf = try arena.alloc(u8, attribute_list_size); + if (windows.exp.kernel32.InitializeProcThreadAttributeList( + attribute_list_buf.ptr, + 1, + 0, + &attribute_list_size, + ) == 0) return windows.unexpectedError(windows.kernel32.GetLastError()); + + if (windows.exp.kernel32.UpdateProcThreadAttribute( + attribute_list_buf.ptr, + 0, + windows.exp.PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + pseudo_console, + @sizeOf(windows.exp.HPCON), + null, + null, + ) == 0) return windows.unexpectedError(windows.kernel32.GetLastError()); + + break :b .{ attribute_list_buf.ptr, null, null, null }; + } else b: { + const stdin = if (self.stdin) |f| f.handle else null_fd.?; + const stdout = if (self.stdout) |f| f.handle else null_fd.?; + const stderr = if (self.stderr) |f| f.handle else null_fd.?; + break :b .{ null, stdin, stdout, stderr }; + }; + + var startup_info_ex = windows.exp.STARTUPINFOEX{ + .StartupInfo = .{ + .cb = if (attribute_list != null) @sizeOf(windows.exp.STARTUPINFOEX) else @sizeOf(windows.STARTUPINFOW), + .hStdError = stderr, + .hStdOutput = stdout, + .hStdInput = stdin, + .dwFlags = windows.STARTF_USESTDHANDLES, + .lpReserved = null, + .lpDesktop = null, + .lpTitle = null, + .dwX = 0, + .dwY = 0, + .dwXSize = 0, + .dwYSize = 0, + .dwXCountChars = 0, + .dwYCountChars = 0, + .dwFillAttribute = 0, + .wShowWindow = 0, + .cbReserved2 = 0, + .lpReserved2 = null, + }, + .lpAttributeList = attribute_list, + }; + + var flags: windows.DWORD = windows.exp.CREATE_UNICODE_ENVIRONMENT; + if (attribute_list != null) flags |= windows.exp.EXTENDED_STARTUPINFO_PRESENT; + + var process_information: windows.PROCESS_INFORMATION = undefined; + if (windows.exp.kernel32.CreateProcessW( + application_w.ptr, + if (command_line_w) |w| w.ptr else null, + null, + null, + windows.TRUE, + flags, + if (env_w) |w| w.ptr else null, + if (cwd_w) |w| w.ptr else null, + @ptrCast(&startup_info_ex.StartupInfo), + &process_information, + ) == 0) return windows.unexpectedError(windows.kernel32.GetLastError()); + + self.pid = process_information.hProcess; +} + +fn setupFd(src: File.Handle, target: i32) !void { + switch (builtin.os.tag) { + .linux => { + // We use dup3 so that we can clear CLO_ON_EXEC. We do NOT want this + // file descriptor to be closed on exec since we're exactly exec-ing after + // this. + while (true) { + const rc = linux.dup3(src, target, 0); + switch (posix.errno(rc)) { + .SUCCESS => break, + .INTR => continue, + .AGAIN, .ACCES => return error.Locked, + .BADF => unreachable, + .BUSY => return error.FileBusy, + .INVAL => unreachable, // invalid parameters + .PERM => return error.PermissionDenied, + .MFILE => return error.ProcessFdQuotaExceeded, + .NOTDIR => unreachable, // invalid parameter + .DEADLK => return error.DeadLock, + .NOLCK => return error.LockedRegionLimitExceeded, + else => |err| return posix.unexpectedErrno(err), + } + } + }, + .freebsd, .ios, .macos => { + // Mac doesn't support dup3 so we use dup2. We purposely clear + // CLO_ON_EXEC for this fd. + const flags = try posix.fcntl(src, posix.F.GETFD, 0); + if (flags & posix.FD_CLOEXEC != 0) { + _ = try posix.fcntl(src, posix.F.SETFD, flags & ~@as(u32, posix.FD_CLOEXEC)); + } + + try posix.dup2(src, target); + }, + else => @compileError("unsupported platform"), + } +} + +/// Wait for the command to exit and return information about how it exited. +pub fn wait(self: Command, block: bool) !Exit { + if (comptime builtin.os.tag == .windows) { + // Block until the process exits. This returns immediately if the + // process already exited. + const result = windows.kernel32.WaitForSingleObject(self.pid.?, windows.INFINITE); + if (result == windows.WAIT_FAILED) { + return windows.unexpectedError(windows.kernel32.GetLastError()); + } + + var exit_code: windows.DWORD = undefined; + const has_code = windows.kernel32.GetExitCodeProcess(self.pid.?, &exit_code) != 0; + if (!has_code) { + return windows.unexpectedError(windows.kernel32.GetLastError()); + } + + return .{ .Exited = exit_code }; + } + + const res = if (block) posix.waitpid(self.pid.?, 0) else res: { + // We specify NOHANG because its not our fault if the process we launch + // for the tty doesn't properly waitpid its children. We don't want + // to hang the terminal over it. + // When NOHANG is specified, waitpid will return a pid of 0 if the process + // doesn't have a status to report. When that happens, it is as though the + // wait call has not been performed, so we need to keep trying until we get + // a non-zero pid back, otherwise we end up with zombie processes. + while (true) { + const res = posix.waitpid(self.pid.?, std.c.W.NOHANG); + if (res.pid != 0) break :res res; + } + }; + + return .init(res.status); +} + +/// Sets command->data to data. +pub fn setData(self: *Command, pointer: ?*anyopaque) void { + self.data = pointer; +} + +/// Returns command->data. +pub fn getData(self: Command, comptime DT: type) ?*DT { + return if (self.data) |ptr| @ptrCast(@alignCast(ptr)) else null; +} + +// Copied from Zig. This is a publicly exported function but there is no +// way to get it from the std package. +fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 { + const envp_count = env_map.count(); + const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); + + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| : (i += 1) { + const env_buf = try arena.allocSentinel(u8, pair.key_ptr.len + pair.value_ptr.len + 1, 0); + @memcpy(env_buf[0..pair.key_ptr.len], pair.key_ptr.*); + env_buf[pair.key_ptr.len] = '='; + @memcpy(env_buf[pair.key_ptr.len + 1 ..], pair.value_ptr.*); + envp_buf[i] = env_buf.ptr; + } + std.debug.assert(i == envp_count); + + return envp_buf; +} + +// Copied from Zig. This is a publicly exported function but there is no +// way to get it from the std package. +fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ![]u16 { + // count bytes needed + const max_chars_needed = x: { + var max_chars_needed: usize = 4; // 4 for the final 4 null bytes + var it = env_map.iterator(); + while (it.next()) |pair| { + // +1 for '=' + // +1 for null byte + max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; + } + break :x max_chars_needed; + }; + const result = try allocator.alloc(u16, max_chars_needed); + errdefer allocator.free(result); + + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| { + i += try std.unicode.utf8ToUtf16Le(result[i..], pair.key_ptr.*); + result[i] = '='; + i += 1; + i += try std.unicode.utf8ToUtf16Le(result[i..], pair.value_ptr.*); + result[i] = 0; + i += 1; + } + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + return try allocator.realloc(result, i); +} + +/// Copied from Zig. This function could be made public in child_process.zig instead. +fn windowsCreateCommandLine(allocator: mem.Allocator, argv: []const []const u8) ![:0]u8 { + var buf: std.Io.Writer.Allocating = .init(allocator); + defer buf.deinit(); + const writer = &buf.writer; + + for (argv, 0..) |arg, arg_i| { + if (arg_i != 0) try writer.writeByte(' '); + if (mem.indexOfAny(u8, arg, " \t\n\"") == null) { + try writer.writeAll(arg); + continue; + } + try writer.writeByte('"'); + var backslash_count: usize = 0; + for (arg) |byte| { + switch (byte) { + '\\' => backslash_count += 1, + '"' => { + try writer.splatByteAll('\\', backslash_count * 2 + 1); + try writer.writeByte('"'); + backslash_count = 0; + }, + else => { + try writer.splatByteAll('\\', backslash_count); + try writer.writeByte(byte); + backslash_count = 0; + }, + } + } + try writer.splatByteAll('\\', backslash_count * 2); + try writer.writeByte('"'); + } + + return buf.toOwnedSliceSentinel(0); +} + +test "createNullDelimitedEnvMap" { + const allocator = testing.allocator; + var envmap = EnvMap.init(allocator); + defer envmap.deinit(); + + try envmap.put("HOME", "/home/ifreund"); + try envmap.put("WAYLAND_DISPLAY", "wayland-1"); + try envmap.put("DISPLAY", ":1"); + try envmap.put("DEBUGINFOD_URLS", " "); + try envmap.put("XCURSOR_SIZE", "24"); + + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + const environ = try createNullDelimitedEnvMap(arena.allocator(), &envmap); + + try testing.expectEqual(@as(usize, 5), environ.len); + + inline for (.{ + "HOME=/home/ifreund", + "WAYLAND_DISPLAY=wayland-1", + "DISPLAY=:1", + "DEBUGINFOD_URLS= ", + "XCURSOR_SIZE=24", + }) |target| { + for (environ) |variable| { + if (mem.eql(u8, mem.span(variable orelse continue), target)) break; + } else { + try testing.expect(false); // Environment variable not found + } + } +} + +test "Command: os pre exec 1" { + if (builtin.os.tag == .windows) return error.SkipZigTest; + var cmd: Command = .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-v" }, + .os_pre_exec = (struct { + fn do(_: *Command) ?u8 { + // This runs in the child, so we can exit and it won't + // kill the test runner. + posix.exit(42); + } + }).do, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expect(exit.Exited == 42); +} + +test "Command: os pre exec 2" { + if (builtin.os.tag == .windows) return error.SkipZigTest; + var cmd: Command = .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-v" }, + .os_pre_exec = (struct { + fn do(_: *Command) ?u8 { + // This runs in the child, so we can exit and it won't + // kill the test runner. + return 42; + } + }).do, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expect(exit.Exited == 42); +} + +test "Command: rt pre exec 1" { + if (builtin.os.tag == .windows) return error.SkipZigTest; + var cmd: Command = .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-v" }, + .os_pre_exec = null, + .rt_pre_exec = (struct { + fn do(_: *Command) ?u8 { + // This runs in the child, so we can exit and it won't + // kill the test runner. + posix.exit(42); + } + }).do, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expect(exit.Exited == 42); +} + +test "Command: rt pre exec 2" { + if (builtin.os.tag == .windows) return error.SkipZigTest; + var cmd: Command = .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-v" }, + .os_pre_exec = null, + .rt_pre_exec = (struct { + fn do(_: *Command) ?u8 { + // This runs in the child, so we can exit and it won't + // kill the test runner. + return 42; + } + }).do, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expect(exit.Exited == 42); +} + +test "Command: rt post fork 1" { + if (builtin.os.tag == .windows) return error.SkipZigTest; + var cmd: Command = .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-c", "sleep 1" }, + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = (struct { + fn do(_: *Command) PostForkError!void { + return error.PostForkError; + } + }).do, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try testing.expectError(error.PostForkError, cmd.testingStart()); +} + +fn createTestStdout(dir: std.fs.Dir) !File { + const file = try dir.createFile("stdout.txt", .{ .read = true }); + if (builtin.os.tag == .windows) { + try windows.SetHandleInformation( + file.handle, + windows.HANDLE_FLAG_INHERIT, + windows.HANDLE_FLAG_INHERIT, + ); + } + + return file; +} + +fn createTestStderr(dir: std.fs.Dir) !File { + const file = try dir.createFile("stderr.txt", .{ .read = true }); + if (builtin.os.tag == .windows) { + try windows.SetHandleInformation( + file.handle, + windows.HANDLE_FLAG_INHERIT, + windows.HANDLE_FLAG_INHERIT, + ); + } + + return file; +} + +test "Command: redirect stdout to file" { + var td = try TempDir.init(); + defer td.deinit(); + var stdout = try createTestStdout(td.dir); + defer stdout.close(); + + var cmd: Command = if (builtin.os.tag == .windows) .{ + .path = "C:\\Windows\\System32\\whoami.exe", + .args = &.{"C:\\Windows\\System32\\whoami.exe"}, + .stdout = stdout, + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + } else .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-c", "echo hello" }, + .stdout = stdout, + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expectEqual(@as(u32, 0), @as(u32, exit.Exited)); + + // Read our stdout + try stdout.seekTo(0); + const contents = try stdout.readToEndAlloc(testing.allocator, 1024 * 128); + defer testing.allocator.free(contents); + try testing.expect(contents.len > 0); +} + +test "Command: custom env vars" { + var td = try TempDir.init(); + defer td.deinit(); + var stdout = try createTestStdout(td.dir); + defer stdout.close(); + + var env = EnvMap.init(testing.allocator); + defer env.deinit(); + try env.put("VALUE", "hello"); + + var cmd: Command = if (builtin.os.tag == .windows) .{ + .path = "C:\\Windows\\System32\\cmd.exe", + .args = &.{ "C:\\Windows\\System32\\cmd.exe", "/C", "echo %VALUE%" }, + .stdout = stdout, + .env = &env, + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + } else .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-c", "echo $VALUE" }, + .stdout = stdout, + .env = &env, + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expect(exit.Exited == 0); + + // Read our stdout + try stdout.seekTo(0); + const contents = try stdout.readToEndAlloc(testing.allocator, 4096); + defer testing.allocator.free(contents); + + if (builtin.os.tag == .windows) { + try testing.expectEqualStrings("hello\r\n", contents); + } else { + try testing.expectEqualStrings("hello\n", contents); + } +} + +test "Command: custom working directory" { + var td = try TempDir.init(); + defer td.deinit(); + var stdout = try createTestStdout(td.dir); + defer stdout.close(); + + var cmd: Command = if (builtin.os.tag == .windows) .{ + .path = "C:\\Windows\\System32\\cmd.exe", + .args = &.{ "C:\\Windows\\System32\\cmd.exe", "/C", "cd" }, + .stdout = stdout, + .cwd = "C:\\Windows\\System32", + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + } else .{ + .path = "/bin/sh", + .args = &.{ "/bin/sh", "-c", "pwd" }, + .stdout = stdout, + .cwd = "/tmp", + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expect(exit.Exited == 0); + + // Read our stdout + try stdout.seekTo(0); + const contents = try stdout.readToEndAlloc(testing.allocator, 4096); + defer testing.allocator.free(contents); + + if (builtin.os.tag == .windows) { + try testing.expectEqualStrings("C:\\Windows\\System32\r\n", contents); + } else if (builtin.os.tag == .macos) { + try testing.expectEqualStrings("/private/tmp\n", contents); + } else { + try testing.expectEqualStrings("/tmp\n", contents); + } +} + +// Test validate an execveZ failure correctly terminates when error.ExecFailedInChild is correctly handled +// +// Incorrectly handling an error.ExecFailedInChild results in a second copy of the test process running. +// Duplicating the test process leads to weird behavior +// zig build test will hang +// test binary created via -Demit-test-exe will run 2 copies of the test suite +test "Command: posix fork handles execveZ failure" { + if (builtin.os.tag == .windows) { + return error.SkipZigTest; + } + var td = try TempDir.init(); + defer td.deinit(); + var stdout = try createTestStdout(td.dir); + defer stdout.close(); + var stderr = try createTestStderr(td.dir); + defer stderr.close(); + + var cmd: Command = .{ + .path = "/not/a/binary", + .args = &.{ "/not/a/binary", "" }, + .stdout = stdout, + .stderr = stderr, + .cwd = "/bin", + .os_pre_exec = null, + .rt_pre_exec = null, + .rt_post_fork = null, + .rt_pre_exec_info = undefined, + .rt_post_fork_info = undefined, + }; + + try cmd.testingStart(); + try testing.expect(cmd.pid != null); + const exit = try cmd.wait(true); + try testing.expect(exit == .Exited); + try testing.expect(exit.Exited == 1); +} + +// If cmd.start fails with error.ExecFailedInChild it's the _child_ process that is running. If it does not +// terminate in response to that error both the parent and child will continue as if they _are_ the test suite +// process. +fn testingStart(self: *Command) !void { + self.start(testing.allocator) catch |err| { + if (err == error.ExecFailedInChild) { + // I am a child process, I must not get confused and continue running the rest of the test suite. + posix.exit(1); + } + return err; + }; +} diff --git a/vendor/libghostty-vt/src/Surface.zig b/vendor/libghostty-vt/src/Surface.zig new file mode 100644 index 00000000..dfc3a50e --- /dev/null +++ b/vendor/libghostty-vt/src/Surface.zig @@ -0,0 +1,6660 @@ +//! Surface represents a single terminal "surface". A terminal surface is +//! a minimal "widget" where the terminal is drawn and responds to events +//! such as keyboard and mouse. Each surface also creates and owns its pty +//! session. +//! +//! The word "surface" is used because it is left to the higher level +//! application runtime to determine if the surface is a window, a tab, +//! a split, a preview pane in a larger window, etc. This struct doesn't care: +//! it just draws and responds to events. The events come from the application +//! runtime so the runtime can determine when and how those are delivered +//! (i.e. with focus, without focus, and so on). +const Surface = @This(); + +const apprt = @import("apprt.zig"); +pub const Mailbox = apprt.surface.Mailbox; +pub const Message = apprt.surface.Message; + +const std = @import("std"); +const builtin = @import("builtin"); +const assert = @import("quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const global_state = &@import("global.zig").state; +const oni = @import("oniguruma"); +const crash = @import("crash/main.zig"); +const unicode = @import("unicode/main.zig"); +const rendererpkg = @import("renderer.zig"); +const termio = @import("termio.zig"); +const font = @import("font/main.zig"); +const Command = @import("Command.zig"); +const terminal = @import("terminal/main.zig"); +const configpkg = @import("config.zig"); +const Duration = configpkg.Config.Duration; +const input = @import("input.zig"); +const App = @import("App.zig"); +const internal_os = @import("os/main.zig"); +const inspectorpkg = @import("inspector/main.zig"); +const SurfaceMouse = @import("surface_mouse.zig"); +const ProcessInfo = @import("pty.zig").ProcessInfo; + +const log = std.log.scoped(.surface); + +// The renderer implementation to use. +const Renderer = rendererpkg.Renderer; + +/// Minimum window size in cells. This is used to prevent the window from +/// being resized to a size that is too small to be useful. These defaults +/// are chosen to match the default size of Mac's Terminal.app, but is +/// otherwise somewhat arbitrary. +pub const min_window_width_cells: u32 = 10; +pub const min_window_height_cells: u32 = 4; + +/// The maximum number of key tables that can be active at any +/// given time. `activate_key_table` calls after this are ignored. +const max_active_key_tables = 8; + +/// Unique ID used to identify this surface for IPC purposes. It is +/// exposed to the commands running in surfaces as the environment variable +/// GHOSTTY_SURFACE_ID. It must not be zero as zero is used to incicate a null +/// value when communicating an ID over DBus as DBus does not allow null/maybe +/// values. +id: u64, + +/// Allocator +alloc: Allocator, + +/// The app that this surface is attached to. +app: *App, + +/// The windowing system surface and app. +rt_app: *apprt.runtime.App, +rt_surface: *apprt.runtime.Surface, + +/// The font structures +font_grid_key: font.SharedGridSet.Key, +font_size: font.face.DesiredSize, +font_metrics: font.Metrics, + +/// This keeps track of if the font size was ever modified. If it wasn't, +/// then config reloading will change the font. If it was manually adjusted, +/// we don't change it on config reload since we assume the user wants +/// a specific size. +font_size_adjusted: bool, + +/// The renderer for this surface. +renderer: Renderer, + +/// The render state +renderer_state: rendererpkg.State, + +/// The renderer thread manager +renderer_thread: rendererpkg.Thread, + +/// The actual thread +renderer_thr: std.Thread, + +/// Mouse state. +mouse: Mouse, + +/// Keyboard input state. +keyboard: Keyboard, + +/// A currently pressed key. This is used so that we can send a keyboard +/// release event when the surface is unfocused. Note that when the surface +/// is refocused, a key press event may not be sent again -- this depends +/// on the apprt (UI framework) in use, but we want to consistently send +/// a release. +/// +/// This is only sent when a keypress event results in a key event being +/// sent to the pty. If it is consumed by a keybinding or other action, +/// this is not set. +/// +/// Also note the utf8 value is not valid for this event so some unfocused +/// release events may not send exactly the right data within Kitty keyboard +/// events. This seems unspecified in the spec so for now I'm okay with +/// this. Plus, its only for release events where the key text is far +/// less important. +pressed_key: ?input.KeyEvent = null, + +/// The hash value of the last keybinding trigger that we performed. This +/// is only set if the last key input matched a keybinding, consumed it, +/// and performed it. This is used to prevent sending release/repeat events +/// for handled bindings. +last_binding_trigger: u64 = 0, + +/// The terminal IO handler. +io: termio.Termio, +io_thread: termio.Thread, +io_thr: std.Thread, + +/// Terminal inspector +inspector: ?*inspectorpkg.Inspector = null, + +/// All our sizing information. +size: rendererpkg.Size, + +/// The configuration derived from the main config. We "derive" it so that +/// we don't have a shared pointer hanging around that we need to worry about +/// the lifetime of. This makes updating config at runtime easier. +config: DerivedConfig, + +/// The conditional state of the configuration. This can affect +/// how certain configurations take effect such as light/dark mode. +/// This is managed completely by Ghostty core but an apprt action +/// is sent whenever this changes. +config_conditional_state: configpkg.ConditionalState, + +/// This is set to true if our IO thread notifies us our child exited. +/// This is used to determine if we need to confirm, hold open, etc. +child_exited: bool = false, + +/// We maintain our focus state and assume we're focused by default. +/// If we're not initially focused then apprts can call focusCallback +/// to let us know. +focused: bool = true, + +/// Used to determine whether to continuously scroll. +selection_scroll_active: bool = false, + +/// True if the surface is in read-only mode. When read-only, no input +/// is sent to the PTY but terminal-level operations like selections, +/// (native) scrolling, and copy keybinds still work. Warn before quit is +/// always enabled in this state. +readonly: bool = false, + +/// Used to send notifications that long running commands have finished. +/// Requires that shell integration be active. Should represent a nanosecond +/// precision timestamp. It does not necessarily need to correspond to the +/// actual time, but we must be able to compare two subsequent timestamps to get +/// the wall clock time that has elapsed between timestamps. +command_timer: ?std.time.Instant = null, + +/// Search state +search: ?Search = null, + +/// Used to rate limit BEL handling. +last_bell_time: ?std.time.Instant = null, + +/// The effect of an input event. This can be used by callers to take +/// the appropriate action after an input event. For example, key +/// input can be forwarded to the OS for further processing if it +/// wasn't handled in any way by Ghostty. +pub const InputEffect = enum { + /// The input was not handled in any way by Ghostty and should be + /// forwarded to other subsystems (i.e. the OS) for further + /// processing. + ignored, + + /// The input was handled and consumed by Ghostty. + consumed, + + /// The input resulted in a close event for this surface so + /// the surface, runtime surface, etc. pointers may all be + /// unsafe to use so exit immediately. + closed, +}; + +/// The search state for the surface. +const Search = struct { + state: terminal.search.Thread, + thread: std.Thread, + + pub fn deinit(self: *Search) void { + // Notify the thread to stop + self.state.stop.notify() catch |err| log.err( + "error notifying search thread to stop, may stall err={}", + .{err}, + ); + + // Wait for the OS thread to quit + self.thread.join(); + + // Now it is safe to deinit the state + self.state.deinit(); + } +}; + +/// Mouse state for the surface. +const Mouse = struct { + /// The last tracked mouse button state by button. + click_state: [input.MouseButton.max]input.MouseButtonState = @splat(.release), + + /// The last mods state when the last mouse button (whatever it was) was + /// pressed or release. + mods: input.Mods = .{}, + + /// The point at which the left mouse click happened. This is in screen + /// coordinates so that scrolling preserves the location. + left_click_pin: ?*terminal.Pin = null, + left_click_screen: terminal.ScreenSet.Key = .primary, + + /// The starting xpos/ypos of the left click. Note that if scrolling occurs, + /// these will point to different "cells", but the xpos/ypos will stay + /// stable during scrolling relative to the surface. + left_click_xpos: f64 = 0, + left_click_ypos: f64 = 0, + + /// The count of clicks to count double and triple clicks and so on. + /// The left click time was the last time the left click was done. This + /// is always set on the first left click. + left_click_count: u8 = 0, + left_click_time: std.time.Instant = undefined, + + /// The last x/y sent for mouse reports. + event_point: ?terminal.point.Coordinate = null, + + /// The pressure stage for the mouse. This should always be none if + /// the mouse is not pressed. + pressure_stage: input.MousePressureStage = .none, + + /// Pending scroll amounts for high-precision scrolls + pending_scroll_x: f64 = 0, + pending_scroll_y: f64 = 0, + + /// True if the mouse is hidden + hidden: bool = false, + + /// True if the mouse position is currently over a link. + over_link: bool = false, + + /// The last x/y in the cursor position for links. We use this to + /// only process link hover events when the mouse actually moves cells. + link_point: ?terminal.point.Coordinate = null, +}; + +/// Keyboard state for the surface. +pub const Keyboard = struct { + /// The currently active key sequence for the surface. If this is null + /// then we're not currently in a key sequence. + sequence_set: ?*const input.Binding.Set = null, + + /// The queued keys when we're in the middle of a sequenced binding. + /// These are flushed when the sequence is completed and unconsumed or + /// invalid. + /// + /// This is naturally bounded due to the configuration maximum + /// length of a sequence. + sequence_queued: std.ArrayListUnmanaged(termio.Message.WriteReq) = .empty, + + /// The stack of tables that is currently active. The first value + /// in this is the first activated table (NOT the default keybinding set). + /// + /// This is bounded by `max_active_key_tables`. + table_stack: std.ArrayListUnmanaged(struct { + set: *const input.Binding.Set, + once: bool, + }) = .empty, + + /// The last handled binding. This is used to prevent encoding release + /// events for handled bindings. We only need to keep track of one because + /// at least at the time of writing this, its impossible for two keys of + /// a combination to be handled by different bindings before the release + /// of the prior (namely since you can't bind modifier-only). + last_trigger: ?u64 = null, +}; + +/// The configuration that a surface has, this is copied from the main +/// Config struct usually to prevent sharing a single value. +const DerivedConfig = struct { + arena: ArenaAllocator, + + /// For docs for these, see the associated config they are derived from. + original_font_size: f32, + keybind: configpkg.Keybinds, + abnormal_command_exit_runtime_ms: u32, + clipboard_read: configpkg.ClipboardAccess, + clipboard_write: configpkg.ClipboardAccess, + clipboard_trim_trailing_spaces: bool, + clipboard_paste_protection: bool, + clipboard_paste_bracketed_safe: bool, + clipboard_codepoint_map: configpkg.Config.RepeatableClipboardCodepointMap, + copy_on_select: configpkg.CopyOnSelect, + right_click_action: configpkg.RightClickAction, + confirm_close_surface: configpkg.ConfirmCloseSurface, + cursor_click_to_move: bool, + desktop_notifications: bool, + font: font.SharedGridSet.DerivedConfig, + mouse_interval: u64, + mouse_hide_while_typing: bool, + mouse_reporting: bool, + mouse_scroll_multiplier: configpkg.MouseScrollMultiplier, + mouse_shift_capture: configpkg.MouseShiftCapture, + fullscreen: configpkg.Fullscreen, + macos_non_native_fullscreen: configpkg.NonNativeFullscreen, + macos_option_as_alt: ?input.OptionAsAlt, + selection_clear_on_copy: bool, + selection_clear_on_typing: bool, + selection_word_chars: []const u21, + vt_kam_allowed: bool, + wait_after_command: bool, + window_padding_top: u32, + window_padding_bottom: u32, + window_padding_left: u32, + window_padding_right: u32, + window_padding_balance: configpkg.Config.WindowPaddingBalance, + window_height: u32, + window_width: u32, + title: ?[:0]const u8, + title_report: bool, + links: []DerivedConfig.Link, + link_previews: configpkg.LinkPreviews, + scroll_to_bottom: configpkg.Config.ScrollToBottom, + notify_on_command_finish: configpkg.Config.NotifyOnCommandFinish, + notify_on_command_finish_action: configpkg.Config.NotifyOnCommandFinishAction, + notify_on_command_finish_after: Duration, + key_remaps: input.KeyRemapSet, + + const Link = struct { + regex: oni.Regex, + action: input.Link.Action, + highlight: input.Link.Highlight, + }; + + pub fn init(alloc_gpa: Allocator, config: *const configpkg.Config) !DerivedConfig { + var arena = ArenaAllocator.init(alloc_gpa); + errdefer arena.deinit(); + const alloc = arena.allocator(); + + // Build all of our links + const links = links: { + var links: std.ArrayList(DerivedConfig.Link) = .empty; + defer links.deinit(alloc); + for (config.link.links.items) |link| { + var regex = try link.oniRegex(); + errdefer regex.deinit(); + try links.append(alloc, .{ + .regex = regex, + .action = link.action, + .highlight = link.highlight, + }); + } + + break :links try links.toOwnedSlice(alloc); + }; + errdefer { + for (links) |*link| link.regex.deinit(); + alloc.free(links); + } + + return .{ + .original_font_size = config.@"font-size", + .keybind = try config.keybind.clone(alloc), + .abnormal_command_exit_runtime_ms = config.@"abnormal-command-exit-runtime", + .clipboard_read = config.@"clipboard-read", + .clipboard_write = config.@"clipboard-write", + .clipboard_trim_trailing_spaces = config.@"clipboard-trim-trailing-spaces", + .clipboard_paste_protection = config.@"clipboard-paste-protection", + .clipboard_paste_bracketed_safe = config.@"clipboard-paste-bracketed-safe", + .clipboard_codepoint_map = try config.@"clipboard-codepoint-map".clone(alloc), + .copy_on_select = config.@"copy-on-select", + .right_click_action = config.@"right-click-action", + .confirm_close_surface = config.@"confirm-close-surface", + .cursor_click_to_move = config.@"cursor-click-to-move", + .desktop_notifications = config.@"desktop-notifications", + .font = try font.SharedGridSet.DerivedConfig.init(alloc, config), + .mouse_interval = config.@"click-repeat-interval" * 1_000_000, // 500ms + .mouse_hide_while_typing = config.@"mouse-hide-while-typing", + .mouse_reporting = config.@"mouse-reporting", + .mouse_scroll_multiplier = config.@"mouse-scroll-multiplier", + .mouse_shift_capture = config.@"mouse-shift-capture", + .fullscreen = config.fullscreen, + .macos_non_native_fullscreen = config.@"macos-non-native-fullscreen", + .macos_option_as_alt = config.@"macos-option-as-alt", + .selection_clear_on_copy = config.@"selection-clear-on-copy", + .selection_clear_on_typing = config.@"selection-clear-on-typing", + .selection_word_chars = try alloc.dupe(u21, config.@"selection-word-chars".codepoints), + .vt_kam_allowed = config.@"vt-kam-allowed", + .wait_after_command = config.@"wait-after-command", + .window_padding_top = config.@"window-padding-y".top_left, + .window_padding_bottom = config.@"window-padding-y".bottom_right, + .window_padding_left = config.@"window-padding-x".top_left, + .window_padding_right = config.@"window-padding-x".bottom_right, + .window_padding_balance = config.@"window-padding-balance", + .window_height = config.@"window-height", + .window_width = config.@"window-width", + .title = config.title, + .title_report = config.@"title-report", + .links = links, + .link_previews = config.@"link-previews", + .scroll_to_bottom = config.@"scroll-to-bottom", + .notify_on_command_finish = config.@"notify-on-command-finish", + .notify_on_command_finish_action = config.@"notify-on-command-finish-action", + .notify_on_command_finish_after = config.@"notify-on-command-finish-after", + .key_remaps = try config.@"key-remap".clone(alloc), + + // Assignments happen sequentially so we have to do this last + // so that the memory is captured from allocs above. + .arena = arena, + }; + } + + pub fn deinit(self: *DerivedConfig) void { + for (self.links) |*link| link.regex.deinit(); + self.arena.deinit(); + } + + fn scaledPadding(self: *const DerivedConfig, x_dpi: f32, y_dpi: f32) rendererpkg.Padding { + const padding_top: u32 = padding_top: { + const padding_top: f32 = @floatFromInt(self.window_padding_top); + break :padding_top @intFromFloat(@floor(padding_top * y_dpi / 72)); + }; + const padding_bottom: u32 = padding_bottom: { + const padding_bottom: f32 = @floatFromInt(self.window_padding_bottom); + break :padding_bottom @intFromFloat(@floor(padding_bottom * y_dpi / 72)); + }; + const padding_left: u32 = padding_left: { + const padding_left: f32 = @floatFromInt(self.window_padding_left); + break :padding_left @intFromFloat(@floor(padding_left * x_dpi / 72)); + }; + const padding_right: u32 = padding_right: { + const padding_right: f32 = @floatFromInt(self.window_padding_right); + break :padding_right @intFromFloat(@floor(padding_right * x_dpi / 72)); + }; + + return .{ + .top = padding_top, + .bottom = padding_bottom, + .left = padding_left, + .right = padding_right, + }; + } +}; + +/// Create a new surface. This must be called from the main thread. The +/// pointer to the memory for the surface must be provided and must be +/// stable due to interfacing with various callbacks. +pub fn init( + self: *Surface, + alloc: Allocator, + config_original: *const configpkg.Config, + app: *App, + rt_app: *apprt.runtime.App, + rt_surface: *apprt.runtime.Surface, +) !void { + // Apply our conditional state. If we fail to apply the conditional state + // then we log and attempt to move forward with the old config. + var config_: ?configpkg.Config = config_original.changeConditionalState( + app.config_conditional_state, + ) catch |err| err: { + log.warn("failed to apply conditional state to config err={}", .{err}); + break :err null; + }; + defer if (config_) |*c| c.deinit(); + + // We want a config pointer for everything so we get that either + // based on our conditional state or the original config. + const config: *const configpkg.Config = if (config_) |*c| config: { + // We want to preserve our original working directory. We + // don't need to dupe memory here because termio will derive + // it. We preserve this so directory inheritance works. + c.@"working-directory" = config_original.@"working-directory"; + break :config c; + } else config_original; + + // Get our configuration + var derived_config = try DerivedConfig.init(alloc, config); + errdefer derived_config.deinit(); + + // Initialize our renderer with our initialized surface. + try Renderer.surfaceInit(rt_surface); + + // Determine our DPI configurations so we can properly configure + // font points to pixels and handle other high-DPI scaling factors. + const content_scale = try rt_surface.getContentScale(); + const x_dpi = content_scale.x * font.face.default_dpi; + const y_dpi = content_scale.y * font.face.default_dpi; + log.debug("xscale={} yscale={} xdpi={} ydpi={}", .{ + content_scale.x, + content_scale.y, + x_dpi, + y_dpi, + }); + + // The font size we desire along with the DPI determined for the surface + const font_size: font.face.DesiredSize = .{ + .points = config.@"font-size", + .xdpi = @intFromFloat(x_dpi), + .ydpi = @intFromFloat(y_dpi), + }; + + // Setup our font group. This will reuse an existing font group if + // it was already loaded. + const font_grid_key, const font_grid = try app.font_grid_set.ref( + &derived_config.font, + font_size, + ); + + // Build our size struct which has all the sizes we need. + const size: rendererpkg.Size = size: { + var size: rendererpkg.Size = .{ + .screen = screen: { + const surface_size = try rt_surface.getSize(); + break :screen .{ + .width = surface_size.width, + .height = surface_size.height, + }; + }, + + .cell = font_grid.cellSize(), + .padding = .{}, + }; + + const explicit: rendererpkg.Padding = derived_config.scaledPadding( + x_dpi, + y_dpi, + ); + if (derived_config.window_padding_balance != .false) { + size.balancePadding(explicit, derived_config.window_padding_balance); + } else { + size.padding = explicit; + } + + break :size size; + }; + + // Create our terminal grid with the initial size + const app_mailbox: App.Mailbox = .{ .rt_app = rt_app, .mailbox = &app.mailbox }; + var renderer_impl = try Renderer.init(alloc, .{ + .config = try .init(alloc, config), + .font_grid = font_grid, + .size = size, + .surface_mailbox = .{ .surface = self, .app = app_mailbox }, + .rt_surface = rt_surface, + .thread = &self.renderer_thread, + }); + errdefer renderer_impl.deinit(); + + // The mutex used to protect our renderer state. + const mutex = try alloc.create(std.Thread.Mutex); + mutex.* = .{}; + errdefer alloc.destroy(mutex); + + // Create the renderer thread + var render_thread = try rendererpkg.Thread.init( + alloc, + config, + rt_surface, + &self.renderer, + &self.renderer_state, + app_mailbox, + ); + errdefer render_thread.deinit(); + + // Create the IO thread + var io_thread = try termio.Thread.init(alloc); + errdefer io_thread.deinit(); + + self.* = .{ + .id = id: { + while (true) { + const candidate = std.crypto.random.int(u64); + if (candidate == 0) continue; + break :id candidate; + } + }, + .alloc = alloc, + .app = app, + .rt_app = rt_app, + .rt_surface = rt_surface, + .font_grid_key = font_grid_key, + .font_size = font_size, + .font_size_adjusted = false, + .font_metrics = font_grid.metrics, + .renderer = renderer_impl, + .renderer_thread = render_thread, + .renderer_state = .{ + .mutex = mutex, + .terminal = &self.io.terminal, + }, + .renderer_thr = undefined, + .mouse = .{}, + .keyboard = .{}, + .io = undefined, + .io_thread = io_thread, + .io_thr = undefined, + .size = size, + .config = derived_config, + + // Our conditional state is initialized to the app state. This + // lets us get the most likely correct color theme and so on. + .config_conditional_state = app.config_conditional_state, + }; + + // The command we're going to execute + const command: ?configpkg.Command = command: { + if (app.first) { + if (config.@"initial-command") |command| { + break :command command; + } + } + break :command config.command; + }; + + // Start our IO implementation + // This separate block ({}) is important because our errdefers must + // be scoped here to be valid. + { + var env = rt_surface.defaultTermioEnv() catch |err| env: { + // If an error occurs, we don't want to block surface startup. + log.warn("error getting env map for surface err={}", .{err}); + break :env internal_os.getEnvMap(alloc) catch + std.process.EnvMap.init(alloc); + }; + errdefer env.deinit(); + + // don't leak GHOSTTY_LOG to any subprocesses + env.remove("GHOSTTY_LOG"); + + var buf: [18]u8 = undefined; + try env.put( + "GHOSTTY_SURFACE_ID", + std.fmt.bufPrint(&buf, "0x{x:0>16}", .{self.id}) catch unreachable, + ); + + // Initialize our IO backend + var io_exec = try termio.Exec.init(alloc, .{ + .command = command, + .env = env, + .env_override = config.env, + .shell_integration = config.@"shell-integration", + .shell_integration_features = config.@"shell-integration-features", + .cursor_blink = config.@"cursor-style-blink", + .working_directory = if (config.@"working-directory") |wd| wd.value() else null, + .resources_dir = global_state.resources_dir.host(), + .term = config.term, + .rt_pre_exec_info = .init(config), + .rt_post_fork_info = .init(config), + }); + errdefer io_exec.deinit(); + + // Initialize our IO mailbox + var io_mailbox = try termio.Mailbox.initSPSC(alloc); + errdefer io_mailbox.deinit(alloc); + + try termio.Termio.init(&self.io, alloc, .{ + .size = size, + .full_config = config, + .config = try termio.Termio.DerivedConfig.init(alloc, config), + .backend = .{ .exec = io_exec }, + .mailbox = io_mailbox, + .renderer_state = &self.renderer_state, + .renderer_wakeup = render_thread.wakeup, + .renderer_mailbox = render_thread.mailbox, + .surface_mailbox = .{ .surface = self, .app = app_mailbox }, + }); + } + // Outside the block, IO has now taken ownership of our temporary state + // so we can just defer this and not the subcomponents. + errdefer self.io.deinit(); + + // Report initial cell size on surface creation + _ = try rt_app.performAction( + .{ .surface = self }, + .cell_size, + .{ .width = size.cell.width, .height = size.cell.height }, + ); + + _ = try rt_app.performAction( + .{ .surface = self }, + .size_limit, + .{ + .min_width = size.cell.width * min_window_width_cells, + .min_height = size.cell.height * min_window_height_cells, + // No max: + .max_width = 0, + .max_height = 0, + }, + ); + + // Call our size callback which handles all our retina setup + // Note: this shouldn't be necessary and when we clean up the surface + // init stuff we should get rid of this. But this is required because + // sizeCallback does retina-aware stuff we don't do here and don't want + // to duplicate. + try self.resize(self.size.screen); + + // Give the renderer one more opportunity to finalize any surface + // setup on the main thread prior to spinning up the rendering thread. + try renderer_impl.finalizeSurfaceInit(rt_surface); + + // Start our renderer thread + self.renderer_thr = try std.Thread.spawn( + .{}, + rendererpkg.Thread.threadMain, + .{&self.renderer_thread}, + ); + self.renderer_thr.setName("renderer") catch {}; + + // Start our IO thread + self.io_thr = try std.Thread.spawn( + .{}, + termio.Thread.threadMain, + .{ &self.io_thread, &self.io }, + ); + self.io_thr.setName("io") catch {}; + + // Determine our initial window size if configured. We need to do this + // quite late in the process because our height/width are in grid dimensions, + // so we need to know our cell sizes first. + // + // Note: it is important to do this after the renderer is setup above. + // This allows the apprt to fully initialize the surface before we + // start messing with the window. + self.recomputeInitialSize() catch |err| { + // We don't treat this as a fatal error because not setting + // an initial size shouldn't stop our terminal from working. + log.warn("unable to set initial window size: {}", .{err}); + }; + + if (config.title) |title| { + _ = try rt_app.performAction( + .{ .surface = self }, + .set_title, + .{ .title = title }, + ); + } else if ((comptime builtin.os.tag == .linux) and + config.@"_xdg-terminal-exec") + xdg: { + // For xdg-terminal-exec execution we special-case and set the window + // title to the command being executed. This allows window managers + // to set custom styling based on the command being executed. + const v = command orelse break :xdg; + const title = v.string(alloc) catch |err| { + log.warn( + "error copying command for title, title will not be set err={}", + .{err}, + ); + break :xdg; + }; + defer alloc.free(title); + _ = try rt_app.performAction( + .{ .surface = self }, + .set_title, + .{ .title = title }, + ); + } else if (command) |cmd| switch (cmd) { + // If a user specifies a command it is appropriate to set the title as argv[0] + // we know in the case of a direct command it has been supplied by the user + .direct => |cmd_str| if (cmd_str.len != 0) { + _ = try rt_app.performAction( + .{ .surface = self }, + .set_title, + .{ .title = cmd_str[0] }, + ); + }, + + // We won't set the title in the case the shell expands the command + // as that should typically be used to launch a shell which should + // set its own titles + .shell => {}, + }; + + // We are no longer the first surface + app.first = false; +} + +pub fn deinit(self: *Surface) void { + // Stop search thread + if (self.search) |*s| s.deinit(); + + // Stop rendering thread + { + self.renderer_thread.stop.notify() catch |err| + log.err("error notifying renderer thread to stop, may stall err={}", .{err}); + self.renderer_thr.join(); + + // We need to become the active rendering thread again + self.renderer.threadEnter(self.rt_surface) catch unreachable; + } + + // Stop our IO thread + { + self.io_thread.stop.notify() catch |err| + log.err("error notifying io thread to stop, may stall err={}", .{err}); + self.io_thr.join(); + } + + // We need to deinit AFTER everything is stopped, since there are + // shared values between the two threads. + self.renderer_thread.deinit(); + self.renderer.deinit(); + self.io_thread.deinit(); + self.io.deinit(); + + if (self.inspector) |v| { + v.deinit(self.alloc); + self.alloc.destroy(v); + } + + // Clean up our keyboard state + for (self.keyboard.sequence_queued.items) |req| req.deinit(); + self.keyboard.sequence_queued.deinit(self.alloc); + self.keyboard.table_stack.deinit(self.alloc); + + // Clean up our font grid + self.app.font_grid_set.deref(self.font_grid_key); + + // Clean up our render state + if (self.renderer_state.preedit) |p| self.alloc.free(p.codepoints); + self.alloc.destroy(self.renderer_state.mutex); + self.config.deinit(); + + log.info("surface closed addr={x}", .{@intFromPtr(self)}); +} + +/// Close this surface. This will trigger the runtime to start the +/// close process, which should ultimately deinitialize this surface. +pub fn close(self: *Surface) void { + self.rt_surface.close(self.needsConfirmQuit()); +} + +/// Returns a mailbox that can be used to send messages to this surface. +inline fn surfaceMailbox(self: *Surface) Mailbox { + return .{ + .surface = self, + .app = .{ .rt_app = self.rt_app, .mailbox = &self.app.mailbox }, + }; +} + +/// Queue a message for the IO thread. +/// +/// We centralize all our logic into this spot so we can intercept +/// messages for example in readonly mode. +fn queueIo( + self: *Surface, + msg: termio.Message, + mutex: termio.Termio.MutexState, +) void { + // In readonly mode, we don't allow any writes through to the pty. + if (self.readonly) { + switch (msg) { + .write_small, + .write_stable, + .write_alloc, + => return, + + else => {}, + } + } + + self.io.queueMessage(msg, mutex); +} + +/// Forces the surface to render. This is useful for when the surface +/// is in the middle of animation (such as a resize, etc.) or when +/// the render timer is managed manually by the apprt. +pub fn draw(self: *Surface) !void { + // Renderers are required to support `drawFrame` being called from + // the main thread, so that they can update contents during resize. + try self.renderer.drawFrame(true); +} + +/// Activate the inspector. This will begin collecting inspection data. +/// This will not affect the GUI. The GUI must use performAction to +/// show/hide the inspector UI. +pub fn activateInspector(self: *Surface) !void { + if (self.inspector != null) return; + + // Setup the inspector + const ptr = try self.alloc.create(inspectorpkg.Inspector); + errdefer self.alloc.destroy(ptr); + ptr.* = try inspectorpkg.Inspector.init(self.alloc); + errdefer ptr.deinit(self.alloc); + self.inspector = ptr; + errdefer self.inspector = null; + + // Put the inspector onto the render state + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + assert(self.renderer_state.inspector == null); + self.renderer_state.inspector = self.inspector; + } + + // Notify our components we have an inspector active + _ = self.renderer_thread.mailbox.push(.{ .inspector = true }, .{ .forever = {} }); + self.queueIo(.{ .inspector = true }, .unlocked); +} + +/// Deactivate the inspector and stop collecting any information. +pub fn deactivateInspector(self: *Surface) void { + const insp = self.inspector orelse return; + + // Remove the inspector from the render state + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + assert(self.renderer_state.inspector != null); + self.renderer_state.inspector = null; + } + + // Notify our components we have deactivated inspector + _ = self.renderer_thread.mailbox.push(.{ .inspector = false }, .{ .forever = {} }); + self.queueIo(.{ .inspector = false }, .unlocked); + + // Deinit the inspector + insp.deinit(self.alloc); + self.alloc.destroy(insp); + self.inspector = null; +} + +/// True if the surface requires confirmation to quit. This should be called +/// by apprt to determine if the surface should confirm before quitting. +pub fn needsConfirmQuit(self: *Surface) bool { + // If the surface is in read-only mode, always require confirmation + if (self.readonly) return true; + + // If the child has exited, then our process is certainly not alive. + // We check this first to avoid the locking overhead below. + if (self.child_exited) return false; + + // Check the configuration for confirming close behavior. + return switch (self.config.confirm_close_surface) { + .always => true, + .false => false, + .true => true: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + break :true !self.io.terminal.cursorIsAtPrompt(); + }, + }; +} + +/// Called from the app thread to handle mailbox messages to our specific +/// surface. +pub fn handleMessage(self: *Surface, msg: Message) !void { + switch (msg) { + .change_config => |config| try self.updateConfig(config), + + .set_title => |*v| { + // We ignore the message in case the title was set via config. + if (self.config.title != null) { + log.debug("ignoring title change request since static title is set via config", .{}); + return; + } + + // The ptrCast just gets sliceTo to return the proper type. + // We know that our title should end in 0. + const slice = std.mem.sliceTo(@as([*:0]const u8, @ptrCast(v)), 0); + log.debug("changing title \"{s}\"", .{slice}); + _ = try self.rt_app.performAction( + .{ .surface = self }, + .set_title, + .{ .title = slice }, + ); + }, + + .report_title => |style| report_title: { + if (!self.config.title_report) { + log.info("report_title requested, but disabled via config", .{}); + break :report_title; + } + + const title: ?[:0]const u8 = self.rt_surface.getTitle(); + const data = switch (style) { + .csi_21_t => try std.fmt.allocPrint( + self.alloc, + "\x1b]l{s}\x1b\\", + .{title orelse ""}, + ), + }; + + // We always use an allocating message because we don't know + // the length of the title and this isn't a performance critical + // path. + self.queueIo(.{ + .write_alloc = .{ + .alloc = self.alloc, + .data = data, + }, + }, .unlocked); + }, + + .color_change => |change| color_change: { + // Notify our apprt, but don't send a mode 2031 DSR report + // because VT sequences were used to change the color. + _ = try self.rt_app.performAction( + .{ .surface = self }, + .color_change, + .{ + .kind = switch (change.target) { + .palette => |v| @enumFromInt(v), + .dynamic => |dyn| switch (dyn) { + .foreground => .foreground, + .background => .background, + .cursor => .cursor, + // Unsupported dynamic color change notification type + else => break :color_change, + }, + // Special colors aren't supported for change notification + .special => break :color_change, + }, + .r = change.color.r, + .g = change.color.g, + .b = change.color.b, + }, + ); + }, + + .set_mouse_shape => |shape| { + log.debug("changing mouse shape: {}", .{shape}); + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_shape, + shape, + ); + }, + + .clipboard_read => |clipboard| { + if (self.config.clipboard_read == .deny) { + log.info("application attempted to read clipboard, but 'clipboard-read' is set to deny", .{}); + return; + } + + _ = try self.startClipboardRequest(.standard, .{ .osc_52_read = clipboard }); + }, + + .clipboard_write => |w| switch (w.req) { + .small => |v| try self.clipboardWrite(v.data[0..v.len], w.clipboard_type), + .stable => |v| try self.clipboardWrite(v, w.clipboard_type), + .alloc => |v| { + defer v.alloc.free(v.data); + try self.clipboardWrite(v.data, w.clipboard_type); + }, + }, + + .pwd_change => |w| { + defer w.deinit(); + + // We always allocate for this because we need to null-terminate. + const str = try self.alloc.dupeZ(u8, w.slice()); + defer self.alloc.free(str); + + _ = try self.rt_app.performAction( + .{ .surface = self }, + .pwd, + .{ .pwd = str }, + ); + }, + + .close => self.close(), + + .child_exited => |v| self.childExited(v), + + .desktop_notification => |notification| { + if (!self.config.desktop_notifications) { + log.info("application attempted to display a desktop notification, but 'desktop-notifications' is disabled", .{}); + return; + } + + const title = std.mem.sliceTo(¬ification.title, 0); + const body = std.mem.sliceTo(¬ification.body, 0); + try self.showDesktopNotification(title, body); + }, + + .renderer_health => |health| self.updateRendererHealth(health), + + .scrollbar => |scrollbar| self.updateScrollbar(scrollbar), + + .present_surface => try self.presentSurface(), + + .password_input => |v| try self.passwordInput(v), + + .ring_bell => bell: { + const now = std.time.Instant.now() catch unreachable; + if (self.last_bell_time) |last| { + if (now.since(last) < 100 * std.time.ns_per_ms) break :bell; + } + self.last_bell_time = now; + _ = self.rt_app.performAction( + .{ .surface = self }, + .ring_bell, + {}, + ) catch |err| { + log.warn("apprt failed to ring bell={}", .{err}); + }; + }, + + .progress_report => |v| { + _ = self.rt_app.performAction( + .{ .surface = self }, + .progress_report, + v, + ) catch |err| { + log.warn("apprt failed to report progress err={}", .{err}); + }; + }, + + .selection_scroll_tick => |active| { + self.selection_scroll_active = active; + try self.selectionScrollTick(); + }, + + .start_command => { + self.command_timer = try .now(); + }, + + .stop_command => |v| timer: { + const end: std.time.Instant = try .now(); + const start = self.command_timer orelse break :timer; + self.command_timer = null; + + const duration: Duration = .{ .duration = end.since(start) }; + log.debug("command took {f}", .{duration}); + + _ = self.rt_app.performAction( + .{ .surface = self }, + .command_finished, + .{ + .exit_code = v, + .duration = duration, + }, + ) catch |err| { + log.warn("apprt failed to notify command finish={}", .{err}); + }; + }, + + .search_total => |v| { + _ = try self.rt_app.performAction( + .{ .surface = self }, + .search_total, + .{ .total = v }, + ); + }, + + .search_selected => |v| { + _ = try self.rt_app.performAction( + .{ .surface = self }, + .search_selected, + .{ .selected = v }, + ); + }, + } +} + +fn selectionScrollTick(self: *Surface) !void { + // If we're no longer active then we don't do anything. + if (!self.selection_scroll_active) return; + + // If we don't have a left mouse button down then we + // don't do anything. + if (self.mouse.left_click_count == 0) return; + + const pos = try self.rt_surface.getCursorPos(); + const pos_vp = self.posToViewport(pos.x, pos.y); + const delta: isize = if (pos.y < 0) -1 else 1; + + // We need our locked state for the remainder + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t: *terminal.Terminal = self.renderer_state.terminal; + + // If our screen changed while this is happening, we stop our + // selection scroll. + if (self.mouse.left_click_screen != t.screens.active_key) { + self.queueIo( + .{ .selection_scroll = false }, + .locked, + ); + return; + } + + // Scroll the viewport as required + t.scrollViewport(.{ .delta = delta }); + + // Next, trigger our drag behavior + const pin = t.screens.active.pages.pin(.{ + .viewport = .{ + .x = pos_vp.x, + .y = pos_vp.y, + }, + }) orelse { + if (comptime std.debug.runtime_safety) unreachable; + return; + }; + try self.dragLeftClickSingle(pin, pos.x); + + // We modified our viewport and selection so we need to queue + // a render. + try self.queueRender(); +} + +fn childExited(self: *Surface, info: apprt.surface.Message.ChildExited) void { + // Mark our flag that we exited immediately + self.child_exited = true; + + // If our runtime was below some threshold then we assume that this + // was an abnormal exit and we show an error message. + if (info.runtime_ms <= self.config.abnormal_command_exit_runtime_ms) runtime: { + // On macOS, our exit code detection doesn't work, possibly + // because of our `login` wrapper. More investigation required. + if (comptime !builtin.target.os.tag.isDarwin()) { + // If the exit code is 0 then it was a good exit. + if (info.exit_code == 0) break :runtime; + } + + log.warn("abnormal process exit detected, showing error message", .{}); + + // Try and show a GUI message. If it returns true, don't do anything else. + if (self.rt_app.performAction( + .{ .surface = self }, + .show_child_exited, + info, + ) catch |err| gui: { + log.err("error trying to show native child exited GUI err={}", .{err}); + break :gui false; + }) return; + + // If a native GUI notification was not shown, update our terminal to + // note the abnormal exit. + self.childExitedAbnormally(info) catch |err| { + log.err("error handling abnormal child exit err={}", .{err}); + return; + }; + + return; + } + + // We output a message so that the user knows what's going on and + // doesn't think their terminal just froze. We show this unconditionally + // on close even if `wait_after_command` is false and the surface closes + // immediately because if a user does an `undo` to restore a closed + // surface then they will see this message and know the process has + // completed. + terminal: { + // First try and show a native GUI message. + if (self.rt_app.performAction( + .{ .surface = self }, + .show_child_exited, + info, + ) catch |err| gui: { + log.err("error trying to show native child exited GUI err={}", .{err}); + break :gui false; + }) break :terminal; + + // If the native GUI can't be shown, display a text message in the + // terminal. + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t: *terminal.Terminal = self.renderer_state.terminal; + t.carriageReturn(); + t.linefeed() catch break :terminal; + t.printString("Process exited. Press any key to close the terminal.") catch + break :terminal; + t.modes.set(.cursor_visible, false); + + // We also want to ensure that normal keyboard encoding is on + // so that we can close the terminal. We close the terminal on + // any key press that encodes a character. + t.modes.set(.disable_keyboard, false); + t.screens.active.kitty_keyboard.set(.set, .disabled); + } + + // Waiting after command we stop here. The terminal is updated, our + // state is updated, and now its up to the user to decide what to do. + if (self.config.wait_after_command) return; + + // If we aren't waiting after the command, then we exit immediately + // with no confirmation. + self.close(); +} + +/// Called when the child process exited abnormally. +fn childExitedAbnormally( + self: *Surface, + info: apprt.surface.Message.ChildExited, +) !void { + var arena = ArenaAllocator.init(self.alloc); + defer arena.deinit(); + const alloc = arena.allocator(); + + // Build up our command for the error message + const command = try std.mem.join(alloc, " ", switch (self.io.backend) { + .exec => |*exec| exec.subprocess.args, + }); + const runtime_str = try std.fmt.allocPrint(alloc, "{d} ms", .{info.runtime_ms}); + + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t: *terminal.Terminal = self.renderer_state.terminal; + + // No matter what move the cursor back to the column 0. + t.carriageReturn(); + + // Reset styles + try t.setAttribute(.{ .unset = {} }); + + // If there is data in the viewport, we want to scroll down + // a little bit and write a horizontal rule before writing + // our message. This lets the use see the error message the + // command may have output. + const viewport_str = try t.plainString(alloc); + if (viewport_str.len > 0) { + try t.linefeed(); + for (0..t.cols) |_| try t.print(0x2501); + t.carriageReturn(); + try t.linefeed(); + try t.linefeed(); + } + + // Output our error message + try t.setAttribute(.{ .@"8_fg" = .bright_red }); + try t.setAttribute(.{ .bold = {} }); + try t.printString("Ghostty failed to launch the requested command:"); + try t.setAttribute(.{ .unset = {} }); + + t.carriageReturn(); + try t.linefeed(); + try t.linefeed(); + try t.printString(command); + try t.setAttribute(.{ .unset = {} }); + + t.carriageReturn(); + try t.linefeed(); + try t.linefeed(); + try t.printString("Runtime: "); + try t.setAttribute(.{ .@"8_fg" = .red }); + try t.printString(runtime_str); + try t.setAttribute(.{ .unset = {} }); + + // We don't print this on macOS because the exit code is always 0 + // due to the way we launch the process. + if (comptime !builtin.target.os.tag.isDarwin()) { + const exit_code_str = try std.fmt.allocPrint(alloc, "{d}", .{info.exit_code}); + t.carriageReturn(); + try t.linefeed(); + try t.printString("Exit Code: "); + try t.setAttribute(.{ .@"8_fg" = .red }); + try t.printString(exit_code_str); + try t.setAttribute(.{ .unset = {} }); + } + + t.carriageReturn(); + try t.linefeed(); + try t.linefeed(); + try t.printString("Press any key to close the window."); + + // Hide the cursor + t.modes.set(.cursor_visible, false); +} + +/// Called when the terminal detects there is a password input prompt. +fn passwordInput(self: *Surface, v: bool) !void { + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // If our password input state is unchanged then we don't + // waste time doing anything more. + const old = self.io.terminal.flags.password_input; + if (old == v) return; + + self.io.terminal.flags.password_input = v; + } + + // Notify our apprt so it can do whatever it wants. + _ = self.rt_app.performAction( + .{ .surface = self }, + .secure_input, + if (v) .on else .off, + ) catch |err| { + // We ignore this error because we don't want to fail this + // entire operation just because the apprt failed to set + // the secure input state. + log.warn("apprt failed to set secure input state err={}", .{err}); + }; + + try self.queueRender(); +} + +fn searchCallback(event: terminal.search.Thread.Event, ud: ?*anyopaque) void { + // IMPORTANT: This function is run on the SEARCH THREAD! It is NOT SAFE + // to access anything other than values that never change on the surface. + // The surface is guaranteed to be valid for the lifetime of the search + // thread. + const self: *Surface = @ptrCast(@alignCast(ud.?)); + self.searchCallback_(event) catch |err| { + log.warn("error in search callback err={}", .{err}); + }; +} + +fn searchCallback_( + self: *Surface, + event: terminal.search.Thread.Event, +) !void { + // NOTE: This runs on the search thread. + + switch (event) { + .viewport_matches => |matches_unowned| { + var arena: ArenaAllocator = .init(self.alloc); + errdefer arena.deinit(); + const alloc = arena.allocator(); + + const matches = try alloc.dupe(terminal.highlight.Flattened, matches_unowned); + for (matches) |*m| m.* = try m.clone(alloc); + + _ = self.renderer_thread.mailbox.push( + .{ .search_viewport_matches = .{ + .arena = arena, + .matches = matches, + } }, + .forever, + ); + try self.renderer_thread.wakeup.notify(); + }, + + .selected_match => |selected_| { + if (selected_) |sel| { + // Copy the flattened match. + var arena: ArenaAllocator = .init(self.alloc); + errdefer arena.deinit(); + const alloc = arena.allocator(); + const match = try sel.highlight.clone(alloc); + + _ = self.renderer_thread.mailbox.push( + .{ .search_selected_match = .{ + .arena = arena, + .match = match, + } }, + .forever, + ); + + // Send the selected index to the surface mailbox + _ = self.surfaceMailbox().push( + .{ .search_selected = sel.idx }, + .forever, + ); + } else { + // Reset our selected match + _ = self.renderer_thread.mailbox.push( + .{ .search_selected_match = null }, + .forever, + ); + + // Reset the selected index + _ = self.surfaceMailbox().push( + .{ .search_selected = null }, + .forever, + ); + } + + try self.renderer_thread.wakeup.notify(); + }, + + .total_matches => |total| { + _ = self.surfaceMailbox().push( + .{ .search_total = total }, + .forever, + ); + }, + + // When we quit, tell our renderer to reset any search state. + .quit => { + _ = self.renderer_thread.mailbox.push( + .{ .search_selected_match = null }, + .forever, + ); + _ = self.renderer_thread.mailbox.push( + .{ .search_viewport_matches = .{ + .arena = .init(self.alloc), + .matches = &.{}, + } }, + .forever, + ); + try self.renderer_thread.wakeup.notify(); + + // Reset search totals in the surface + _ = self.surfaceMailbox().push( + .{ .search_total = null }, + .forever, + ); + _ = self.surfaceMailbox().push( + .{ .search_selected = null }, + .forever, + ); + }, + + // Unhandled, so far. + .complete => {}, + } +} + +/// Call this when modifiers change. This is safe to call even if modifiers +/// match the previous state. +/// +/// This is not publicly exported because modifier changes happen implicitly +/// on mouse callbacks, key callbacks, etc. +/// +/// The renderer state mutex MUST NOT be held. +fn modsChanged(self: *Surface, mods: input.Mods) void { + // The only place we keep track of mods currently is on the mouse. + if (!self.mouse.mods.equal(mods)) { + // The mouse mods only contain binding modifiers since we don't + // want caps/num lock or sided modifiers to affect the mouse. + self.mouse.mods = mods.binding(); + + // We also need to update the renderer so it knows if it should + // highlight links. Additionally, mark the screen as dirty so + // that the highlight state of all links is properly updated. + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + self.renderer_state.mouse.mods = self.mouseModsWithCapture(self.mouse.mods); + + // We use the clear screen dirty flag to force a rebuild of all + // rows because changing mouse mods can affect the highlight state + // of a link. If there is no link this seems very wasteful but + // its really only one frame so it's not so bad. + self.renderer_state.terminal.flags.dirty.clear = true; + } + + self.queueRender() catch |err| { + // Not a big deal if this fails. + log.warn("failed to notify renderer of mods change err={}", .{err}); + }; + } +} + +/// Call this whenever the mouse moves or mods changed. The time +/// at which this is called may matter for the correctness of other +/// mouse events (see cursorPosCallback) but this is shared logic +/// for multiple events. +fn mouseRefreshLinks( + self: *Surface, + pos: apprt.CursorPos, + pos_vp: terminal.point.Coordinate, + over_link: bool, +) !void { + // If the position is outside our viewport, do nothing + if (pos.x < 0 or pos.y < 0) return; + + // Update the last point that we checked for links so we don't + // recheck if the mouse moves some pixels to the same point. + self.mouse.link_point = pos_vp; + + // We use an arena for everything below to make things easy to clean up. + // In the case we don't do any allocs this is very cheap to setup + // (effectively just struct init). + var arena = ArenaAllocator.init(self.alloc); + defer arena.deinit(); + const alloc = arena.allocator(); + + // Get our link at the current position. This returns null if there + // isn't a link OR if we shouldn't be showing links for some reason + // (see further comments for cases). + const link_: ?apprt.action.MouseOverLink, const preview: bool = link: { + // If we clicked and our mouse moved cells then we never + // highlight links until the mouse is unclicked. This follows + // standard macOS and Linux behavior where a click and drag cancels + // mouse actions. + const left_idx = @intFromEnum(input.MouseButton.left); + if (self.mouse.click_state[left_idx] == .press) click: { + const pin = self.mouse.left_click_pin orelse break :click; + const click_pt = self.io.terminal.screens.active.pages.pointFromPin( + .viewport, + pin.*, + ) orelse break :click; + + if (!click_pt.coord().eql(pos_vp)) { + log.debug("mouse moved while left click held, ignoring link hover", .{}); + break :link .{ null, false }; + } + } + + const link = (try self.linkAtPos(pos)) orelse break :link .{ null, false }; + switch (link.action) { + .open => { + const str = try self.io.terminal.screens.active.selectionString(alloc, .{ + .sel = link.selection, + .trim = false, + }); + break :link .{ + .{ .url = str }, + self.config.link_previews == .true, + }; + }, + + ._open_osc8 => { + // Show the URL in the status bar + const pin = link.selection.start(); + const uri = self.osc8URI(pin) orelse { + log.warn("failed to get URI for OSC8 hyperlink", .{}); + break :link .{ null, false }; + }; + break :link .{ + .{ .url = try alloc.dupeZ(u8, uri) }, + self.config.link_previews != .false, + }; + }, + } + }; + + // If we found a link, setup our internal state and notify the + // apprt so it can highlight it. + if (link_) |link| { + self.renderer_state.mouse.point = pos_vp; + self.mouse.over_link = true; + self.renderer_state.terminal.screens.active.dirty.hyperlink_hover = true; + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_shape, + .pointer, + ); + + if (preview) { + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_over_link, + link, + ); + } + + try self.queueRender(); + return; + } + + // No link, if we're previously over a link then we need to clear + // the over-link apprt state. + if (over_link) { + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_shape, + self.io.terminal.mouse_shape, + ); + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_over_link, + .{ .url = "" }, + ); + try self.queueRender(); + return; + } +} + +/// Called when our renderer health state changes. +fn updateRendererHealth(self: *Surface, health: rendererpkg.Health) void { + log.warn("renderer health status change status={}", .{health}); + _ = self.rt_app.performAction( + .{ .surface = self }, + .renderer_health, + health, + ) catch |err| { + log.warn("failed to notify app of renderer health change err={}", .{err}); + }; +} + +/// Called when the scrollbar state changes. +fn updateScrollbar(self: *Surface, scrollbar: terminal.Scrollbar) void { + _ = self.rt_app.performAction( + .{ .surface = self }, + .scrollbar, + scrollbar, + ) catch |err| { + log.warn("failed to notify app of scrollbar change err={}", .{err}); + }; +} + +/// This should be called anytime `config_conditional_state` changes +/// so that the apprt can reload the configuration. +fn notifyConfigConditionalState(self: *Surface) void { + _ = self.rt_app.performAction( + .{ .surface = self }, + .reload_config, + .{ .soft = true }, + ) catch |err| { + log.warn("failed to notify app of config state change err={}", .{err}); + }; +} + +/// Update our configuration at runtime. This can be called by the apprt +/// to set a surface-specific configuration that differs from the app +/// or other surfaces. +pub fn updateConfig( + self: *Surface, + original: *const configpkg.Config, +) !void { + // Apply our conditional state. If we fail to apply the conditional state + // then we log and attempt to move forward with the old config. + var config_: ?configpkg.Config = original.changeConditionalState( + self.config_conditional_state, + ) catch |err| err: { + log.warn("failed to apply conditional state to config err={}", .{err}); + break :err null; + }; + defer if (config_) |*c| c.deinit(); + + // We want a config pointer for everything so we get that either + // based on our conditional state or the original config. + const config: *const configpkg.Config = if (config_) |*c| c else original; + + // Update our new derived config immediately + const derived = DerivedConfig.init(self.alloc, config) catch |err| { + // If the derivation fails then we just log and return. We don't + // hard fail in this case because we don't want to error the surface + // when config fails we just want to keep using the old config. + log.err("error updating configuration err={}", .{err}); + return; + }; + self.config.deinit(); + self.config = derived; + + // If our mouse is hidden but we disabled mouse hiding, then show it again. + if (!self.config.mouse_hide_while_typing and self.mouse.hidden) { + self.showMouse(); + } + + // If we are in the middle of a key sequence, clear it. + self.endKeySequence(.drop, .free); + + // Deactivate all key tables since they may have changed. Importantly, + // we store pointers into the config as part of our table stack so + // we can't keep them active across config changes. But this behavior + // also matches key sequences. + _ = self.deactivateAllKeyTables() catch |err| { + log.warn("failed to deactivate key tables err={}", .{err}); + }; + + // Before sending any other config changes, we give the renderer a new font + // grid. We could check to see if there was an actual change to the font, + // but this is easier and pretty rare so it's not a performance concern. + // + // (Calling setFontSize builds and sends a new font grid to the renderer.) + try self.setFontSize(font_size: { + // If we have manually adjusted the font size, keep it that way. + if (self.font_size_adjusted) { + log.info("font size manually adjusted, preserving previous size on config reload", .{}); + break :font_size self.font_size; + } + + // If we haven't, then we update to the configured font size. + // This allows config changes to update the font size. We used to + // never do this but it was a common source of confusion and people + // assumed that Ghostty was broken! This logic makes more sense. + var size = self.font_size; + size.points = std.math.clamp(config.@"font-size", 1.0, 255.0); + break :font_size size; + }); + + // We need to store our configs in a heap-allocated pointer so that + // our messages aren't huge. + var renderer_message = try rendererpkg.Message.initChangeConfig(self.alloc, config); + errdefer renderer_message.deinit(); + var termio_config_ptr = try self.alloc.create(termio.Termio.DerivedConfig); + errdefer self.alloc.destroy(termio_config_ptr); + termio_config_ptr.* = try termio.Termio.DerivedConfig.init(self.alloc, config); + errdefer termio_config_ptr.deinit(); + + _ = self.renderer_thread.mailbox.push(renderer_message, .{ .forever = {} }); + self.queueIo(.{ + .change_config = .{ + .alloc = self.alloc, + .ptr = termio_config_ptr, + }, + }, .unlocked); + + // With mailbox messages sent, we have to wake them up so they process it. + self.queueRender() catch |err| { + log.warn("failed to notify renderer of config change err={}", .{err}); + }; + + // If we have a title set then we update our window to have the + // newly configured title. + if (config.title) |title| _ = try self.rt_app.performAction( + .{ .surface = self }, + .set_title, + .{ .title = title }, + ); + + // Notify the window + _ = try self.rt_app.performAction( + .{ .surface = self }, + .config_change, + .{ .config = config }, + ); +} + +const InitialSizeError = error{ + ContentScaleUnavailable, + AppActionFailed, +}; + +/// Recalculate the initial size of the window based on the +/// configuration and invoke the apprt `initial_size` action if +/// necessary. +fn recomputeInitialSize( + self: *Surface, +) InitialSizeError!void { + // Both width and height must be set for this to work, as + // documented on the config options. + if (self.config.window_height <= 0 or + self.config.window_width <= 0) return; + + const scale = self.rt_surface.getContentScale() catch + return error.ContentScaleUnavailable; + const height = @max( + self.config.window_height, + min_window_height_cells, + ) * self.size.cell.height; + const width = @max( + self.config.window_width, + min_window_width_cells, + ) * self.size.cell.width; + const width_f32: f32 = @floatFromInt(width); + const height_f32: f32 = @floatFromInt(height); + + // The final values are affected by content scale and we need to + // account for the padding so we get the exact correct grid size. + const final_width: u32 = + @as(u32, @intFromFloat(@ceil(width_f32 / scale.x))) + + self.size.padding.left + + self.size.padding.right; + const final_height: u32 = + @as(u32, @intFromFloat(@ceil(height_f32 / scale.y))) + + self.size.padding.top + + self.size.padding.bottom; + + _ = self.rt_app.performAction( + .{ .surface = self }, + .initial_size, + .{ .width = final_width, .height = final_height }, + ) catch return error.AppActionFailed; +} + +/// Represents text read from the terminal and some metadata about it +/// that is often useful to apprts. +pub const Text = struct { + /// The text that was read from the terminal. + text: [:0]const u8, + + /// The viewport information about this text, if it is visible in + /// the viewport. + viewport: ?Viewport = null, + + pub const Viewport = struct { + /// The top-left corner of the selection in pixels within the viewport. + tl_px_x: f64, + tl_px_y: f64, + + /// The linear offset of the start of the selection and the length. + /// This is "linear" in the sense that it is the offset in the + /// flattened viewport as a single array of text. + /// + /// Note: these values are currently wrong if there is a partially + /// visible selection in the viewport (i.e. the top-left or + /// bottom-right of the selection is outside the viewport). But the + /// apprt usecase we have right now doesn't require these to be + /// correct so... let's fix this later. The wrong values will always + /// be within the text bounds so we aren't risking an overflow. + offset_start: u32, + offset_len: u32, + }; + + pub fn deinit(self: *Text, alloc: Allocator) void { + alloc.free(self.text); + } +}; + +/// Grab the value of text at the given selection point. Note that the +/// selection structure is used as a way to determine the area of the +/// screen to read from, it doesn't have to match the user's current +/// selection state. +/// +/// The returned value contains allocated data and must be deinitialized. +pub fn dumpText( + self: *Surface, + alloc: Allocator, + sel: terminal.Selection, +) !Text { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + return try self.dumpTextLocked(alloc, sel); +} + +/// Same as `dumpText` but assumes the renderer state mutex is already +/// held. +pub fn dumpTextLocked( + self: *Surface, + alloc: Allocator, + sel: terminal.Selection, +) !Text { + // Read out the text + const text = try self.io.terminal.screens.active.selectionString(alloc, .{ + .sel = sel, + .trim = false, + }); + errdefer alloc.free(text); + + // Calculate our viewport info if we can. + const vp: ?Text.Viewport = viewport: { + // If our bottom right pin is before the viewport, then we can't + // possibly have this text be within the viewport. + const vp_tl_pin = self.io.terminal.screens.active.pages.getTopLeft(.viewport); + const br_pin = sel.bottomRight(self.io.terminal.screens.active); + if (br_pin.before(vp_tl_pin)) break :viewport null; + + // If our top-left pin is after the viewport, then we can't possibly + // have this text be within the viewport. + const vp_br_pin = self.io.terminal.screens.active.pages.getBottomRight(.viewport) orelse { + // I don't think this is possible but I don't want to crash on + // that assertion so let's just break out... + log.warn("viewport bottom-right pin not found, bug?", .{}); + break :viewport null; + }; + const tl_pin = sel.topLeft(self.io.terminal.screens.active); + if (vp_br_pin.before(tl_pin)) break :viewport null; + + // We established that our top-left somewhere before the viewport + // bottom-right and that our bottom-right is somewhere after + // the top-left. This means that at least some portion of our + // selection is within the viewport. + + // Our top-left point. If it doesn't exist in the viewport it must + // be before and we can return (0,0). + const tl_pt: terminal.Point = self.io.terminal.screens.active.pages.pointFromPin( + .viewport, + tl_pin, + ) orelse tl: { + if (comptime std.debug.runtime_safety) { + assert(tl_pin.before(vp_tl_pin)); + } + + break :tl .{ .viewport = .{} }; + }; + + // Our bottom-right point. If it doesn't exist in the viewport + // it must be the bottom-right of the viewport. + const br_pt = self.io.terminal.screens.active.pages.pointFromPin( + .viewport, + br_pin, + ) orelse br: { + if (comptime std.debug.runtime_safety) { + assert(vp_br_pin.before(br_pin)); + } + + break :br self.io.terminal.screens.active.pages.pointFromPin( + .viewport, + vp_br_pin, + ).?; + }; + + const tl_coord = tl_pt.coord(); + const br_coord = br_pt.coord(); + + // Our sizes are all scaled so we need to send the unscaled values back. + const content_scale = self.rt_surface.getContentScale() catch .{ .x = 1, .y = 1 }; + const x: f64 = x: { + // Simple x * cell width gives the left + var x: f64 = @floatFromInt(tl_coord.x * self.size.cell.width); + + // Add padding + x += @floatFromInt(self.size.padding.left); + + // Scale + x /= content_scale.x; + + break :x x; + }; + const y: f64 = y: { + // Simple y * cell height gives the top + var y: f64 = @floatFromInt(tl_coord.y * self.size.cell.height); + + // We want the text baseline + y += @floatFromInt(self.size.cell.height); + y -= @floatFromInt(self.font_metrics.cell_baseline); + + // Add padding + y += @floatFromInt(self.size.padding.top); + + // Scale + y /= content_scale.y; + + break :y y; + }; + + // Utilize viewport sizing to convert to offsets + const start = tl_coord.y * self.io.terminal.screens.active.pages.cols + tl_coord.x; + const end = br_coord.y * self.io.terminal.screens.active.pages.cols + br_coord.x; + + break :viewport .{ + .tl_px_x = x, + .tl_px_y = y, + .offset_start = start, + .offset_len = end - start, + }; + }; + + return .{ + .text = text, + .viewport = vp, + }; +} + +/// Returns true if the terminal has a selection. +pub fn hasSelection(self: *const Surface) bool { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + return self.io.terminal.screens.active.selection != null; +} + +/// Returns the selected text. This is allocated. +pub fn selectionString(self: *Surface, alloc: Allocator) !?[:0]const u8 { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const sel = self.io.terminal.screens.active.selection orelse return null; + return try self.io.terminal.screens.active.selectionString(alloc, .{ + .sel = sel, + .trim = false, + }); +} + +/// Returns the pwd of the terminal, if any. This is always copied because +/// the pwd can change at any point from termio. If we are calling from the IO +/// thread you should just check the terminal directly. +pub fn pwd( + self: *const Surface, + alloc: Allocator, +) Allocator.Error!?[]const u8 { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const terminal_pwd = self.io.terminal.getPwd() orelse return null; + return try alloc.dupe(u8, terminal_pwd); +} + +/// Resolves a relative file path to an absolute path using the terminal's pwd. +fn resolvePathForOpening( + self: *Surface, + path: []const u8, +) Allocator.Error!?[]const u8 { + if (!std.fs.path.isAbsolute(path)) { + const terminal_pwd = self.io.terminal.getPwd() orelse { + return null; + }; + + const resolved = try std.fs.path.resolve(self.alloc, &.{ terminal_pwd, path }); + + std.fs.accessAbsolute(resolved, .{}) catch { + self.alloc.free(resolved); + return null; + }; + + return resolved; + } + + return null; +} + +/// Returns the x/y coordinate of where the IME (Input Method Editor) +/// keyboard should be rendered. +pub fn imePoint(self: *const Surface) apprt.IMEPos { + self.renderer_state.mutex.lock(); + const cursor = self.renderer_state.terminal.screens.active.cursor; + const preedit_width: usize = if (self.renderer_state.preedit) |preedit| preedit.width() else 0; + self.renderer_state.mutex.unlock(); + + // TODO: need to handle when scrolling and the cursor is not + // in the visible portion of the screen. + + // Our sizes are all scaled so we need to send the unscaled values back. + const content_scale = self.rt_surface.getContentScale() catch .{ .x = 1, .y = 1 }; + + const x: f64 = x: { + // Simple x * cell width gives the top-left corner, then add padding offset + var x: f64 = @floatFromInt(cursor.x * self.size.cell.width + self.size.padding.left); + + // We want the midpoint + x += @as(f64, @floatFromInt(self.size.cell.width)) / 2; + + // And scale it + x /= content_scale.x; + + break :x x; + }; + + const y: f64 = y: { + // Simple y * cell height gives the top-left corner, then add padding offset + var y: f64 = @floatFromInt(cursor.y * self.size.cell.height + self.size.padding.top); + + // We want the bottom + y += @floatFromInt(self.size.cell.height); + + // And scale it + y /= content_scale.y; + + break :y y; + }; + + // Our height for now is always just the cell height because our preedit + // rendering only renders in a single line. + const height: f64 = height: { + var height: f64 = @floatFromInt(self.size.cell.height); + height /= content_scale.y; + break :height height; + }; + const width: f64 = width: { + var width: f64 = @floatFromInt(preedit_width * self.size.cell.width); + + // Our max width is the remaining screen width after the cursor. + // We don't have to deal with wrapping because the preedit doesn't + // wrap right now. + const screen_width: f64 = @floatFromInt(self.size.terminal().width); + const x_offset: f64 = @floatFromInt((cursor.x + 1) * self.size.cell.width); + const max = screen_width - x_offset; + width = @min(width, max); + + // Note: we don't apply content scale here because it looks like + // for some reason in macOS its already scaled. I'm not sure why + // that is so I'm going to just leave this comment here so its known + // that I left this out on purpose pending more investigation. + + break :width width; + }; + + return .{ + .x = x, + .y = y, + .width = width, + .height = height, + }; +} + +fn clipboardWrite(self: *const Surface, data: []const u8, loc: apprt.Clipboard) !void { + if (self.config.clipboard_write == .deny) { + log.info("application attempted to write clipboard, but 'clipboard-write' is set to deny", .{}); + return; + } + + const dec = std.base64.standard.Decoder; + + // Build buffer + const size = dec.calcSizeForSlice(data) catch |err| switch (err) { + error.InvalidPadding => { + log.info("application sent invalid base64 data for OSC 52", .{}); + return; + }, + + // Should not be reachable but don't want to risk it. + else => return, + }; + var buf = try self.alloc.allocSentinel(u8, size, 0); + defer self.alloc.free(buf); + buf[buf.len] = 0; + + // Decode + dec.decode(buf, data) catch |err| switch (err) { + // Ignore this. It is possible to actually have valid data and + // get this error, so we allow it. + error.InvalidPadding => {}, + + else => { + log.info("application sent invalid base64 data for OSC 52", .{}); + return; + }, + }; + assert(buf[buf.len] == 0); + + // When clipboard-write is "ask" a prompt is displayed to the user asking + // them to confirm the clipboard access. Each app runtime handles this + // differently. + const confirm = self.config.clipboard_write == .ask; + self.rt_surface.setClipboard(loc, &.{.{ + .mime = "text/plain", + .data = buf, + }}, confirm) catch |err| { + log.err("error setting clipboard string err={}", .{err}); + return; + }; +} + +fn copySelectionToClipboards( + self: *Surface, + sel: terminal.Selection, + clipboards: []const apprt.Clipboard, + format: input.Binding.Action.CopyToClipboard, +) !void { + // Create an arena to simplify memory management here. + var arena = ArenaAllocator.init(self.alloc); + defer arena.deinit(); + const alloc = arena.allocator(); + + // The options we'll use for all formatting. We'll just override the + // emit format. + const opts: terminal.formatter.Options = .{ + .emit = .plain, // We'll override this below + .unwrap = true, + .trim = self.config.clipboard_trim_trailing_spaces, + .codepoint_map = self.config.clipboard_codepoint_map.map.list, + .background = self.io.terminal.colors.background.get(), + .foreground = self.io.terminal.colors.foreground.get(), + .palette = &self.io.terminal.colors.palette.current, + }; + + const ScreenFormatter = terminal.formatter.ScreenFormatter; + var aw: std.Io.Writer.Allocating = .init(alloc); + var contents: std.ArrayList(apprt.ClipboardContent) = .empty; + switch (format) { + .plain => { + var formatter: ScreenFormatter = .init(self.io.terminal.screens.active, opts); + formatter.content = .{ .selection = sel }; + try formatter.format(&aw.writer); + try contents.append(alloc, .{ + .mime = "text/plain", + .data = try aw.toOwnedSliceSentinel(0), + }); + }, + + .vt => { + var formatter: ScreenFormatter = .init(self.io.terminal.screens.active, opts: { + var copy = opts; + copy.emit = .vt; + break :opts copy; + }); + formatter.content = .{ .selection = sel }; + try formatter.format(&aw.writer); + + // Note: We don't apply codepoint mappings to VT format since it contains + // escape sequences that should be preserved as-is + try contents.append(alloc, .{ + .mime = "text/plain", + .data = try aw.toOwnedSliceSentinel(0), + }); + }, + + .html => { + var formatter: ScreenFormatter = .init(self.io.terminal.screens.active, opts: { + var copy = opts; + copy.emit = .html; + break :opts copy; + }); + formatter.content = .{ .selection = sel }; + try formatter.format(&aw.writer); + + // Note: We don't apply codepoint mappings to HTML format since HTML + // has its own character encoding and entity system + try contents.append(alloc, .{ + .mime = "text/html", + .data = try aw.toOwnedSliceSentinel(0), + }); + }, + + .mixed => { + // First, generate plain text with codepoint mappings applied + var formatter: ScreenFormatter = .init(self.io.terminal.screens.active, opts); + formatter.content = .{ .selection = sel }; + try formatter.format(&aw.writer); + try contents.append(alloc, .{ + .mime = "text/plain", + .data = try aw.toOwnedSliceSentinel(0), + }); + + assert(aw.written().len == 0); + // Second, generate HTML without codepoint mappings + formatter = .init(self.io.terminal.screens.active, opts: { + var copy = opts; + copy.emit = .html; + + // We purposely don't emit background/foreground for mixed + // mode because the HTML contents is often used for rich text + // input and with trimmed spaces it looks pretty bad. + copy.background = null; + copy.foreground = null; + + break :opts copy; + }); + formatter.content = .{ .selection = sel }; + try formatter.format(&aw.writer); + + // Note: We don't apply codepoint mappings to HTML format + try contents.append(alloc, .{ + .mime = "text/html", + .data = try aw.toOwnedSliceSentinel(0), + }); + }, + } + + assert(contents.items.len > 0); + for (clipboards) |clipboard| self.rt_surface.setClipboard( + clipboard, + contents.items, + false, + ) catch |err| { + log.err( + "error setting clipboard string clipboard={} err={}", + .{ clipboard, err }, + ); + }; +} + +/// Set the selection contents. +/// +/// This must be called with the renderer mutex held. +fn setSelection(self: *Surface, sel_: ?terminal.Selection) !void { + const prev_ = self.io.terminal.screens.active.selection; + try self.io.terminal.screens.active.select(sel_); + + // If copy on select is false then exit early. + if (self.config.copy_on_select == .false) return; + + // Set our selection clipboard. If the selection is cleared we do not + // clear the clipboard. If the selection is set, we only set the clipboard + // again if it changed, since setting the clipboard can be an expensive + // operation. + const sel = sel_ orelse return; + if (prev_) |prev| if (sel.eql(prev)) return; + + switch (self.config.copy_on_select) { + .false => unreachable, // handled above with an early exit + + // Both standard and selection clipboards are set. + .clipboard => 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, + ); + }, + } +} + +/// Change the cell size for the terminal grid. This can happen as +/// a result of changing the font size at runtime. +fn setCellSize(self: *Surface, size: rendererpkg.CellSize) !void { + // Update our cell size within our size struct + self.size.cell = size; + self.balancePaddingIfNeeded(); + + // Notify the terminal + self.queueIo(.{ .resize = self.size }, .unlocked); + + // Update our terminal default size if necessary. + self.recomputeInitialSize() catch |err| { + // We don't treat this as a fatal error because not setting + // an initial size shouldn't stop our terminal from working. + log.warn("unable to recompute initial window size: {}", .{err}); + }; + + // Notify the window + _ = try self.rt_app.performAction( + .{ .surface = self }, + .cell_size, + .{ .width = size.width, .height = size.height }, + ); +} + +/// Change the font size. +/// +/// This can only be called from the main thread. +pub fn setFontSize(self: *Surface, size: font.face.DesiredSize) !void { + log.debug("set font size size={}", .{size.points}); + + // Update our font size so future changes work + self.font_size = size; + + // We need to build up a new font stack for this font size. + const font_grid_key, const font_grid = try self.app.font_grid_set.ref( + &self.config.font, + self.font_size, + ); + errdefer self.app.font_grid_set.deref(font_grid_key); + + // Set our cell size + try self.setCellSize(.{ + .width = font_grid.metrics.cell_width, + .height = font_grid.metrics.cell_height, + }); + + // Notify our render thread of the new font stack. The renderer + // MUST accept the new font grid and deref the old. + _ = self.renderer_thread.mailbox.push(.{ + .font_grid = .{ + .grid = font_grid, + .set = &self.app.font_grid_set, + .old_key = self.font_grid_key, + .new_key = font_grid_key, + }, + }, .{ .forever = {} }); + + // Once we've sent the key we can replace our key + self.font_grid_key = font_grid_key; + self.font_metrics = font_grid.metrics; + + // Schedule render which also drains our mailbox + self.queueRender() catch unreachable; +} + +/// This queues a render operation with the renderer thread. The render +/// isn't guaranteed to happen immediately but it will happen as soon as +/// practical. +fn queueRender(self: *Surface) !void { + try self.renderer_thread.wakeup.notify(); +} + +pub fn sizeCallback(self: *Surface, size: apprt.SurfaceSize) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + const new_screen_size: rendererpkg.ScreenSize = .{ + .width = size.width, + .height = size.height, + }; + + // Update our screen size, but only if it actually changed. And if + // the screen size didn't change, then our grid size could not have + // changed, so we just return. + if (self.size.screen.equals(new_screen_size)) return; + + try self.resize(new_screen_size); +} + +fn resize(self: *Surface, size: rendererpkg.ScreenSize) !void { + // Save our screen size + self.size.screen = size; + self.balancePaddingIfNeeded(); + + // Recalculate our grid size. Because Ghostty supports fluid resizing, + // its possible the grid doesn't change at all even if the screen size changes. + // We have to update the IO thread no matter what because we send + // pixel-level sizing to the subprocess. + const grid_size = self.size.grid(); + if (grid_size.columns < 5 and (self.size.padding.left > 0 or self.size.padding.right > 0)) { + log.warn("WARNING: very small terminal grid detected with padding " ++ + "set. Is your padding reasonable?", .{}); + } + if (grid_size.rows < 2 and (self.size.padding.top > 0 or self.size.padding.bottom > 0)) { + log.warn("WARNING: very small terminal grid detected with padding " ++ + "set. Is your padding reasonable?", .{}); + } + + // Mail the IO thread + self.queueIo(.{ .resize = self.size }, .unlocked); +} + +/// Recalculate the balanced padding if needed. +fn balancePaddingIfNeeded(self: *Surface) void { + if (self.config.window_padding_balance == .false) return; + const content_scale = try self.rt_surface.getContentScale(); + const x_dpi = content_scale.x * font.face.default_dpi; + const y_dpi = content_scale.y * font.face.default_dpi; + self.size.balancePadding(self.config.scaledPadding(x_dpi, y_dpi), self.config.window_padding_balance); +} + +/// Called to set the preedit state for character input. Preedit is used +/// with dead key states, for example, when typing an accent character. +/// This should be called with null to reset the preedit state. +/// +/// The core surface will NOT reset the preedit state on charCallback or +/// keyCallback and we rely completely on the apprt implementation to track +/// the preedit state correctly. +/// +/// The preedit input must be UTF-8 encoded. +pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) !void { + // log.debug("text preeditCallback value={any}", .{preedit_}); + + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // We clear our selection when ANY OF: + // 1. We have an existing preedit + // 2. We have preedit text + if (self.renderer_state.preedit != null or + preedit_ != null) + { + if (self.config.selection_clear_on_typing) { + self.setSelection(null) catch {}; + } + } + + // We always clear our prior preedit + if (self.renderer_state.preedit) |p| { + self.alloc.free(p.codepoints); + self.renderer_state.preedit = null; + } + + // Mark preedit dirty flag + self.io.terminal.flags.dirty.preedit = true; + + // If we have no text, we're done. We queue a render in case we cleared + // a prior preedit (likely). + const text = preedit_ orelse { + try self.queueRender(); + return; + }; + + // We convert the UTF-8 text to codepoints. + const view = try std.unicode.Utf8View.init(text); + var it = view.iterator(); + + // Allocate the codepoints slice + const Codepoint = rendererpkg.State.Preedit.Codepoint; + var codepoints: std.ArrayListUnmanaged(Codepoint) = .{}; + defer codepoints.deinit(self.alloc); + while (it.nextCodepoint()) |cp| { + const width: usize = @intCast(unicode.table.get(cp).width); + + // I've never seen a preedit text with a zero-width character. In + // theory its possible but we can't really handle it right now. + // Let's just ignore it. + if (width <= 0) continue; + + try codepoints.append( + self.alloc, + .{ .codepoint = cp, .wide = width >= 2 }, + ); + } + + // If we have no codepoints, then we're done. + if (codepoints.items.len == 0) { + try self.queueRender(); + return; + } + + self.renderer_state.preedit = .{ + .codepoints = try codepoints.toOwnedSlice(self.alloc), + }; + try self.queueRender(); +} + +/// Returns true if the given key event would trigger a keybinding +/// if it were to be processed. This is useful for determining if +/// a key event should be sent to the terminal or not. +/// +/// Note that this function does not check if the binding itself +/// is performable, only if the key event would trigger a binding. +/// If a performable binding is found and the event is not performable, +/// then Ghosty will act as though the binding does not exist. +pub fn keyEventIsBinding( + self: *Surface, + event_orig: input.KeyEvent, +) ?input.Binding.Flags { + // Apply key remappings for consistency with keyCallback + var event = event_orig; + if (self.config.key_remaps.isRemapped(event_orig.mods)) { + event.mods = self.config.key_remaps.apply(event_orig.mods); + } + + switch (event.action) { + .release => return null, + .press, .repeat => {}, + } + + // Look up our entry + const entry: input.Binding.Set.Entry = entry: { + // If we're in a sequence, check the sequence set + if (self.keyboard.sequence_set) |set| { + break :entry set.getEvent(event) orelse return null; + } + + // Check active key tables (inner-most to outer-most) + const table_items = self.keyboard.table_stack.items; + for (0..table_items.len) |i| { + const rev_i: usize = table_items.len - 1 - i; + if (table_items[rev_i].set.getEvent(event)) |entry| { + break :entry entry; + } + } + + // Check the root set + break :entry self.config.keybind.set.getEvent(event) orelse return null; + }; + + // Return flags based on the + return switch (entry.value_ptr.*) { + .leader => .{}, + inline .leaf, .leaf_chained => |v| v.flags, + }; +} + +/// Called for any key events. This handles keybindings, encoding and +/// sending to the terminal, etc. +pub fn keyCallback( + self: *Surface, + event_orig: input.KeyEvent, +) !InputEffect { + // log.warn("text keyCallback event={}", .{event_orig}); + + // Apply key remappings to transform modifiers before any processing. + // This allows users to remap modifier keys at the app level. + var event = event_orig; + if (self.config.key_remaps.isRemapped(event_orig.mods)) { + event.mods = self.config.key_remaps.apply(event_orig.mods); + } + + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // Setup our inspector event if we have an inspector. + var insp_ev: ?inspectorpkg.KeyEvent = if (self.inspector != null) ev: { + var copy = event; + copy.utf8 = ""; + if (event.utf8.len > 0) copy.utf8 = try self.alloc.dupe(u8, event.utf8); + break :ev .{ .event = copy }; + } else null; + + // When we're done processing, we always want to add the event to + // the inspector. + defer if (insp_ev) |ev| ev: { + // We have to check for the inspector again because our keybinding + // might close it. + const insp = self.inspector orelse { + ev.deinit(self.alloc); + break :ev; + }; + + if (insp.recordKeyEvent(self.alloc, ev)) { + self.queueRender() catch {}; + } else |err| { + log.warn("error adding key event to inspector err={}", .{err}); + } + }; + + // Handle keybindings first. We need to handle this on all events + // (press, repeat, release) because a press may perform a binding but + // a release should not encode if we consumed the press. + if (try self.maybeHandleBinding( + event, + if (insp_ev) |*ev| ev else null, + )) |v| return v; + // If we allow KAM and KAM is enabled then we do nothing. + if (self.config.vt_kam_allowed) { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + if (self.io.terminal.modes.get(.disable_keyboard)) return .consumed; + } + + // If this input event has text, then we hide the mouse if configured. + // We only do this on pressed events to avoid hiding the mouse when we + // change focus due to a keybinding (i.e. switching tabs). + if (self.config.mouse_hide_while_typing and + event.action == .press and + !self.mouse.hidden and + event.utf8.len > 0) + { + self.hideMouse(); + } + + // If our mouse modifiers change we may need to change our + // link highlight state. + if (!self.mouse.mods.equal(event.mods)) mouse_mods: { + // Update our modifiers, this will update mouse mods too + self.modsChanged(event.mods); + + // We only refresh links if + // 1. mouse reporting is off + // OR + // 2. mouse reporting is on and we are not reporting shift to the terminal + if (self.io.terminal.flags.mouse_event == .none or + (self.mouse.mods.shift and !self.mouseShiftCapture(false))) + { + // Refresh our link state + const pos = self.rt_surface.getCursorPos() catch break :mouse_mods; + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + self.mouseRefreshLinks( + pos, + self.posToViewport(pos.x, pos.y), + self.mouse.over_link, + ) catch |err| { + log.warn("failed to refresh links err={}", .{err}); + break :mouse_mods; + }; + } else if (self.io.terminal.flags.mouse_event != .none and !self.mouse.mods.shift) { + // If we have mouse reports on and we don't have shift pressed, we reset state + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_shape, + self.io.terminal.mouse_shape, + ); + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_over_link, + .{ .url = "" }, + ); + try self.queueRender(); + } + } + + // Process the cursor state logic. This will update the cursor shape if + // needed, depending on the key state. + if ((SurfaceMouse{ + .physical_key = event.key, + .mouse_event = self.io.terminal.flags.mouse_event, + .mouse_shape = self.io.terminal.mouse_shape, + .mods = self.mouse.mods, + .over_link = self.mouse.over_link, + .hidden = self.mouse.hidden, + }).keyToMouseShape()) |shape| _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_shape, + shape, + ); + + // We've processed a key event that produced some data so we want to + // track the last pressed key. + self.pressed_key = event: { + // We need to unset the allocated fields that will become invalid + var copy = event; + copy.utf8 = ""; + + // If we have a previous pressed key and we're releasing it + // then we set it to invalid to prevent repeating the release event. + if (event.action == .release) { + // if we didn't have a previous event and this is a release + // event then we just want to set it to null. + const prev = self.pressed_key orelse break :event null; + if (prev.key == copy.key) copy.key = .unidentified; + } + + // If our key is invalid and we have no mods, then we're done! + // This helps catch the state that we naturally released all keys. + if (copy.key == .unidentified and copy.mods.empty()) break :event null; + + break :event copy; + }; + + // Encode and send our key. If we didn't encode anything, then we + // return the effect as ignored. + if (try self.encodeKey( + event, + if (insp_ev) |*ev| ev else null, + )) |write_req| { + // If our process is exited and we press a key that results in + // an encoded value, we close the surface. We want to eventually + // move this behavior to the apprt probably. + if (self.child_exited) { + self.close(); + return .closed; + } + + errdefer write_req.deinit(); + self.queueIo(switch (write_req) { + .small => |v| .{ .write_small = v }, + .stable => |v| .{ .write_stable = v }, + .alloc => |v| .{ .write_alloc = v }, + }, .unlocked); + } else { + // No valid request means that we didn't encode anything. + return .ignored; + } + + // If our event is any keypress that isn't a modifier and we generated + // some data to send to the pty, then we move the viewport down to the + // bottom. We also clear the selection for any key other then modifiers. + if (!event.key.modifier()) { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + if (self.config.selection_clear_on_typing or + event.key == .escape) + { + try self.setSelection(null); + } + + if (self.config.scroll_to_bottom.keystroke) self.io.terminal.scrollViewport(.bottom); + + try self.queueRender(); + } + + return .consumed; +} + +/// Maybe handles a binding for a given event and if so returns the effect. +/// Returns null if the event is not handled in any way and processing should +/// continue. +fn maybeHandleBinding( + self: *Surface, + event: input.KeyEvent, + insp_ev: ?*inspectorpkg.KeyEvent, +) !?InputEffect { + switch (event.action) { + // Release events never trigger a binding but we need to check if + // we consumed the press event so we don't encode the release. + .release => { + if (self.keyboard.last_trigger) |last| { + if (last == event.bindingHash()) { + // We don't reset the last trigger on release because + // an apprt may send multiple release events for a single + // press event. + return .consumed; + } + } + + return null; + }, + + // Carry on processing. + .press, .repeat => {}, + } + + // Find an entry in the keybind set that matches our event. + const entry: input.Binding.Set.Entry = entry: { + // Handle key sequences first. + if (self.keyboard.sequence_set) |set| { + // Get our entry from the set for the given event. + if (set.getEvent(event)) |v| break :entry v; + + // No entry found. We need to encode everything up to this + // point and send to the pty since we're in a sequence. + + // We ignore modifiers so that nested sequences such as + // ctrl+a>ctrl+b>c work. + if (event.key.modifier()) return null; + + // If we have a catch-all of ignore, then we special case our + // invalid sequence handling to ignore it. + if (self.catchAllIsIgnore()) { + self.endKeySequence(.drop, .retain); + return .ignored; + } + + // Encode everything up to this point + self.endKeySequence(.flush, .retain); + + return null; + } + + // No currently active sequence, move on to tables. For tables, + // we search inner-most table to outer-most. The table stack does + // NOT include the root set. + const table_items = self.keyboard.table_stack.items; + if (table_items.len > 0) { + for (0..table_items.len) |i| { + const rev_i: usize = table_items.len - 1 - i; + const table = table_items[rev_i]; + if (table.set.getEvent(event)) |v| { + // If this is a one-shot activation AND its the currently + // active table, then we deactivate it after this. + // Note: we may want to change the semantics here to + // remove this table no matter where it is in the stack, + // maybe. + if (table.once and i == 0) _ = try self.performBindingAction( + .deactivate_key_table, + ); + + break :entry v; + } + } + } + + // No table, use our default set + break :entry self.config.keybind.set.getEvent(event) orelse + return null; + }; + + // Determine if this entry has an action or if its a leader key. + const leaf: input.Binding.Set.GenericLeaf = switch (entry.value_ptr.*) { + .leader => |set| { + // Setup the next set we'll look at. + self.keyboard.sequence_set = set; + + // Store this event so that we can drain and encode on invalid. + // We don't need to cap this because it is naturally capped by + // the config validation. + if (try self.encodeKey(event, insp_ev)) |req| { + try self.keyboard.sequence_queued.append(self.alloc, req); + } + + // Start or continue our key sequence + _ = self.rt_app.performAction( + .{ .surface = self }, + .key_sequence, + .{ .trigger = entry.key_ptr.* }, + ) catch |err| { + log.warn( + "failed to notify app of key sequence err={}", + .{err}, + ); + }; + + return .consumed; + }, + + inline .leaf, .leaf_chained => |leaf| leaf.generic(), + }; + + // consumed determines if the input is consumed or if we continue + // encoding the key (if we have a key to encode). + const consumed = consumed: { + // If the consumed flag is explicitly set, then we are consumed. + if (leaf.flags.consumed) break :consumed true; + + // If the global or all flag is set, we always consume. + if (leaf.flags.global or leaf.flags.all) break :consumed true; + + break :consumed false; + }; + + // We have an action, so at this point we're handling SOMETHING so + // we reset the last trigger to null. We only set this if we actually + // perform an action (below) + self.keyboard.last_trigger = null; + + // An action also always resets the sequence set. + self.keyboard.sequence_set = null; + + // Setup our actions + const actions = leaf.actionsSlice(); + + // Attempt to perform the action + log.debug("key event binding flags={} action={any}", .{ + leaf.flags, + actions, + }); + const performed = performed: { + // If this is a global or all action, then we perform it on + // the app and it applies to every surface. + if (leaf.flags.global or leaf.flags.all) { + self.app.performAllChainedAction( + self.rt_app, + actions, + ); + + // "All" actions are always performed since they are global. + break :performed true; + } + + // Perform each action. We are performed if ANY of the chained + // actions perform. + var performed: bool = false; + for (actions) |action| { + if (self.performBindingAction(action)) |v| { + performed = performed or v; + } else |err| { + log.info( + "key binding action failed action={t} err={}", + .{ action, err }, + ); + } + } + + break :performed performed; + }; + + if (performed) { + // If we performed an action and it was a closing action, + // our "self" pointer is not safe to use anymore so we need to + // just exit immediately. + for (actions) |action| if (closingAction(action)) { + log.debug("key binding is a closing binding, halting key event processing", .{}); + return .closed; + }; + + // If our action was "ignore" then we return the special input + // effect of "ignored". + for (actions) |action| if (action == .ignore) { + // If we're in a sequence, clear it. + self.endKeySequence(.drop, .retain); + + return .ignored; + }; + } + + // If we have the performable flag and the action was not performed, + // then we act as though a binding didn't exist. + if (leaf.flags.performable and !performed) { + // If we're in a sequence, we treat this as if we pressed a key + // that doesn't exist in the sequence. Reset our sequence and flush + // any queued events. + self.endKeySequence(.flush, .retain); + + return null; + } + + // If we consume this event, then we are done. If we don't consume + // it, we processed the action but we still want to process our + // encodings, too. + if (consumed) { + // If we had queued events, we deinit them since we consumed + self.endKeySequence(.drop, .retain); + + // Store our last trigger so we don't encode the release event + self.keyboard.last_trigger = event.bindingHash(); + + if (insp_ev) |ev| { + ev.binding = self.alloc.dupe( + input.Binding.Action, + actions, + ) catch |err| binding: { + log.warn( + "error allocating binding action for inspector err={}", + .{err}, + ); + break :binding &.{}; + }; + } + return .consumed; + } + + // If we didn't perform OR we didn't consume, then we want to + // encode any queued events for a sequence. + self.endKeySequence(.flush, .retain); + + return null; +} + +fn deactivateAllKeyTables(self: *Surface) !bool { + switch (self.keyboard.table_stack.items.len) { + // No key table active. This does nothing. + 0 => return false, + + // Clear the entire table stack. + else => self.keyboard.table_stack.clearAndFree(self.alloc), + } + + // Notify the UI. + _ = self.rt_app.performAction( + .{ .surface = self }, + .key_table, + .deactivate_all, + ) catch |err| { + log.warn( + "failed to notify app of key table err={}", + .{err}, + ); + }; + + return true; +} + +/// This checks if the current keybinding sets have a catch_all binding +/// with `ignore`. This is used to determine some special input cases. +fn catchAllIsIgnore(self: *Surface) bool { + // Get our catch all + const entry: input.Binding.Set.Entry = entry: { + const trigger: input.Binding.Trigger = .{ .key = .catch_all }; + + const table_items = self.keyboard.table_stack.items; + for (0..table_items.len) |i| { + const rev_i: usize = table_items.len - 1 - i; + const entry = table_items[rev_i].set.get(trigger) orelse continue; + break :entry entry; + } + + break :entry self.config.keybind.set.get(trigger) orelse + return false; + }; + + // We have a catch-all entry, see if its an ignore + return switch (entry.value_ptr.*) { + .leader => false, + .leaf => |leaf| leaf.action == .ignore, + .leaf_chained => |leaf| chained: for (leaf.actions.items) |action| { + if (action == .ignore) break :chained true; + } else false, + }; +} + +const KeySequenceQueued = enum { flush, drop }; +const KeySequenceMemory = enum { retain, free }; + +/// End a key sequence. Safe to call if no key sequence is active. +/// +/// Action and mem determine the behavior of the queued inputs up to this +/// point. +fn endKeySequence( + self: *Surface, + action: KeySequenceQueued, + mem: KeySequenceMemory, +) void { + // Notify apprt key sequence ended + _ = self.rt_app.performAction( + .{ .surface = self }, + .key_sequence, + .end, + ) catch |err| { + log.warn( + "failed to notify app of key sequence end err={}", + .{err}, + ); + }; + + // No matter what we clear our current sequence set. This restores + // the set we look at to the root set. + self.keyboard.sequence_set = null; + + // If we have no queued data, there is nothing else to do. + if (self.keyboard.sequence_queued.items.len == 0) return; + + // Run the proper action first + switch (action) { + .flush => for (self.keyboard.sequence_queued.items) |write_req| { + self.queueIo(switch (write_req) { + .small => |v| .{ .write_small = v }, + .stable => |v| .{ .write_stable = v }, + .alloc => |v| .{ .write_alloc = v }, + }, .unlocked); + }, + + .drop => for (self.keyboard.sequence_queued.items) |req| req.deinit(), + } + + // Memory handling of the sequence after the action + switch (mem) { + .free => self.keyboard.sequence_queued.clearAndFree(self.alloc), + .retain => self.keyboard.sequence_queued.clearRetainingCapacity(), + } +} + +/// Encodes the key event into a write request. The write request will +/// always copy or allocate so the caller can safely free the event. +fn encodeKey( + self: *Surface, + event: input.KeyEvent, + insp_ev: ?*inspectorpkg.KeyEvent, +) !?termio.Message.WriteReq { + const write_req: termio.Message.WriteReq = req: { + // Build our encoding options, which requires the lock. + const encoding_opts = self.encodeKeyOpts(); + + // Try to write the input into a small array. This fits almost + // every scenario. Larger situations can happen due to long + // pre-edits. + var data: termio.Message.WriteReq.Small.Array = undefined; + var writer: std.Io.Writer = .fixed(&data); + if (input.key_encode.encode( + &writer, + event, + encoding_opts, + )) { + const written = writer.buffered(); + + // Special-case: we did nothing. + if (written.len == 0) return null; + + break :req .{ .small = .{ + .data = data, + .len = @intCast(written.len), + } }; + } else |err| switch (err) { + // Means we need to allocate + error.WriteFailed => {}, + } + + // We need to allocate. We allocate double the UTF-8 length + // or double the small array size, whichever is larger. That's + // a heuristic that should work. The only scenario I know while + // typing this where we don't have enough space is a long preedit, + // and in that case the size we need is exactly the UTF-8 length, + // so the double is being safe. + var alloc_writer: std.Io.Writer.Allocating = try .initCapacity( + self.alloc, + @max(event.utf8.len * 2, data.len * 2), + ); + defer alloc_writer.deinit(); + + // This results in a double allocation but this is such an unlikely + // path the performance impact is unimportant. + try input.key_encode.encode( + &alloc_writer.writer, + event, + encoding_opts, + ); + break :req try termio.Message.WriteReq.init( + self.alloc, + alloc_writer.writer.buffered(), + ); + }; + + // Copy the encoded data into the inspector event if we have one. + // We do this before the mailbox because the IO thread could + // release the memory before we get a chance to copy it. + if (insp_ev) |ev| pty: { + const slice = write_req.slice(); + const copy = self.alloc.alloc(u8, slice.len) catch |err| { + log.warn("error allocating pty data for inspector err={}", .{err}); + break :pty; + }; + errdefer self.alloc.free(copy); + @memcpy(copy, slice); + ev.pty = copy; + } + + return write_req; +} + +fn encodeKeyOpts(self: *const Surface) input.key_encode.Options { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t = &self.io.terminal; + + var opts: input.key_encode.Options = .fromTerminal(t); + if (comptime builtin.os.tag != .macos) return opts; + + opts.macos_option_as_alt = self.config.macos_option_as_alt orelse detect: { + // If we don't have alt pressed, it doesn't matter what this + // config is so we can just say "false" and break out and avoid + // more expensive checks below. + if (!self.mouse.mods.alt) break :detect .false; + + // Alt is pressed, we're on macOS. We break some encapsulation + // here and assume libghostty for ease... + break :detect self.rt_app.keyboardLayout().detectOptionAsAlt(); + }; + + return opts; +} + +/// Sends text as-is to the terminal without triggering any keyboard +/// protocol. This will treat the input text as if it was pasted +/// from the clipboard so the same logic will be applied. Namely, +/// if bracketed mode is on this will do a bracketed paste. Otherwise, +/// this will filter newlines to '\r'. +pub fn textCallback(self: *Surface, text: []const u8) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + try self.completeClipboardPaste(text, true); +} + +/// Callback for when the surface is fully visible or not, regardless +/// of focus state. This is used to pause rendering when the surface +/// is not visible, and also re-render when it becomes visible again. +pub fn occlusionCallback(self: *Surface, visible: bool) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + _ = self.renderer_thread.mailbox.push(.{ + .visible = visible, + }, .{ .forever = {} }); + try self.queueRender(); +} + +pub fn focusCallback(self: *Surface, focused: bool) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // Always update the app focused surface, otherwise we miss + // the first surface created. + if (focused) self.app.focusSurface(self); + + // If our focus state is unchanged we do nothing else. + if (self.focused == focused) return; + self.focused = focused; + + // Notify our render thread of the new state + _ = self.renderer_thread.mailbox.push(.{ + .focus = focused, + }, .{ .forever = {} }); + + if (!focused) unfocused: { + // If we lost focus and we have a keypress, then we want to send a key + // release event for it. Depending on the apprt, this CAN result in + // duplicate key release events, but that is better than not sending + // a key release event at all. + var pressed_key = self.pressed_key orelse break :unfocused; + self.pressed_key = null; + + // All our actions will be releases + pressed_key.action = .release; + + // Release the full key first + if (pressed_key.key != .unidentified) { + assert(self.keyCallback(pressed_key) catch |err| err: { + log.warn("error releasing key on focus loss err={}", .{err}); + break :err .ignored; + } != .closed); + } + + // Release any modifiers if set + if (pressed_key.mods.empty()) break :unfocused; + + // This is kind of nasty comptime meta programming but all we're doing + // here is going through all the modifiers and if they're set, releasing + // both the left and right sides of the modifier. This may not match + // the exact input event but it ensures a full reset. + const keys = &.{ "shift", "ctrl", "alt", "super" }; + const original_key = pressed_key.key; + inline for (keys) |key| { + if (@field(pressed_key.mods, key)) { + @field(pressed_key.mods, key) = false; + inline for (&.{ "right", "left" }) |side| { + const keyname = comptime keyname: { + break :keyname if (std.mem.eql(u8, key, "ctrl")) + "control" + else if (std.mem.eql(u8, key, "super")) + "meta" + else + key; + }; + pressed_key.key = @field(input.Key, keyname ++ "_" ++ side); + if (pressed_key.key != original_key) { + assert(self.keyCallback(pressed_key) catch |err| err: { + log.warn("error releasing key on focus loss err={}", .{err}); + break :err .ignored; + } != .closed); + } + } + } + } + } + + // Schedule render which also drains our mailbox + try self.queueRender(); + + // Whenever our focus changes we unhide the mouse. The mouse will be + // hidden again if the user starts typing. This helps alleviate some + // buggy behavior upstream in macOS with the mouse never becoming visible + // again when tabbing between programs (see #2525). + self.showMouse(); + + // Update the focus state and notify the terminal + { + self.renderer_state.mutex.lock(); + self.io.terminal.flags.focused = focused; + self.renderer_state.mutex.unlock(); + self.queueIo(.{ .focused = focused }, .unlocked); + } +} + +pub fn refreshCallback(self: *Surface) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // The point of this callback is to schedule a render, so do that. + try self.queueRender(); +} + +// The amount to scroll. This structure is always normalized so that +// negative is down, left and positive is up, right. Note that INTERNALLY, +// vertical scroll on our terminal uses positive for down (right is not +// supported by our screen since scrollback is only vertical). +const ScrollAmount = struct { + delta: isize = 0, + + pub fn direction(self: ScrollAmount) enum { down_left, up_right } { + return if (self.delta < 0) .down_left else .up_right; + } + + pub fn magnitude(self: ScrollAmount) usize { + return @abs(self.delta); + } +}; + +/// Mouse scroll event. Negative is down, left. Positive is up, right. +/// +/// "Natural scrolling" is a macOS term for inverting the scroll direction. +/// This should be handled by the apprt implementation. At this layer, +/// negative is always down, left. +pub fn scrollCallback( + self: *Surface, + xoff: f64, + yoff: f64, + scroll_mods: input.ScrollMods, +) !void { + // log.info("SCROLL: xoff={} yoff={} mods={}", .{ xoff, yoff, scroll_mods }); + + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // Always show the mouse again if it is hidden + if (self.mouse.hidden) self.showMouse(); + + const y: ScrollAmount = if (yoff == 0) .{} else y: { + // We use cell_size to determine if we have accumulated enough to trigger a scroll + const cell_size: f64 = @floatFromInt(self.size.cell.height); + + // If we have precision scroll, yoff is the number of pixels to scroll. In non-precision + // scroll, yoff is the number of wheel ticks. Some mice are capable of reporting fractional + // wheel ticks, which don't necessarily get reported as precision scrolls. We normalize all + // scroll events to pixels by multiplying the wheel tick value and the cell size. This means + // that a wheel tick of 1 results in single scroll event. + const yoff_adjusted: f64 = if (scroll_mods.precision) + yoff * self.config.mouse_scroll_multiplier.precision + else yoff_adjusted: { + // Round out the yoff to an absolute minimum of 1. macos tries to + // simulate precision scrolling with non precision events by + // ramping up the magnitude of the offsets as it detects faster + // scrolling. Single click (very slow) scrolls are reported with a + // magnitude of 0.1 which would normally require a few clicks + // before we register an actual scroll event (depending on cell + // height and the mouse_scroll_multiplier setting). + const yoff_max: f64 = if (yoff > 0) + @max(yoff, 1) + else + @min(yoff, -1); + + break :yoff_adjusted yoff_max * cell_size * self.config.mouse_scroll_multiplier.discrete; + }; + + // Add our previously saved pending amount to the offset to get the + // new offset value. The signs of the pending and yoff should match + // so that we move further away from zero, but we don't assert + // this because in theory a user could scroll in the opposite + // direction and undo a pending scroll. + const poff: f64 = self.mouse.pending_scroll_y + yoff_adjusted; + + // If the new offset is less than a single unit of scroll, we save + // the new pending value and do not scroll yet. + if (@abs(poff) < cell_size) { + self.mouse.pending_scroll_y = poff; + break :y .{}; + } + + // We scroll by the number of rows in the offset and save the remainder + const amount = poff / cell_size; + assert(@abs(amount) >= 1); + self.mouse.pending_scroll_y = poff - (amount * cell_size); + + // Round towards zero. + const delta: isize = @intFromFloat(@trunc(amount)); + assert(@abs(delta) >= 1); + + break :y .{ .delta = delta }; + }; + + // For detailed comments see the y calculation above. + const x: ScrollAmount = if (xoff == 0) .{} else x: { + if (!scroll_mods.precision) { + const x_delta_isize: isize = @intFromFloat(@round(xoff)); + break :x .{ .delta = x_delta_isize }; + } + + const poff: f64 = self.mouse.pending_scroll_x + xoff; + const cell_size: f64 = @floatFromInt(self.size.cell.width); + if (@abs(poff) < cell_size) { + self.mouse.pending_scroll_x = poff; + break :x .{}; + } + + const amount = poff / cell_size; + assert(@abs(amount) >= 1); + self.mouse.pending_scroll_x = poff - (amount * cell_size); + const delta: isize = @intFromFloat(@trunc(amount)); + assert(@abs(delta) >= 1); + break :x .{ .delta = delta }; + }; + + // log.info("SCROLL: delta_y={} delta_x={}", .{ y.delta, x.delta }); + + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // If we have an active mouse reporting mode, clear the selection. + // The selection can occur if the user uses the shift mod key to + // override mouse grabbing from the window. + if (self.isMouseReporting()) { + try self.setSelection(null); + } + + // If we're in alternate screen with alternate scroll enabled, then + // we convert to cursor keys. This only happens if we're: + // (1) alt screen (2) no explicit mouse reporting and (3) alt + // scroll mode enabled. + if (self.io.terminal.screens.active_key == .alternate and + self.io.terminal.flags.mouse_event == .none and + self.io.terminal.modes.get(.mouse_alternate_scroll)) + { + if (y.delta != 0) { + // When we send mouse events as cursor keys we always + // clear the selection. + try self.setSelection(null); + + const seq = if (self.io.terminal.modes.get(.cursor_keys)) seq: { + // cursor key: application mode + break :seq switch (y.direction()) { + .up_right => "\x1bOA", + .down_left => "\x1bOB", + }; + } else seq: { + // cursor key: normal mode + break :seq switch (y.direction()) { + .up_right => "\x1b[A", + .down_left => "\x1b[B", + }; + }; + for (0..y.magnitude()) |_| { + self.queueIo(.{ .write_stable = seq }, .locked); + } + } + + return; + } + + // We have mouse events, are not in an alternate scroll buffer, + // or have alternate scroll disabled. In this case, we just run + // the normal logic. + + // If we're scrolling up or down, then send a mouse event. + if (self.isMouseReporting()) { + for (0..@abs(y.delta)) |_| { + const pos = try self.rt_surface.getCursorPos(); + self.mouseReport(switch (y.direction()) { + .up_right => .four, + .down_left => .five, + }, .press, self.mouse.mods, pos); + } + + for (0..@abs(x.delta)) |_| { + const pos = try self.rt_surface.getCursorPos(); + self.mouseReport(switch (x.direction()) { + .up_right => .six, + .down_left => .seven, + }, .press, self.mouse.mods, pos); + } + + // If mouse reporting is on, we do not want to scroll the + // viewport. + return; + } + + if (y.delta != 0) { + // Modify our viewport, this requires a lock since it affects + // rendering. We have to switch signs here because our delta + // is negative down but our viewport is positive down. + self.io.terminal.scrollViewport(.{ .delta = y.delta * -1 }); + } + } + + try self.queueRender(); +} + +/// This is called when the content scale of the surface changes. The surface +/// can then update any DPI-sensitive state. +pub fn contentScaleCallback(self: *Surface, content_scale: apprt.ContentScale) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // Calculate the new DPI + const x_dpi = content_scale.x * font.face.default_dpi; + const y_dpi = content_scale.y * font.face.default_dpi; + + // Update our font size which is dependent on the DPI + const size = size: { + var size = self.font_size; + size.xdpi = @intFromFloat(x_dpi); + size.ydpi = @intFromFloat(y_dpi); + break :size size; + }; + + // If our DPI didn't actually change, save a lot of work by doing nothing. + if (size.xdpi == self.font_size.xdpi and size.ydpi == self.font_size.ydpi) { + return; + } + + try self.setFontSize(size); + + // Update our padding which is dependent on DPI. We only do this for + // unbalanced padding since balanced padding is not dependent on DPI. + if (self.config.window_padding_balance == .false) { + self.size.padding = self.config.scaledPadding(x_dpi, y_dpi); + } + + // Force a resize event because the change in padding will affect + // pixel-level changes to the renderer and viewport. + try self.resize(self.size.screen); +} + +/// Returns true if mouse reporting is enabled both in the config and +/// the terminal state. +fn isMouseReporting(self: *const Surface) bool { + return self.config.mouse_reporting and + self.io.terminal.flags.mouse_event != .none; +} + +fn mouseReport( + self: *Surface, + button: ?input.MouseButton, + action: input.MouseAction, + mods: input.Mods, + pos: apprt.CursorPos, +) void { + // Mouse reporting must be enabled by both config and terminal state + assert(self.config.mouse_reporting); + assert(self.io.terminal.flags.mouse_event != .none); + + // Build our encoding options. + const encoding_opts: input.mouse_encode.Options = opts: { + // Terminal and size state. + var opts: input.mouse_encode.Options = .fromTerminal( + &self.io.terminal, + self.size, + ); + + // Whether any button is pressed at all. + opts.any_button_pressed = pressed: { + for (self.mouse.click_state) |state| { + if (state != .release) break :pressed true; + } + + break :pressed false; + }; + + // Keep track of our last reported viewport cell for event + // deduplication. + opts.last_cell = &self.mouse.event_point; + + break :opts opts; + }; + + var data: termio.Message.WriteReq.Small.Array = undefined; + var writer: std.Io.Writer = .fixed(&data); + input.mouse_encode.encode(&writer, .{ + .button = button, + .action = action, + .mods = mods, + .pos = .{ + .x = pos.x, + .y = pos.y, + }, + }, encoding_opts) catch |err| switch (err) { + error.WriteFailed => { + // This should never happen since mouse events should never + // be able to overflow the size of our small array. But if it + // does, let's log it and return. No need to crash upstreams. + // In the future we may want to fall back to allocation. + log.warn("failed to encode mouse event err={}", .{err}); + return; + }, + }; + const written = writer.buffered(); + if (written.len == 0) return; + + self.queueIo(.{ .write_small = .{ + .data = data, + .len = @intCast(written.len), + } }, .locked); +} + +/// Returns true if the shift modifier is allowed to be captured by modifier +/// events. It is up to the caller to still verify it is a situation in which +/// shift capture makes sense (i.e. left button, mouse click, etc.) +fn mouseShiftCapture(self: *const Surface, lock: bool) bool { + // Handle our never/always case where we don't need a lock. + switch (self.config.mouse_shift_capture) { + .never => return false, + .always => return true, + .false, .true => {}, + } + + if (lock) self.renderer_state.mutex.lock(); + defer if (lock) self.renderer_state.mutex.unlock(); + + // If the terminal explicitly requests it then we always allow it + // since we processed never/always at this point. + switch (self.io.terminal.flags.mouse_shift_capture) { + .false => return false, + .true => return true, + .null => {}, + } + + // Otherwise, go with the user's preference + return switch (self.config.mouse_shift_capture) { + .false => false, + .true => true, + .never, .always => unreachable, // handled earlier + }; +} + +/// Returns true if the mouse is currently captured by the terminal +/// (i.e. reporting events). +pub fn mouseCaptured(self: *Surface) bool { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + return self.io.terminal.flags.mouse_event != .none; +} + +/// Called for mouse button press/release events. This will return true +/// if the mouse event was consumed in some way (i.e. the program is capturing +/// mouse events). If the event was not consumed, then false is returned. +pub fn mouseButtonCallback( + self: *Surface, + action: input.MouseButtonState, + button: input.MouseButton, + mods: input.Mods, +) !bool { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // log.debug("mouse action={} button={} mods={}", .{ action, button, mods }); + + // If we have an inspector, we always queue a render + if (self.inspector != null) { + defer self.queueRender() catch {}; + } + + // Always record our latest mouse state + self.mouse.click_state[@intCast(@intFromEnum(button))] = action; + + // Always show the mouse again if it is hidden + if (self.mouse.hidden) self.showMouse(); + + // Update our modifiers if they changed + self.modsChanged(mods); + + // This is set to true if the terminal is allowed to capture the shift + // modifier. Note we can do this more efficiently probably with less + // locking/unlocking but clicking isn't that frequent enough to be a + // bottleneck. + const shift_capture = self.mouseShiftCapture(true); + + // Shift-click continues the previous mouse state if we have a selection. + // cursorPosCallback will also do a mouse report so we don't need to do any + // of the logic below. + if (button == .left and action == .press) { + // We could do all the conditionals in one but I find it more + // readable as a human to break this one up. + if (mods.shift and + self.mouse.left_click_count > 0 and + !shift_capture) + extend_selection: { + // We split this conditional out on its own because this is the + // only one that requires a renderer mutex grab which is VERY + // expensive because it could block all our threads. + if (!self.hasSelection()) break :extend_selection; + + // If we are within the interval that the click would register + // an increment then we do not extend the selection. + if (std.time.Instant.now()) |now| { + const since = now.since(self.mouse.left_click_time); + if (since <= self.config.mouse_interval) { + // Click interval very short, we may be increasing + // click counts so we don't extend the selection. + break :extend_selection; + } + } else |err| { + // This is a weird behavior, I think either behavior is actually + // fine. This failure should be exceptionally rare anyways. + // My thinking here is that we can't be sure if we should extend + // the selection or not so we just don't. + log.warn("failed to get time, not extending selection err={}", .{err}); + break :extend_selection; + } + + const pos = try self.rt_surface.getCursorPos(); + try self.cursorPosCallback(pos, null); + return true; + } + } + + if (button == .left and action == .release) { + // Stop selection scrolling when releasing the left mouse button + // but only when selection scrolling is active. + if (self.selection_scroll_active) { + self.queueIo( + .{ .selection_scroll = false }, + .unlocked, + ); + } + + // 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) { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const prev_ = self.io.terminal.screens.active.selection; + if (prev_) |prev| { + try self.setSelection(terminal.Selection.init( + prev.start(), + prev.end(), + prev.rectangle, + )); + } + } + + // Handle link clicking. We want to do this before we do mouse + // reporting or any other mouse handling because a successfully + // clicked link will swallow the event. + if (self.mouse.over_link) { + const pos = try self.rt_surface.getCursorPos(); + if (self.processLinks(pos)) |processed| { + if (processed) return true; + } else |err| { + log.warn("error processing links err={}", .{err}); + } + } + + // Handle prompt clicking. If we released our mouse on a prompt + // and we support some kind of click events, then we need to + // move to it. + if (self.maybePromptClick()) |handled| { + if (handled) return true; + } else |err| { + log.warn("error processing prompt click err={}", .{err}); + } + } + + // Report mouse events if enabled + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + if (self.isMouseReporting()) report: { + // If we have shift-pressed and we aren't allowed to capture it, + // then we do not do a mouse report. + if (mods.shift and !shift_capture) break :report; + + // In any other mouse button scenario without shift pressed we + // clear the selection since the underlying application can handle + // that in any way (i.e. "scrolling"). + try self.setSelection(null); + + // We also set the left click count to 0 so that if mouse reporting + // is disabled in the middle of press (before release) we don't + // suddenly start selecting text. + self.mouse.left_click_count = 0; + + const pos = try self.rt_surface.getCursorPos(); + + const report_action: input.MouseAction = switch (action) { + .press => .press, + .release => .release, + }; + + self.mouseReport( + button, + report_action, + self.mouse.mods, + pos, + ); + + // If we're doing mouse reporting, we do not support any other + // selection or highlighting. + return true; + } + } + + // For left button clicks we always record some information for + // selection/highlighting purposes. + if (button == .left and action == .press) click: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t: *terminal.Terminal = self.renderer_state.terminal; + const screen: *terminal.Screen = self.renderer_state.terminal.screens.active; + + const pos = try self.rt_surface.getCursorPos(); + const pin = pin: { + const pt_viewport = self.posToViewport(pos.x, pos.y); + const pin = screen.pages.pin(.{ + .viewport = .{ + .x = pt_viewport.x, + .y = pt_viewport.y, + }, + }) orelse { + // Weird... our viewport x/y that we just converted isn't + // found in our pages. This is probably a bug but we don't + // want to crash in releases because its harmless. So, we + // only assert in debug mode. + if (comptime std.debug.runtime_safety) unreachable; + break :click; + }; + + break :pin try screen.pages.trackPin(pin); + }; + errdefer screen.pages.untrackPin(pin); + + // If we move our cursor too much between clicks then we reset + // the multi-click state. + if (self.mouse.left_click_count > 0) { + const max_distance: f64 = @floatFromInt(self.size.cell.width); + const distance = @sqrt( + std.math.pow(f64, pos.x - self.mouse.left_click_xpos, 2) + + std.math.pow(f64, pos.y - self.mouse.left_click_ypos, 2), + ); + + if (distance > max_distance) self.mouse.left_click_count = 0; + } + + if (self.mouse.left_click_pin) |prev| { + if (t.screens.get(self.mouse.left_click_screen)) |pin_screen| { + pin_screen.pages.untrackPin(prev); + } + self.mouse.left_click_pin = null; + } + + // Store it + self.mouse.left_click_pin = pin; + self.mouse.left_click_screen = t.screens.active_key; + self.mouse.left_click_xpos = pos.x; + self.mouse.left_click_ypos = pos.y; + + // Setup our click counter and timer + if (std.time.Instant.now()) |now| { + // If we have mouse clicks, then we check if the time elapsed + // is less than and our interval and if so, increase the count. + if (self.mouse.left_click_count > 0) { + const since = now.since(self.mouse.left_click_time); + if (since > self.config.mouse_interval) { + self.mouse.left_click_count = 0; + } + } + + self.mouse.left_click_time = now; + self.mouse.left_click_count += 1; + + // We only support up to triple-clicks. + if (self.mouse.left_click_count > 3) self.mouse.left_click_count = 1; + } else |err| { + self.mouse.left_click_count = 1; + log.err("error reading time, mouse multi-click won't work err={}", .{err}); + } + + // In all cases below, we set the selection directly rather than use + // `setSelection` because we want to avoid copying the selection + // to the selection clipboard. For left mouse clicks we only set + // the clipboard on release. + switch (self.mouse.left_click_count) { + // Single click + 1 => { + // If we have a selection, clear it. This always happens. + if (self.io.terminal.screens.active.selection != null) { + try self.io.terminal.screens.active.select(null); + try self.queueRender(); + } + }, + + // Double click, select the word under our mouse. + // First try to detect if we're clicking on a URL to select the entire URL. + 2 => { + const sel_ = sel: { + // Try link detection without requiring modifier keys + if (self.linkAtPin( + pin.*, + null, + )) |result_| { + if (result_) |result| { + break :sel result.selection; + } + } else |_| { + // Ignore any errors, likely regex errors. + } + + break :sel self.io.terminal.screens.active.selectWord(pin.*, self.config.selection_word_chars); + }; + if (sel_) |sel| { + try self.io.terminal.screens.active.select(sel); + try self.queueRender(); + } + }, + + // Triple click, select the line under our mouse + 3 => { + const sel_ = if (mods.ctrlOrSuper()) + self.io.terminal.screens.active.selectOutput(pin.*) + else + self.io.terminal.screens.active.selectLine(.{ .pin = pin.* }); + if (sel_) |sel| { + try self.io.terminal.screens.active.select(sel); + try self.queueRender(); + } + }, + + // We should be bounded by 1 to 3 + else => unreachable, + } + } + + // Middle-click pastes from our selection clipboard + if (button == .middle and action == .press) { + const clipboard: apprt.Clipboard = if (self.rt_surface.supportsClipboard(.selection)) + .selection + else + .standard; + _ = try self.startClipboardRequest(clipboard, .{ .paste = {} }); + } + + // Right-click down selects word for context menus. If the apprt + // doesn't implement context menus this can be a bit weird but they + // are supported by our two main apprts so we always do this. If we + // want to be careful in the future we can add a function to apprts + // that let's us know. + if (button == .right and action == .press) sel: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // Get our viewport pin + const screen: *terminal.Screen = self.renderer_state.terminal.screens.active; + const pos = try self.rt_surface.getCursorPos(); + const pin = pin: { + const pt_viewport = self.posToViewport(pos.x, pos.y); + const pin = screen.pages.pin(.{ + .viewport = .{ + .x = pt_viewport.x, + .y = pt_viewport.y, + }, + }) orelse { + if (comptime std.debug.runtime_safety) unreachable; + break :sel; + }; + + break :pin pin; + }; + + switch (self.config.right_click_action) { + .ignore => {}, + .@"context-menu" => { + // If we already have a selection and the selection contains + // where we clicked then we don't want to modify the selection. + if (self.io.terminal.screens.active.selection) |prev_sel| { + if (prev_sel.contains(screen, pin)) break :sel; + + // The selection doesn't contain our pin, so we create a new + // word selection where we clicked. + } + + // If there is a link at this position, we want to + // select the link. Otherwise, select the word. + if (try self.linkAtPos(pos)) |link| { + try self.setSelection(link.selection); + } else { + const sel = screen.selectWord( + pin, + self.config.selection_word_chars, + ) orelse break :sel; + try self.setSelection(sel); + } + try self.queueRender(); + + // Don't consume so that we show the context menu in apprt. + return false; + }, + .copy => { + if (self.io.terminal.screens.active.selection) |sel| { + try self.copySelectionToClipboards( + sel, + &.{.standard}, + .mixed, + ); + } + + try self.setSelection(null); + try self.queueRender(); + }, + .@"copy-or-paste" => if (self.io.terminal.screens.active.selection) |sel| { + try self.copySelectionToClipboards( + sel, + &.{.standard}, + .mixed, + ); + try self.setSelection(null); + try self.queueRender(); + } else { + // Pasting can trigger a lock grab in complete clipboard + // request so we need to unlock. + self.renderer_state.mutex.unlock(); + defer self.renderer_state.mutex.lock(); + _ = try self.startClipboardRequest(.standard, .paste); + + // We don't need to clear selection because we didn't have + // one to begin with. + }, + .paste => { + // Before we yield the lock, clear our selection if we have + // one. + try self.setSelection(null); + try self.queueRender(); + + // Pasting can trigger a lock grab in complete clipboard + // request so we need to unlock. + self.renderer_state.mutex.unlock(); + defer self.renderer_state.mutex.lock(); + _ = try self.startClipboardRequest(.standard, .paste); + }, + } + + // Consume the event such that the context menu is not displayed. + return true; + } + + return false; +} + +fn maybePromptClick(self: *Surface) !bool { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t: *terminal.Terminal = self.renderer_state.terminal; + const screen: *terminal.Screen = t.screens.active; + + // If our screen doesn't handle any prompt clicks, then we never + // do anything. + if (screen.semantic_prompt.click == .none) return false; + + // If cursor-click-to-move is disabled, we don't do any prompt clicking. + if (!self.config.cursor_click_to_move) return false; + + // If our cursor isn't currently at a prompt then we don't handle + // prompt clicks because we can't move if we're not in a prompt! + if (!t.cursorIsAtPrompt()) return false; + + // If we have a selection currently, then releasing the mouse + // completes the selection and we don't do prompt moving. I don't + // love this logic, I think it should be generalized to "if the + // mouse release was on a different cell than the mouse press" but + // our mouse state at the time of writing this doesn't support that. + if (screen.selection != null) return false; + + // Get the pin for our mouse click. + const pos = try self.rt_surface.getCursorPos(); + const pos_vp = self.posToViewport(pos.x, pos.y); + const click_pin: terminal.Pin = pin: { + const pin = screen.pages.pin(.{ + .viewport = .{ + .x = pos_vp.x, + .y = pos_vp.y, + }, + }) orelse { + // See mouseButtonCallback for explanation + if (comptime std.debug.runtime_safety) unreachable; + return false; + }; + + break :pin pin; + }; + + // Get our cursor's most current prompt. + const prompt_pin: terminal.Pin = prompt_pin: { + var it = screen.cursor.page_pin.promptIterator( + .left_up, + null, + ); + break :prompt_pin it.next() orelse { + // This shouldn't be possible because we asserted we're at + // a prompt above, so we MUST find some prompt in a left_up search. + log.warn("cursor is at prompt but no prompt found", .{}); + if (comptime std.debug.runtime_safety) unreachable; + return false; + }; + }; + + // If our mouse click is before the prompt, we don't move. + // We DO ALLOW clicks AFTER the prompt, specifically with Kitty's + // click_events=1 since we rely on the shell to validate out of + // bounds clicks. This matches Kitty's logic as best I can tell. + if (click_pin.before(prompt_pin)) return false; + + // At this point we've established: + // - Screen supports prompt clicks + // - Cursor is at a prompt + // - Click is at or below our prompt + switch (screen.semantic_prompt.click) { + // Guarded at the start of this function + .none => unreachable, + + .click_events => { + // For the event, we always send a left-click press event. + // This matches what Kitty sends. + var data: termio.Message.WriteReq.Small.Array = undefined; + const resp = try std.fmt.bufPrint( + &data, + "\x1B[<0;{d};{d}M", + .{ pos_vp.x + 1, pos_vp.y + 1 }, + ); + + // Not that noisy since this only happens on prompt clicks. + log.debug( + "sending click_events=1 event=ESC{s}", + .{resp[1..]}, + ); + + // Ask our IO thread to write the data + self.queueIo(.{ .write_small = .{ + .data = data, + .len = @intCast(resp.len), + } }, .locked); + }, + + .cl => { + const left_arrow = if (t.modes.get(.cursor_keys)) "\x1bOD" else "\x1b[D"; + const right_arrow = if (t.modes.get(.cursor_keys)) "\x1bOC" else "\x1b[C"; + + const move = screen.promptClickMove(click_pin); + for (0..move.left) |_| { + self.queueIo( + .{ .write_stable = left_arrow }, + .locked, + ); + } + for (0..move.right) |_| { + self.queueIo( + .{ .write_stable = right_arrow }, + .locked, + ); + } + }, + } + + return true; +} + +const Link = struct { + action: input.Link.Action, + selection: terminal.Selection, +}; + +/// Returns the link at the given cursor position, if any. +/// +/// Requires the renderer mutex is held. +fn linkAtPos( + self: *Surface, + pos: apprt.CursorPos, +) !?Link { + // Convert our cursor position to a screen point. + const screen: *terminal.Screen = self.renderer_state.terminal.screens.active; + const mouse_pin: terminal.Pin = mouse_pin: { + const point = self.posToViewport(pos.x, pos.y); + const pin = screen.pages.pin(.{ .viewport = point }) orelse { + log.warn("failed to get pin for clicked point", .{}); + return null; + }; + break :mouse_pin pin; + }; + + // Get our comparison mods + const mouse_mods = self.mouseModsWithCapture(self.mouse.mods); + + // If we have the proper modifiers set then we can check for OSC8 links. + if (mouse_mods.equal(input.ctrlOrSuper(.{}))) hyperlink: { + const rac = mouse_pin.rowAndCell(); + const cell = rac.cell; + if (!cell.hyperlink) break :hyperlink; + const sel = terminal.Selection.init(mouse_pin, mouse_pin, false); + return .{ .action = ._open_osc8, .selection = sel }; + } + + // Fall back to configured links + return try self.linkAtPin(mouse_pin, mouse_mods); +} + +/// Detects if a link is present at the given pin. +/// +/// If mouse mods is null then mouse mod requirements are ignored (all +/// configured links are checked). +/// +/// Requires the renderer state mutex is held. +fn linkAtPin( + self: *Surface, + mouse_pin: terminal.Pin, + mouse_mods: ?input.Mods, +) !?Link { + if (self.config.links.len == 0) return null; + + const screen: *terminal.Screen = self.renderer_state.terminal.screens.active; + const line = screen.selectLine(.{ + .pin = mouse_pin, + .whitespace = null, + .semantic_prompt_boundary = false, + }) orelse return null; + + var strmap: terminal.StringMap = undefined; + self.alloc.free(try screen.selectionString(self.alloc, .{ + .sel = line, + .trim = false, + .map = &strmap, + })); + defer strmap.deinit(self.alloc); + + for (self.config.links) |link| { + // Skip highlight/mods check when mouse_mods is null (double-click mode) + if (mouse_mods) |mods| switch (link.highlight) { + .always, .hover => {}, + .always_mods, .hover_mods => |v| if (!v.equal(mods)) continue, + }; + + var it = strmap.searchIterator(link.regex); + while (true) { + var match = (try it.next()) orelse break; + defer match.deinit(); + const sel = match.selection(); + if (!sel.contains(screen, mouse_pin)) continue; + return .{ + .action = link.action, + .selection = sel, + }; + } + } + + return null; +} + +/// This returns the mouse mods to consider for link highlighting or +/// other purposes taking into account when shift is pressed for releasing +/// the mouse from capture. +/// +/// The renderer state mutex must be held. +fn mouseModsWithCapture(self: *Surface, mods: input.Mods) input.Mods { + // In any of these scenarios, whatever mods are set (even shift) + // are preserved. + if (self.io.terminal.flags.mouse_event == .none) return mods; + if (!mods.shift) return mods; + if (self.mouseShiftCapture(false)) return mods; + + // We have mouse capture, shift set, and we're not allowed to capture + // shift, so we can clear shift. + var final = mods; + final.shift = false; + return final; +} + +/// Attempt to invoke the action of any link that is under the +/// given position. +/// +/// Requires the renderer state mutex is held. +fn processLinks(self: *Surface, pos: apprt.CursorPos) !bool { + const link = try self.linkAtPos(pos) orelse return false; + switch (link.action) { + .open => { + const str = try self.io.terminal.screens.active.selectionString(self.alloc, .{ + .sel = link.selection, + .trim = false, + }); + defer self.alloc.free(str); + + const resolved_path = try self.resolvePathForOpening(str); + defer if (resolved_path) |p| self.alloc.free(p); + + const url_to_open = resolved_path orelse str; + try self.openUrl(.{ .kind = .unknown, .url = url_to_open }); + }, + + ._open_osc8 => { + const uri = self.osc8URI(link.selection.start()) orelse { + log.warn("failed to get URI for OSC8 hyperlink", .{}); + return false; + }; + try self.openUrl(.{ .kind = .unknown, .url = uri }); + }, + } + + return true; +} + +fn openUrl( + self: *Surface, + action: apprt.action.OpenUrl, +) !void { + // If the apprt handles it then we're done. + if (try self.rt_app.performAction( + .{ .surface = self }, + .open_url, + action, + )) return; + + // apprt didn't handle it, fallback to our simple cross-platform + // URL opener. We log a warning because we want well-behaved + // apprts to handle this themselves. + log.warn("apprt did not handle open URL action, falling back to default opener", .{}); + try internal_os.open( + self.alloc, + action.kind, + action.url, + ); +} + +/// Return the URI for an OSC8 hyperlink at the given position or null +/// if there is no hyperlink. +fn osc8URI(self: *Surface, pin: terminal.Pin) ?[]const u8 { + _ = self; + const page = &pin.node.data; + const cell = pin.rowAndCell().cell; + const link_id = page.lookupHyperlink(cell) orelse return null; + const entry = page.hyperlink_set.get(page.memory, link_id); + return entry.uri.slice(page.memory); +} + +pub fn mousePressureCallback( + self: *Surface, + stage: input.MousePressureStage, + pressure: f64, +) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // We don't currently use the pressure value for anything. In the + // future, we could report this to applications using new mouse + // events or utilize it for some custom UI. + _ = pressure; + + // If the pressure stage is the same as what we already have do nothing + if (self.mouse.pressure_stage == stage) return; + + // Update our pressure stage. + self.mouse.pressure_stage = stage; + + // If our left mouse button is pressed and we're entering a deep + // click then we want to start a selection. We treat this as a + // word selection since that is typical macOS behavior. + const left_idx = @intFromEnum(input.MouseButton.left); + if (self.mouse.click_state[left_idx] == .press and + stage == .deep) + select: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // This should always be set in this state but we don't want + // to handle state inconsistency here. + const pin = self.mouse.left_click_pin orelse break :select; + const sel = self.io.terminal.screens.active.selectWord( + pin.*, + self.config.selection_word_chars, + ) orelse break :select; + try self.io.terminal.screens.active.select(sel); + try self.queueRender(); + } +} + +/// Cursor position callback. +/// +/// Send negative x or y values to indicate the cursor is outside the +/// viewport. The magnitude of the negative values are meaningless; +/// they are only used to indicate the cursor is outside the viewport. +/// It's important to do this to ensure hover states are cleared. +/// +/// The mods parameter is optional because some apprts do not provide +/// modifier information on cursor position events. If mods is null then +/// we'll use the last known mods. This is usually accurate since mod events +/// will trigger key press events but on some platforms we don't get them. +/// For example, on macOS, unfocused surfaces don't receive key events but +/// do receive mouse events so we have to rely on updated mods. +pub fn cursorPosCallback( + self: *Surface, + pos: apprt.CursorPos, + mods: ?input.Mods, +) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + // log.debug("cursor pos x={} y={} mods={?}", .{ pos.x, pos.y, mods }); + + // If the position is negative, it is outside our viewport and + // we need to clear any hover states. + if (pos.x < 0 or pos.y < 0) { + // Reset our hyperlink state + self.mouse.link_point = null; + if (self.mouse.over_link) { + self.mouse.over_link = false; + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_shape, + self.io.terminal.mouse_shape, + ); + _ = try self.rt_app.performAction( + .{ .surface = self }, + .mouse_over_link, + .{ .url = "" }, + ); + try self.queueRender(); + } + + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // No mouse point so we don't highlight links + self.renderer_state.mouse.point = null; + + // Mark the link's row as dirty, but continue with updating the + // mouse state below so we can scroll when our position is negative. + self.renderer_state.terminal.screens.active.dirty.hyperlink_hover = true; + } + + // Always show the mouse again if it is hidden + if (self.mouse.hidden) self.showMouse(); + + // Update our modifiers if they changed + if (mods) |v| self.modsChanged(v); + + // The mouse position in the viewport + const pos_vp = self.posToViewport(pos.x, pos.y); + + // We always reset the over link status because it will be reprocessed + // below. But we need the old value to know if we need to undo mouse + // shape changes. + const over_link = self.mouse.over_link; + self.mouse.over_link = false; + + // We are reading/writing state for the remainder + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // Stop selection scrolling when inside the viewport within a 1px buffer + // for fullscreen windows, but only when selection scrolling is active. + if (pos.y >= 1 and self.selection_scroll_active) { + self.queueIo( + .{ .selection_scroll = false }, + .locked, + ); + } + + // Update our mouse state. We set this to null initially because we only + // want to set it when we're not selecting or doing any other mouse + // event. + self.renderer_state.mouse.point = null; + + // If we have an inspector, we need to always record position information + if (self.inspector) |insp| { + insp.mouse.last_xpos = pos.x; + insp.mouse.last_ypos = pos.y; + + const screen: *terminal.Screen = self.renderer_state.terminal.screens.active; + insp.mouse.last_point = screen.pages.pin(.{ .viewport = .{ + .x = pos_vp.x, + .y = pos_vp.y, + } }); + try self.queueRender(); + } + + // Handle link hovering + // We refresh links when + // 1. we were previously over a link + // OR + // 2. the cursor position has changed (either we have no previous state, or the state has + // changed) + // AND + // 1. mouse reporting is off + // OR + // 2. mouse reporting is on and we are not reporting shift to the terminal + if ((over_link or + self.mouse.link_point == null or + (self.mouse.link_point != null and !self.mouse.link_point.?.eql(pos_vp))) and + (self.io.terminal.flags.mouse_event == .none or + (self.mouse.mods.shift and !self.mouseShiftCapture(false)))) + { + // If we were previously over a link, we always update. We do this so that if the text + // changed underneath us, even if the mouse didn't move, we update the URL hints and state + try self.mouseRefreshLinks(pos, pos_vp, over_link); + } + + // Do a mouse report + if (self.isMouseReporting()) report: { + // Shift overrides mouse "grabbing" in the window, taken from Kitty. + // This only applies if there is a mouse button pressed so that + // movement reports are not affected. + if (self.mouse.mods.shift and !self.mouseShiftCapture(false)) { + for (self.mouse.click_state) |state| { + if (state != .release) break :report; + } + } + + // We use the first mouse button we find pressed in order to report + // since the spec (afaict) does not say... + const button: ?input.MouseButton = button: for (self.mouse.click_state, 0..) |state, i| { + if (state == .press) + break :button @enumFromInt(i); + } else null; + + self.mouseReport(button, .motion, self.mouse.mods, pos); + + // If we're doing mouse motion tracking, we do not support text + // selection. + return; + } + + // Handle cursor position for text selection + if (self.mouse.click_state[@intFromEnum(input.MouseButton.left)] == .press) select: { + // Left click pressed but count zero can happen if mouse reporting is on. + // In this scenario, we mark the click state because we need that to + // properly make some mouse reports, but we don't keep track of the + // count because we don't want to handle selection. + if (self.mouse.left_click_count == 0) break :select; + + // If our terminal screen changed then we don't process this. We don't + // invalidate our pin or mouse state because if the screen switches + // back then we can continue our selection. + const t: *terminal.Terminal = self.renderer_state.terminal; + if (self.mouse.left_click_screen != t.screens.active_key) break :select; + + // All roads lead to requiring a re-render at this point. + try self.queueRender(); + + // If our y is negative, we're above the window. In this case, we scroll + // up. The amount we scroll up is dependent on how negative we are. + // We allow for a 1 pixel buffer at the top and bottom to detect + // scroll even in full screen windows. + // Note: one day, we can change this from distance to time based if we want. + //log.warn("CURSOR POS: {} {}", .{ pos, self.size.screen }); + const max_y: f32 = @floatFromInt(self.size.screen.height); + + // If the mouse is outside the viewport and we have the left + // mouse button pressed then we need to start the scroll timer. + if ((pos.y <= 1 or pos.y > max_y - 1) and + !self.selection_scroll_active) + { + self.queueIo( + .{ .selection_scroll = true }, + .locked, + ); + } + + // Convert to points + const screen: *terminal.Screen = t.screens.active; + const pin = screen.pages.pin(.{ + .viewport = .{ + .x = pos_vp.x, + .y = pos_vp.y, + }, + }) orelse { + if (comptime std.debug.runtime_safety) unreachable; + return; + }; + + // Handle dragging depending on click count + switch (self.mouse.left_click_count) { + 1 => try self.dragLeftClickSingle(pin, pos.x), + 2 => try self.dragLeftClickDouble(pin), + 3 => try self.dragLeftClickTriple(pin), + 0 => unreachable, // handled above + else => unreachable, + } + + return; + } +} + +/// Double-click dragging moves the selection one "word" at a time. +fn dragLeftClickDouble( + self: *Surface, + drag_pin: terminal.Pin, +) !void { + const screen: *terminal.Screen = self.io.terminal.screens.active; + const click_pin = self.mouse.left_click_pin.?.*; + + // Get the word closest to our starting click. + const word_start = screen.selectWordBetween( + click_pin, + drag_pin, + self.config.selection_word_chars, + ) orelse { + try self.setSelection(null); + return; + }; + + // Get the word closest to our current point. + const word_current = screen.selectWordBetween( + drag_pin, + click_pin, + self.config.selection_word_chars, + ) orelse { + try self.setSelection(null); + return; + }; + + // If our current mouse position is before the starting position, + // then the selection start is the word nearest our current position. + if (drag_pin.before(click_pin)) { + try self.io.terminal.screens.active.select(.init( + word_current.start(), + word_start.end(), + false, + )); + } else { + try self.io.terminal.screens.active.select(.init( + word_start.start(), + word_current.end(), + false, + )); + } +} + +/// Triple-click dragging moves the selection one "line" at a time. +fn dragLeftClickTriple( + self: *Surface, + drag_pin: terminal.Pin, +) !void { + const screen: *terminal.Screen = self.io.terminal.screens.active; + const click_pin = self.mouse.left_click_pin.?.*; + + // Get the line selection under our current drag point. If there isn't a + // line, do nothing. + const line = screen.selectLine(.{ .pin = drag_pin }) orelse return; + + // Get the selection under our click point. We first try to trim + // whitespace if we've selected a word. But if no word exists then + // we select the blank line. + const sel_ = screen.selectLine(.{ .pin = click_pin }) orelse + screen.selectLine(.{ .pin = click_pin, .whitespace = null }); + + var sel = sel_ orelse return; + if (drag_pin.before(click_pin)) { + sel.startPtr().* = line.start(); + } else { + sel.endPtr().* = line.end(); + } + try self.io.terminal.screens.active.select(sel); +} + +fn dragLeftClickSingle( + self: *Surface, + drag_pin: terminal.Pin, + drag_x: f64, +) !void { + // This logic is in a separate function so that it can be unit tested. + try self.io.terminal.screens.active.select(mouseSelection( + self.mouse.left_click_pin.?.*, + drag_pin, + @intFromFloat(@max(0.0, self.mouse.left_click_xpos)), + @intFromFloat(@max(0.0, drag_x)), + self.mouse.mods, + self.size, + )); +} + +/// Calculates the appropriate selection given pins and pixel x positions for +/// the click point and the drag point, as well as mouse mods and screen size. +fn mouseSelection( + click_pin: terminal.Pin, + drag_pin: terminal.Pin, + click_x: u32, + drag_x: u32, + mods: input.Mods, + size: rendererpkg.Size, +) ?terminal.Selection { + // Explanation: + // + // # Normal selections + // + // ## Left-to-right selections + // - The clicked cell is included if it was clicked to the left of its + // threshold point and the drag location is right of the threshold point. + // - The cell under the cursor (the "drag cell") is included if the drag + // location is right of its threshold point. + // + // ## Right-to-left selections + // - The clicked cell is included if it was clicked to the right of its + // threshold point and the drag location is left of the threshold point. + // - The cell under the cursor (the "drag cell") is included if the drag + // location is left of its threshold point. + // + // # Rectangular selections + // + // Rectangular selections are handled similarly, except that + // entire columns are considered rather than individual cells. + + // We only include cells in the selection if the threshold point lies + // between the start and end points of the selection. A threshold of + // 60% of the cell width was chosen empirically because it felt good. + const threshold_point: u32 = @intFromFloat(@round( + @as(f64, @floatFromInt(size.cell.width)) * 0.6, + )); + + // We use this to clamp the pixel positions below. + const max_x = size.grid().columns * size.cell.width - 1; + + // We need to know how far across in the cell the drag pos is, so + // we subtract the padding and then take it modulo the cell width. + const drag_x_frac = @min(max_x, drag_x -| size.padding.left) % size.cell.width; + + // We figure out the fractional part of the click x position similarly. + const click_x_frac = @min(max_x, click_x -| size.padding.left) % size.cell.width; + + // Whether or not this is a rectangular selection. + const rectangle_selection = SurfaceMouse.isRectangleSelectState(mods); + + // Whether the click pin and drag pin are equal. + const same_pin = drag_pin.eql(click_pin); + + // Whether or not the end point of our selection is before the start point. + const end_before_start = ebs: { + if (same_pin) { + break :ebs drag_x_frac < click_x_frac; + } + + // Special handling for rectangular selections, we only use x position. + if (rectangle_selection) { + break :ebs switch (std.math.order(drag_pin.x, click_pin.x)) { + .eq => drag_x_frac < click_x_frac, + .lt => true, + .gt => false, + }; + } + + break :ebs drag_pin.before(click_pin); + }; + + // Whether or not the click pin cell + // should be included in the selection. + const include_click_cell = if (end_before_start) + click_x_frac >= threshold_point + else + click_x_frac < threshold_point; + + // Whether or not the drag pin cell + // should be included in the selection. + const include_drag_cell = if (end_before_start) + drag_x_frac < threshold_point + else + drag_x_frac >= threshold_point; + + // If the click cell should be included in the selection then it's the + // start, otherwise we get the previous or next cell to it depending on + // the type and direction of the selection. + const start_pin = + if (include_click_cell) + click_pin + else if (end_before_start) + if (rectangle_selection) + click_pin.leftClamp(1) + else + click_pin.leftWrap(1) orelse click_pin + else if (rectangle_selection) + click_pin.rightClamp(1) + else + click_pin.rightWrap(1) orelse click_pin; + + // Likewise for the end pin with the drag cell. + const end_pin = + if (include_drag_cell) + drag_pin + else if (end_before_start) + if (rectangle_selection) + drag_pin.rightClamp(1) + else + drag_pin.rightWrap(1) orelse drag_pin + else if (rectangle_selection) + drag_pin.leftClamp(1) + else + drag_pin.leftWrap(1) orelse drag_pin; + + // If the click cell is the same as the drag cell and the click cell + // shouldn't be included, or if the cells are adjacent such that the + // start or end pin becomes the other cell, and that cell should not + // be included, then we have no selection, so we set it to null. + // + // If in rectangular selection mode, we compare columns as well. + // + // TODO(qwerasd): this can/should probably be refactored, it's a bit + // repetitive and does excess work in rectangle mode. + if ((!include_click_cell and same_pin) or + (!include_click_cell and rectangle_selection and click_pin.x == drag_pin.x) or + (!include_click_cell and end_pin.eql(click_pin)) or + (!include_click_cell and rectangle_selection and end_pin.x == click_pin.x) or + (!include_drag_cell and start_pin.eql(drag_pin)) or + (!include_drag_cell and rectangle_selection and start_pin.x == drag_pin.x)) + { + return null; + } + + // TODO: Clamp selection to the screen area, don't + // let it extend past the last written row. + + return .init( + start_pin, + end_pin, + rectangle_selection, + ); +} + +/// Call to notify Ghostty that the color scheme for the terminal has +/// changed. +pub fn colorSchemeCallback(self: *Surface, scheme: apprt.ColorScheme) !void { + // Crash metadata in case we crash in here + crash.sentry.thread_state = self.crashThreadState(); + defer crash.sentry.thread_state = null; + + const new_scheme: configpkg.ConditionalState.Theme = switch (scheme) { + .light => .light, + .dark => .dark, + }; + + // If our scheme didn't change, then we don't do anything. + if (self.config_conditional_state.theme == new_scheme) return; + + // Setup our conditional state which has the current color theme. + self.config_conditional_state.theme = new_scheme; + self.notifyConfigConditionalState(); + + // If mode 2031 is on, then we report the change live. + self.queueIo(.{ .color_scheme_report = .{ .force = false } }, .unlocked); +} + +pub fn posToViewport(self: Surface, xpos: f64, ypos: f64) terminal.point.Coordinate { + // Get our grid cell + const coord: rendererpkg.Coordinate = .{ .surface = .{ .x = xpos, .y = ypos } }; + const grid = coord.convert(.grid, self.size).grid; + return .{ .x = grid.x, .y = grid.y }; +} + +/// Scroll to the bottom of the viewport. +/// +/// Precondition: the render_state mutex must be held. +fn scrollToBottom(self: *Surface) !void { + self.io.terminal.scrollViewport(.{ .bottom = {} }); + try self.queueRender(); +} + +fn hideMouse(self: *Surface) void { + if (self.mouse.hidden) return; + self.mouse.hidden = true; + _ = self.rt_app.performAction( + .{ .surface = self }, + .mouse_visibility, + .hidden, + ) catch |err| { + log.warn("apprt failed to set mouse visibility err={}", .{err}); + }; +} + +fn showMouse(self: *Surface) void { + if (!self.mouse.hidden) return; + self.mouse.hidden = false; + _ = self.rt_app.performAction( + .{ .surface = self }, + .mouse_visibility, + .visible, + ) catch |err| { + log.warn("apprt failed to set mouse visibility err={}", .{err}); + }; +} + +/// Perform a binding action. A binding is a keybinding. This function +/// must be called from the GUI thread. +/// +/// This function returns true if the binding action was performed. This +/// may return false if the binding action is not supported or if the +/// binding action would do nothing (i.e. previous tab with no tabs). +/// +/// NOTE: At the time of writing this comment, only previous/next tab +/// will ever return false. We can expand this in the future if it becomes +/// useful. We did previous/next tab so we could implement #498. +pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool { + // Forward app-scoped actions to the app. Some app-scoped actions are + // special-cased here because they do some special things when performed + // from the surface. + if (action.scoped(.app)) |app_action| { + switch (app_action) { + .new_window => try self.app.newWindow( + self.rt_app, + .{ .parent = self }, + ), + + // Undo and redo both support both surface and app targeting. + // If we are triggering on a surface then we perform the + // action with the surface target. + .undo => return try self.rt_app.performAction( + .{ .surface = self }, + .undo, + {}, + ), + + .redo => return try self.rt_app.performAction( + .{ .surface = self }, + .redo, + {}, + ), + + else => try self.app.performAction( + self.rt_app, + action.scoped(.app).?, + ), + } + return true; + } + + switch (action.scoped(.surface).?) { + .csi, .esc => |data| { + // We need to send the CSI/ESC sequence as a single write request. + // If you split it across two then the shell can interpret it + // as two literals. + var buf: [128]u8 = undefined; + const full_data = switch (action) { + .csi => try std.fmt.bufPrint(&buf, "\x1b[{s}", .{data}), + .esc => try std.fmt.bufPrint(&buf, "\x1b{s}", .{data}), + else => unreachable, + }; + self.queueIo(try termio.Message.writeReq( + self.alloc, + full_data, + ), .unlocked); + + // CSI/ESC triggers a scroll. + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + self.scrollToBottom() catch |err| { + log.warn("error scrolling to bottom err={}", .{err}); + }; + } + }, + + .text => |data| { + // For text we always allocate just because its easier to + // handle all cases that way. + const buf = try self.alloc.alloc(u8, data.len); + defer self.alloc.free(buf); + const text = configpkg.string.parse(buf, data) catch |err| { + log.warn( + "error parsing text binding text={s} err={}", + .{ data, err }, + ); + return true; + }; + self.queueIo(try termio.Message.writeReq( + self.alloc, + text, + ), .unlocked); + + // Text triggers a scroll. + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + self.scrollToBottom() catch |err| { + log.warn("error scrolling to bottom err={}", .{err}); + }; + } + }, + + .cursor_key => |ck| { + // We send a different sequence depending on if we're + // in cursor keys mode. We're in "normal" mode if cursor + // keys mode is NOT set. + const normal = normal: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // With the lock held, we must scroll to the bottom. + // We always scroll to the bottom for these inputs. + self.scrollToBottom() catch |err| { + log.warn("error scrolling to bottom err={}", .{err}); + }; + + break :normal !self.io.terminal.modes.get(.cursor_keys); + }; + + if (normal) { + self.queueIo(.{ .write_stable = ck.normal }, .unlocked); + } else { + self.queueIo(.{ .write_stable = ck.application }, .unlocked); + } + }, + + .reset => { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + self.renderer_state.terminal.fullReset(); + }, + + .start_search => { + // To save resources, we don't actually start a search here, + // we just notify the apprt. The real thread will start when + // the first needles are set. + return try self.rt_app.performAction( + .{ .surface = self }, + .start_search, + .{ .needle = "" }, + ); + }, + + .search_selection => { + const selection = try self.selectionString(self.alloc) orelse return false; + defer self.alloc.free(selection); + return try self.rt_app.performAction( + .{ .surface = self }, + .start_search, + .{ .needle = selection }, + ); + }, + + .end_search => { + // We only return that this was performed if we actually + // stopped a search, but we also send the apprt end_search so + // that GUIs can clean up stale stuff. + const performed = self.search != null; + + if (self.search) |*s| { + s.deinit(); + self.search = null; + } + + _ = try self.rt_app.performAction( + .{ .surface = self }, + .end_search, + {}, + ); + + return performed; + }, + + .search => |text| search: { + const s: *Search = if (self.search) |*s| s else init: { + // If we're stopping the search and we had no prior search, + // then there is nothing to do. + if (text.len == 0) return false; + + // We need to assign directly to self.search because we need + // a stable pointer back to the thread state. + self.search = .{ + .state = try .init(self.alloc, .{ + .mutex = self.renderer_state.mutex, + .terminal = self.renderer_state.terminal, + .event_cb = &searchCallback, + .event_userdata = self, + }), + .thread = undefined, + }; + const s: *Search = &self.search.?; + errdefer s.state.deinit(); + + s.thread = try .spawn( + .{}, + terminal.search.Thread.threadMain, + .{&s.state}, + ); + s.thread.setName("search") catch {}; + + break :init s; + }; + + // Zero-length text means stop searching. + if (text.len == 0) { + s.deinit(); + self.search = null; + break :search; + } + + _ = s.state.mailbox.push( + .{ .change_needle = try .init( + self.alloc, + text, + ) }, + .forever, + ); + s.state.wakeup.notify() catch {}; + }, + + .navigate_search => |nav| { + const s: *Search = if (self.search) |*s| s else return false; + _ = s.state.mailbox.push( + .{ .select = switch (nav) { + .next => .next, + .previous => .prev, + } }, + .forever, + ); + s.state.wakeup.notify() catch {}; + }, + + .copy_to_clipboard => |format| { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + if (self.io.terminal.screens.active.selection) |sel| { + try self.copySelectionToClipboards( + sel, + &.{.standard}, + format, + ); + + // Clear the selection if configured to do so. + if (self.config.selection_clear_on_copy) { + if (self.setSelection(null)) { + self.queueRender() catch |err| { + log.warn("failed to queue render after clear selection err={}", .{err}); + }; + } else |err| { + log.warn("failed to clear selection after copy err={}", .{err}); + } + } + + return true; + } + + return false; + }, + + .copy_url_to_clipboard => { + // If the mouse isn't over a link, nothing we can do. + if (!self.mouse.over_link) return false; + const pos = try self.rt_surface.getCursorPos(); + + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + if (try self.linkAtPos(pos)) |link_info| { + const url_text = switch (link_info.action) { + .open => url_text: { + // For regex links, get the text from selection + break :url_text (self.io.terminal.screens.active.selectionString(self.alloc, .{ + .sel = link_info.selection, + .trim = self.config.clipboard_trim_trailing_spaces, + })) catch |err| { + log.err("error reading url string err={}", .{err}); + return false; + }; + }, + + ._open_osc8 => url_text: { + // For OSC8 links, get the URI directly from hyperlink data + const uri = self.osc8URI(link_info.selection.start()) orelse { + log.warn("failed to get URI for OSC8 hyperlink", .{}); + return false; + }; + break :url_text try self.alloc.dupeZ(u8, uri); + }, + }; + defer self.alloc.free(url_text); + + self.rt_surface.setClipboard(.standard, &.{.{ + .mime = "text/plain", + .data = url_text, + }}, false) catch |err| { + log.err("error copying url to clipboard err={}", .{err}); + return false; + }; + + return true; + } + + return false; + }, + + .copy_title_to_clipboard => return try self.rt_app.performAction( + .{ .surface = self }, + .copy_title_to_clipboard, + {}, + ), + + .paste_from_clipboard => return try self.startClipboardRequest( + .standard, + .{ .paste = {} }, + ), + + .paste_from_selection => return try self.startClipboardRequest( + .selection, + .{ .paste = {} }, + ), + + .increase_font_size => |delta| { + // Max delta is somewhat arbitrary. + const clamped_delta = @max(0, @min(255, delta)); + + log.debug("increase font size={}", .{clamped_delta}); + + // Max point size is somewhat arbitrary. + var size = self.font_size; + size.points = @min(size.points + clamped_delta, 255); + try self.setFontSize(size); + + // Mark that we manually adjusted the font size + self.font_size_adjusted = true; + }, + + .decrease_font_size => |delta| { + // Max delta is somewhat arbitrary. + const clamped_delta = @max(0, @min(255, delta)); + + log.debug("decrease font size={}", .{clamped_delta}); + + var size = self.font_size; + size.points = @max(1, size.points - clamped_delta); + try self.setFontSize(size); + + // Mark that we manually adjusted the font size + self.font_size_adjusted = true; + }, + + .reset_font_size => { + log.debug("reset font size", .{}); + + var size = self.font_size; + size.points = self.config.original_font_size; + try self.setFontSize(size); + + // Reset font size also resets the manual adjustment state + self.font_size_adjusted = false; + }, + + .set_font_size => |points| { + log.debug("set font size={d}", .{points}); + + var size = self.font_size; + size.points = std.math.clamp(points, 1.0, 255.0); + try self.setFontSize(size); + + // Mark that we manually adjusted the font size + self.font_size_adjusted = true; + }, + + .prompt_surface_title => return try self.rt_app.performAction( + .{ .surface = self }, + .prompt_title, + .surface, + ), + + .prompt_tab_title => return try self.rt_app.performAction( + .{ .surface = self }, + .prompt_title, + .tab, + ), + + .set_surface_title => |v| { + const title = try self.alloc.dupeZ(u8, v); + defer self.alloc.free(title); + return try self.rt_app.performAction( + .{ .surface = self }, + .set_title, + .{ .title = title }, + ); + }, + + .set_tab_title => |v| { + const title = try self.alloc.dupeZ(u8, v); + defer self.alloc.free(title); + return try self.rt_app.performAction( + .{ .surface = self }, + .set_tab_title, + .{ .title = title }, + ); + }, + + .clear_screen => { + // This is a duplicate of some of the logic in termio.clearScreen + // but we need to do this here so we can know the answer before + // we send the message. If the currently active screen is on the + // alternate screen then clear screen does nothing so we want to + // return false so the keybind can be unconsumed. + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + if (self.io.terminal.screens.active_key == .alternate) return false; + } + + self.queueIo(.{ + .clear_screen = .{ .history = true }, + }, .unlocked); + }, + + .scroll_to_top => { + self.queueIo(.{ + .scroll_viewport = .{ .top = {} }, + }, .unlocked); + }, + + .scroll_to_bottom => { + self.queueIo(.{ + .scroll_viewport = .{ .bottom = {} }, + }, .unlocked); + }, + + .scroll_to_row => |n| { + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const t: *terminal.Terminal = self.renderer_state.terminal; + t.screens.active.scroll(.{ .row = n }); + } + + try self.queueRender(); + }, + + .scroll_to_selection => { + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const sel = self.io.terminal.screens.active.selection orelse return false; + const tl = sel.topLeft(self.io.terminal.screens.active); + self.io.terminal.screens.active.scroll(.{ .pin = tl }); + } + + try self.queueRender(); + }, + + .scroll_page_up => { + const rows: isize = @intCast(self.size.grid().rows); + self.queueIo(.{ + .scroll_viewport = .{ .delta = -1 * rows }, + }, .unlocked); + }, + + .scroll_page_down => { + const rows: isize = @intCast(self.size.grid().rows); + self.queueIo(.{ + .scroll_viewport = .{ .delta = rows }, + }, .unlocked); + }, + + .scroll_page_fractional => |fraction| { + const rows: f32 = @floatFromInt(self.size.grid().rows); + const delta: isize = @intFromFloat(@trunc(fraction * rows)); + self.queueIo(.{ + .scroll_viewport = .{ .delta = delta }, + }, .unlocked); + }, + + .scroll_page_lines => |lines| { + self.queueIo(.{ + .scroll_viewport = .{ .delta = lines }, + }, .unlocked); + }, + + .jump_to_prompt => |delta| { + self.queueIo(.{ + .jump_to_prompt = @intCast(delta), + }, .unlocked); + }, + + .write_screen_file => |v| try self.writeScreenFile( + .screen, + v, + ), + + .write_scrollback_file => |v| try self.writeScreenFile( + .history, + v, + ), + + .write_selection_file => |v| try self.writeScreenFile( + .selection, + v, + ), + + .new_tab => return try self.rt_app.performAction( + .{ .surface = self }, + .new_tab, + {}, + ), + + .close_tab => |v| return try self.rt_app.performAction( + .{ .surface = self }, + .close_tab, + switch (v) { + .this => .this, + .other => .other, + .right => .right, + }, + ), + + inline .previous_tab, + .next_tab, + .last_tab, + .goto_tab, + => |v, tag| return try self.rt_app.performAction( + .{ .surface = self }, + .goto_tab, + switch (tag) { + .previous_tab => .previous, + .next_tab => .next, + .last_tab => .last, + .goto_tab => @enumFromInt(v), + else => comptime unreachable, + }, + ), + + .move_tab => |position| return try self.rt_app.performAction( + .{ .surface = self }, + .move_tab, + .{ .amount = position }, + ), + + .new_split => |direction| return try self.rt_app.performAction( + .{ .surface = self }, + .new_split, + switch (direction) { + .right => .right, + .left => .left, + .down => .down, + .up => .up, + .auto => if (self.size.screen.width > self.size.screen.height) + .right + else + .down, + }, + ), + + .goto_split => |direction| return try self.rt_app.performAction( + .{ .surface = self }, + .goto_split, + switch (direction) { + inline else => |tag| @field( + apprt.action.GotoSplit, + @tagName(tag), + ), + }, + ), + + .goto_window => |direction| return try self.rt_app.performAction( + .{ .surface = self }, + .goto_window, + switch (direction) { + .previous => .previous, + .next => .next, + }, + ), + + .resize_split => |value| return try self.rt_app.performAction( + .{ .surface = self }, + .resize_split, + .{ + .amount = value[1], + .direction = switch (value[0]) { + inline else => |tag| @field( + apprt.action.ResizeSplit.Direction, + @tagName(tag), + ), + }, + }, + ), + + .equalize_splits => return try self.rt_app.performAction( + .{ .surface = self }, + .equalize_splits, + {}, + ), + + .toggle_split_zoom => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_split_zoom, + {}, + ), + + .toggle_readonly => { + self.readonly = !self.readonly; + _ = try self.rt_app.performAction( + .{ .surface = self }, + .readonly, + if (self.readonly) .on else .off, + ); + return true; + }, + + .reset_window_size => return try self.rt_app.performAction( + .{ .surface = self }, + .reset_window_size, + {}, + ), + + .toggle_maximize => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_maximize, + {}, + ), + + .toggle_fullscreen => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_fullscreen, + switch (self.config.macos_non_native_fullscreen) { + .false => .native, + .true => .macos_non_native, + .@"visible-menu" => .macos_non_native_visible_menu, + .@"padded-notch" => .macos_non_native_padded_notch, + }, + ), + + .toggle_window_decorations => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_window_decorations, + {}, + ), + + .toggle_tab_overview => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_tab_overview, + {}, + ), + + .toggle_window_float_on_top => return try self.rt_app.performAction( + .{ .surface = self }, + .float_window, + .toggle, + ), + + .toggle_secure_input => return try self.rt_app.performAction( + .{ .surface = self }, + .secure_input, + .toggle, + ), + + .toggle_mouse_reporting => { + self.config.mouse_reporting = !self.config.mouse_reporting; + log.debug("mouse reporting toggled: {}", .{self.config.mouse_reporting}); + }, + + .toggle_command_palette => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_command_palette, + {}, + ), + + .toggle_background_opacity => return try self.rt_app.performAction( + .{ .surface = self }, + .toggle_background_opacity, + {}, + ), + + .show_on_screen_keyboard => return try self.rt_app.performAction( + .{ .surface = self }, + .show_on_screen_keyboard, + {}, + ), + + .select_all => { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + const sel = self.io.terminal.screens.active.selectAll(); + if (sel) |s| { + try self.setSelection(s); + try self.queueRender(); + } + }, + + .inspector => |mode| return try self.rt_app.performAction( + .{ .surface = self }, + .inspector, + switch (mode) { + inline else => |tag| @field( + apprt.action.Inspector, + @tagName(tag), + ), + }, + ), + + .close_surface => self.close(), + + .close_window => return try self.rt_app.performAction( + .{ .surface = self }, + .close_window, + {}, + ), + + inline .activate_key_table, + .activate_key_table_once, + => |name, tag| { + // Look up the table in our config + const set = self.config.keybind.tables.getPtr(name) orelse { + log.debug("key table not found: {s}", .{name}); + return false; + }; + + // If this is the same table as is currently active, then + // do nothing. + if (self.keyboard.table_stack.items.len > 0) { + const items = self.keyboard.table_stack.items; + const active = items[items.len - 1].set; + if (active == set) { + log.debug("ignoring duplicate activate table: {s}", .{name}); + return false; + } + } + + // If we're already at the max, ignore it. + if (self.keyboard.table_stack.items.len >= max_active_key_tables) { + log.info( + "ignoring activate table, max depth reached: {s}", + .{name}, + ); + return false; + } + + // Add the table to the stack. + try self.keyboard.table_stack.append(self.alloc, .{ + .set = set, + .once = tag == .activate_key_table_once, + }); + + // Notify the UI. + _ = self.rt_app.performAction( + .{ .surface = self }, + .key_table, + .{ .activate = name }, + ) catch |err| { + log.warn( + "failed to notify app of key table err={}", + .{err}, + ); + }; + + log.debug("key table activated: {s}", .{name}); + }, + + .deactivate_key_table => { + switch (self.keyboard.table_stack.items.len) { + // No key table active. This does nothing. + 0 => return false, + + // Final key table active, clear our state. + 1 => self.keyboard.table_stack.clearAndFree(self.alloc), + + // Restore the prior key table. We don't free any memory in + // this case because we assume it will be freed later when + // we finish our key table. + else => _ = self.keyboard.table_stack.pop(), + } + + // Notify the UI. + _ = self.rt_app.performAction( + .{ .surface = self }, + .key_table, + .deactivate, + ) catch |err| { + log.warn( + "failed to notify app of key table err={}", + .{err}, + ); + }; + }, + + .deactivate_all_key_tables => { + return try self.deactivateAllKeyTables(); + }, + + .end_key_sequence => { + // End the key sequence and flush queued keys to the terminal, + // but don't encode the key that triggered this action. This + // will do that because leaf keys (keys with bindings) aren't + // in the queued encoding list. + self.endKeySequence(.flush, .retain); + }, + + .crash => |location| switch (location) { + .main => @panic("crash binding action, crashing intentionally"), + + .render => { + _ = self.renderer_thread.mailbox.push(.{ .crash = {} }, .{ .forever = {} }); + self.queueRender() catch |err| { + // Not a big deal if this fails. + log.warn("failed to notify renderer of crash message err={}", .{err}); + }; + }, + + .io => self.queueIo(.{ .crash = {} }, .unlocked), + }, + + .adjust_selection => |direction| { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + const screen: *terminal.Screen = self.io.terminal.screens.active; + const sel = if (screen.selection) |*sel| sel else { + // If we don't have a selection we do not perform this + // action, allowing the keybind to fall through to the + // terminal. + return false; + }; + sel.adjust(screen, switch (direction) { + .left => .left, + .right => .right, + .up => .up, + .down => .down, + .page_up => .page_up, + .page_down => .page_down, + .home => .home, + .end => .end, + .beginning_of_line => .beginning_of_line, + .end_of_line => .end_of_line, + }); + + // If the selection endpoint is outside of the current viewpoint, + // scroll it in to view. Note we always specifically use sel.end + // because that is what adjust modifies. + scroll: { + const viewport_tl = screen.pages.getTopLeft(.viewport); + const viewport_br = screen.pages.getBottomRight(.viewport).?; + if (sel.end().isBetween(viewport_tl, viewport_br)) + break :scroll; + + // Our end point is not within the viewport. If the end + // point is after the br then we need to adjust the end so + // that it is at the bottom right of the viewport. + const target = if (sel.end().before(viewport_tl)) + sel.end() + else + sel.end().up(screen.pages.rows - 1) orelse sel.end(); + + screen.scroll(.{ .pin = target }); + } + + // Queue a render so its shown + screen.dirty.selection = true; + try self.queueRender(); + }, + } + + return true; +} + +/// Returns true if performing the given action result in closing +/// the surface. This is used to determine if our self pointer is +/// still valid after performing some binding action. +fn closingAction(action: input.Binding.Action) bool { + return switch (action) { + .close_surface, + .close_window, + .close_tab, + => true, + + else => false, + }; +} + +/// The portion of the screen to write for writeScreenFile. +const WriteScreenLoc = enum { + screen, // Full screen + history, // History (scrollback) + selection, // Selected text +}; + +fn writeScreenFile( + self: *Surface, + loc: WriteScreenLoc, + write_screen: input.Binding.Action.WriteScreen, +) !void { + // Create a temporary directory to store our scrollback. + var tmp_dir = try internal_os.TempDir.init(); + errdefer tmp_dir.deinit(); + + var filename_buf: [std.fs.max_path_bytes]u8 = undefined; + const filename = try std.fmt.bufPrint( + &filename_buf, + "{s}.{s}", + .{ + @tagName(loc), + switch (write_screen.emit) { + .plain, .vt => "txt", + .html => "html", + }, + }, + ); + + // Open our scrollback file + var file = try tmp_dir.dir.createFile( + filename, + switch (builtin.os.tag) { + .windows => .{}, + else => .{ .mode = 0o600 }, + }, + ); + defer file.close(); + + // Screen.dumpString writes byte-by-byte, so buffer it + var buf: [4096]u8 = undefined; + var file_writer = file.writer(&buf); + var buf_writer = &file_writer.interface; + + // Write the scrollback contents. This requires a lock. + { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + // We only dump history if we have history. We still keep + // the file and write the empty file to the pty so that this + // command always works on the primary screen. + const pages = &self.io.terminal.screens.active.pages; + const sel_: ?terminal.Selection = switch (loc) { + .history => history: { + // We do not support this for alternate screens + // because they don't have scrollback anyways. + if (self.io.terminal.screens.active_key == .alternate) { + break :history null; + } + + break :history terminal.Selection.init( + pages.getTopLeft(.history), + pages.getBottomRight(.history) orelse + break :history null, + false, + ); + }, + + .screen => screen: { + break :screen terminal.Selection.init( + pages.getTopLeft(.screen), + pages.getBottomRight(.screen) orelse + break :screen null, + false, + ); + }, + + .selection => self.io.terminal.screens.active.selection, + }; + + const sel = sel_ orelse { + // If we have no selection we have no data so we do nothing. + tmp_dir.deinit(); + return; + }; + + const ScreenFormatter = terminal.formatter.ScreenFormatter; + var formatter: ScreenFormatter = .init(self.io.terminal.screens.active, .{ + .emit = switch (write_screen.emit) { + .plain => .plain, + .vt => .vt, + .html => .html, + }, + .unwrap = true, + .trim = false, + .background = self.io.terminal.colors.background.get(), + .foreground = self.io.terminal.colors.foreground.get(), + .palette = &self.io.terminal.colors.palette.current, + }); + formatter.content = .{ .selection = sel.ordered( + self.io.terminal.screens.active, + .forward, + ) }; + try formatter.format(buf_writer); + } + try buf_writer.flush(); + + // Get the final path + var path_buf: [std.fs.max_path_bytes]u8 = undefined; + const path = try tmp_dir.dir.realpath(filename, &path_buf); + + switch (write_screen.action) { + .copy => { + const pathZ = try self.alloc.dupeZ(u8, path); + defer self.alloc.free(pathZ); + try self.rt_surface.setClipboard(.standard, &.{.{ + .mime = "text/plain", + .data = pathZ, + }}, false); + }, + .open => try self.openUrl(.{ + .kind = switch (write_screen.emit) { + .plain, .vt => .text, + .html => .html, + }, + .url = path, + }), + .paste => self.queueIo(try termio.Message.writeReq( + self.alloc, + path, + ), .unlocked), + } +} + +/// Call this to complete a clipboard request sent to apprt. This should +/// only be called once for each request. The data is immediately copied so +/// it is safe to free the data after this call. +/// +/// If `confirmed` is true then any clipboard confirmation prompts are skipped: +/// +/// - For "regular" pasting this means that unsafe pastes are allowed. Unsafe +/// data is defined as data that contains newlines, though this definition +/// may change later to detect other scenarios. +/// +/// - For OSC 52 reads and writes no prompt is shown to the user if +/// `confirmed` is true. +/// +/// If `confirmed` is false then this may return either an UnsafePaste or +/// UnauthorizedPaste error, depending on the type of clipboard request. +pub fn completeClipboardRequest( + self: *Surface, + req: apprt.ClipboardRequest, + data: [:0]const u8, + confirmed: bool, +) !void { + switch (req) { + .paste => try self.completeClipboardPaste(data, confirmed), + + .osc_52_read => |clipboard| try self.completeClipboardReadOSC52( + data, + clipboard, + confirmed, + ), + + .osc_52_write => |clipboard| try self.rt_surface.setClipboard(clipboard, &.{.{ + .mime = "text/plain", + .data = data, + }}, !confirmed), + } +} + +/// This starts a clipboard request, with some basic validation. For example, +/// an OSC 52 request is not actually requested if OSC 52 is disabled. +/// +/// Returns true if the request was started, false if it was not (e.g., clipboard +/// doesn't contain text for paste requests). This allows performable keybinds +/// to pass through when the action cannot be performed. +fn startClipboardRequest( + self: *Surface, + loc: apprt.Clipboard, + req: apprt.ClipboardRequest, +) !bool { + switch (req) { + .paste => {}, // always allowed + .osc_52_read => if (self.config.clipboard_read == .deny) { + log.info( + "application attempted to read clipboard, but 'clipboard-read' is set to deny", + .{}, + ); + return false; + }, + + // No clipboard write code paths travel through this function + .osc_52_write => unreachable, + } + + return try self.rt_surface.clipboardRequest(loc, req); +} + +fn completeClipboardPaste( + self: *Surface, + data: []const u8, + allow_unsafe: bool, +) !void { + if (data.len == 0) return; + + const encode_opts: input.paste.Options = encode_opts: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + const opts: input.paste.Options = .fromTerminal(&self.io.terminal); + + // If we have paste protection enabled, we detect unsafe pastes and return + // an error. The error approach allows apprt to attempt to complete the paste + // before falling back to requesting confirmation. + // + // We do not do this for bracketed pastes because bracketed pastes are + // by definition safe since they're framed. + const unsafe = unsafe: { + // If we've disabled paste protection then we always allow the paste. + if (!self.config.clipboard_paste_protection) break :unsafe false; + + // If we're allowed to paste unsafe data then we always allow the paste. + // This is set during confirmation usually. + if (allow_unsafe) break :unsafe false; + + if (opts.bracketed) { + // If we're bracketed and the paste contains and ending + // bracket then something naughty might be going on and we + // never trust it. + if (std.mem.indexOf(u8, data, "\x1B[201~") != null) break :unsafe true; + + // If we are bracketed and configured to trust that then the + // paste is not unsafe. + if (self.config.clipboard_paste_bracketed_safe) break :unsafe false; + } + + break :unsafe !input.paste.isSafe(data); + }; + + if (unsafe) { + log.info("potentially unsafe paste detected, rejecting until confirmation", .{}); + return error.UnsafePaste; + } + + // With the lock held, we must scroll to the bottom. + // We always scroll to the bottom for these inputs. + self.scrollToBottom() catch |err| { + log.warn("error scrolling to bottom err={}", .{err}); + }; + + break :encode_opts opts; + }; + + // Encode the data. In most cases this doesn't require any + // copies, so we optimize for that case. + var data_duped: ?[]u8 = null; + const vecs = input.paste.encode(data, encode_opts) catch |err| switch (err) { + error.MutableRequired => vecs: { + const buf: []u8 = try self.alloc.dupe(u8, data); + errdefer self.alloc.free(buf); + data_duped = buf; + break :vecs input.paste.encode(buf, encode_opts); + }, + }; + defer if (data_duped) |v| { + // This code path means the data did require a copy and mutation. + // We must free it. + self.alloc.free(v); + }; + + for (vecs) |vec| if (vec.len > 0) { + self.queueIo(try termio.Message.writeReq( + self.alloc, + vec, + ), .unlocked); + }; +} + +fn completeClipboardReadOSC52( + self: *Surface, + data: []const u8, + clipboard_type: apprt.Clipboard, + confirmed: bool, +) !void { + // We should never get here if clipboard-read is set to deny + assert(self.config.clipboard_read != .deny); + + // If clipboard-read is set to ask and we haven't confirmed with the user, + // do that now + if (self.config.clipboard_read == .ask and !confirmed) { + return error.UnauthorizedPaste; + } + + // Even if the clipboard data is empty we reply, since presumably + // the client app is expecting a reply. We first allocate our buffer. + // This must hold the base64 encoded data PLUS the OSC code surrounding it. + const enc = std.base64.standard.Encoder; + const size = enc.calcSize(data.len); + var buf = try self.alloc.alloc(u8, size + 9); // const for OSC + defer self.alloc.free(buf); + + const kind: u8 = switch (clipboard_type) { + .standard => 'c', + .selection => 's', + .primary => 'p', + }; + + // Wrap our data with the OSC code + const prefix = try std.fmt.bufPrint(buf, "\x1b]52;{c};", .{kind}); + assert(prefix.len == 7); + buf[buf.len - 2] = '\x1b'; + buf[buf.len - 1] = '\\'; + + // Do the base64 encoding + const encoded = enc.encode(buf[prefix.len..], data); + assert(encoded.len == size); + + self.queueIo(try termio.Message.writeReq( + self.alloc, + buf, + ), .unlocked); +} + +fn showDesktopNotification(self: *Surface, title: [:0]const u8, body: [:0]const u8) !void { + // Wyhash is used to hash the contents of the desktop notification to limit + // how fast identical notifications can be sent sequentially. + const hash_algorithm = std.hash.Wyhash; + + const now = try std.time.Instant.now(); + + // Set a limit of one desktop notification per second so that the OS + // doesn't kill us when we run out of resources. + if (self.app.last_notification_time) |last| { + if (now.since(last) < 1 * std.time.ns_per_s) { + log.warn("rate limiting desktop notifications", .{}); + return; + } + } + + const new_digest = d: { + var hash = hash_algorithm.init(0); + hash.update(title); + hash.update(body); + break :d hash.final(); + }; + + // Set a limit of one notification per five seconds for desktop + // notifications with identical content. + if (self.app.last_notification_time) |last| { + if (self.app.last_notification_digest == new_digest) { + if (now.since(last) < 5 * std.time.ns_per_s) { + log.warn("suppressing identical desktop notification", .{}); + return; + } + } + } + + self.app.last_notification_time = now; + self.app.last_notification_digest = new_digest; + _ = try self.rt_app.performAction( + .{ .surface = self }, + .desktop_notification, + .{ + .title = title, + .body = body, + }, + ); +} + +fn crashThreadState(self: *Surface) crash.sentry.ThreadState { + return .{ + .type = .main, + .surface = self, + }; +} + +/// Tell the surface to present itself to the user. This may involve raising the +/// window and switching tabs. +fn presentSurface(self: *Surface) !void { + _ = try self.rt_app.performAction( + .{ .surface = self }, + .present_terminal, + {}, + ); +} + +/// Utility function for the unit tests for mouse selection logic. +/// +/// Tests a click and drag on a 10x5 cell grid, x positions are given in +/// fractional cells, e.g. 3.1 would be 10% through the cell at x = 3. +/// +/// NOTE: The size tested with has 10px wide cells, meaning only one digit +/// after the decimal place has any meaning, e.g. 3.14 is equal to 3.1. +/// +/// The provided start_x/y and end_x/y are the expected start and end points +/// of the resulting selection. +fn testMouseSelection( + click_x: f64, + click_y: u32, + drag_x: f64, + drag_y: u32, + start_x: terminal.size.CellCountInt, + start_y: u32, + end_x: terminal.size.CellCountInt, + end_y: u32, + rect: bool, +) !void { + assert(builtin.is_test); + + // Our screen size is 10x5 cells that are + // 10x20 px, with 5px padding on all sides. + const size: rendererpkg.Size = .{ + .cell = .{ .width = 10, .height = 20 }, + .padding = .{ .left = 5, .top = 5, .right = 5, .bottom = 5 }, + .screen = .{ .width = 110, .height = 110 }, + }; + var screen = try terminal.Screen.init(std.testing.allocator, .{ .cols = 10, .rows = 5, .max_scrollback = 0 }); + defer screen.deinit(); + + // We hold both ctrl and alt for rectangular + // select so that this test is platform agnostic. + const mods: input.Mods = .{ + .ctrl = rect, + .alt = rect, + }; + + try std.testing.expectEqual(rect, SurfaceMouse.isRectangleSelectState(mods)); + + const click_pin = screen.pages.pin(.{ + .viewport = .{ .x = @intFromFloat(@floor(click_x)), .y = click_y }, + }) orelse unreachable; + const drag_pin = screen.pages.pin(.{ + .viewport = .{ .x = @intFromFloat(@floor(drag_x)), .y = drag_y }, + }) orelse unreachable; + + const cell_width_f64: f64 = @floatFromInt(size.cell.width); + const click_x_pos: u32 = + @as(u32, @intFromFloat(@floor(click_x * cell_width_f64))) + + size.padding.left; + const drag_x_pos: u32 = + @as(u32, @intFromFloat(@floor(drag_x * cell_width_f64))) + + size.padding.left; + + const start_pin = screen.pages.pin(.{ + .viewport = .{ .x = start_x, .y = start_y }, + }) orelse unreachable; + const end_pin = screen.pages.pin(.{ + .viewport = .{ .x = end_x, .y = end_y }, + }) orelse unreachable; + + try std.testing.expectEqualDeep(terminal.Selection{ + .bounds = .{ .untracked = .{ + .start = start_pin, + .end = end_pin, + } }, + .rectangle = rect, + }, mouseSelection( + click_pin, + drag_pin, + click_x_pos, + drag_x_pos, + mods, + size, + )); +} + +/// Like `testMouseSelection` but checks that the resulting selection is null. +/// +/// See `testMouseSelection` for more details. +fn testMouseSelectionIsNull( + click_x: f64, + click_y: u32, + drag_x: f64, + drag_y: u32, + rect: bool, +) !void { + assert(builtin.is_test); + + // Our screen size is 10x5 cells that are + // 10x20 px, with 5px padding on all sides. + const size: rendererpkg.Size = .{ + .cell = .{ .width = 10, .height = 20 }, + .padding = .{ .left = 5, .top = 5, .right = 5, .bottom = 5 }, + .screen = .{ .width = 110, .height = 110 }, + }; + var screen = try terminal.Screen.init(std.testing.allocator, .{ .cols = 10, .rows = 5, .max_scrollback = 0 }); + defer screen.deinit(); + + // We hold both ctrl and alt for rectangular + // select so that this test is platform agnostic. + const mods: input.Mods = .{ + .ctrl = rect, + .alt = rect, + }; + + try std.testing.expectEqual(rect, SurfaceMouse.isRectangleSelectState(mods)); + + const click_pin = screen.pages.pin(.{ + .viewport = .{ .x = @intFromFloat(@floor(click_x)), .y = click_y }, + }) orelse unreachable; + const drag_pin = screen.pages.pin(.{ + .viewport = .{ .x = @intFromFloat(@floor(drag_x)), .y = drag_y }, + }) orelse unreachable; + + const cell_width_f64: f64 = @floatFromInt(size.cell.width); + const click_x_pos: u32 = + @as(u32, @intFromFloat(@floor(click_x * cell_width_f64))) + + size.padding.left; + const drag_x_pos: u32 = + @as(u32, @intFromFloat(@floor(drag_x * cell_width_f64))) + + size.padding.left; + + try std.testing.expectEqual( + null, + mouseSelection( + click_pin, + drag_pin, + click_x_pos, + drag_x_pos, + mods, + size, + ), + ); +} + +/// Get information about the process(es) running within the surface. Returns +/// `null` if there was an error getting the information or the information is +/// not available on a particular platform. +pub fn getProcessInfo(self: *Surface, comptime info: ProcessInfo) ?ProcessInfo.Type(info) { + return self.io.getProcessInfo(info); +} + +test "Surface: selection logic" { + // We disable format to make these easier to + // read by pairing sets of coordinates per line. + // zig fmt: off + + // -- LTR + // single cell selection + try testMouseSelection( + 3.0, 3, // click + 3.9, 3, // drag + 3, 3, // expected start + 3, 3, // expected end + false, // regular selection + ); + // including click and drag pin cells + try testMouseSelection( + 3.0, 3, // click + 5.9, 3, // drag + 3, 3, // expected start + 5, 3, // expected end + false, // regular selection + ); + // including click pin cell but not drag pin cell + try testMouseSelection( + 3.0, 3, // click + 5.0, 3, // drag + 3, 3, // expected start + 4, 3, // expected end + false, // regular selection + ); + // including drag pin cell but not click pin cell + try testMouseSelection( + 3.9, 3, // click + 5.9, 3, // drag + 4, 3, // expected start + 5, 3, // expected end + false, // regular selection + ); + // including neither click nor drag pin cells + try testMouseSelection( + 3.9, 3, // click + 5.0, 3, // drag + 4, 3, // expected start + 4, 3, // expected end + false, // regular selection + ); + // empty selection (single cell on only left half) + try testMouseSelectionIsNull( + 3.0, 3, // click + 3.1, 3, // drag + false, // regular selection + ); + // empty selection (single cell on only right half) + try testMouseSelectionIsNull( + 3.8, 3, // click + 3.9, 3, // drag + false, // regular selection + ); + // empty selection (between two cells, not crossing threshold) + try testMouseSelectionIsNull( + 3.9, 3, // click + 4.0, 3, // drag + false, // regular selection + ); + + // -- RTL + // single cell selection + try testMouseSelection( + 3.9, 3, // click + 3.0, 3, // drag + 3, 3, // expected start + 3, 3, // expected end + false, // regular selection + ); + // including click and drag pin cells + try testMouseSelection( + 5.9, 3, // click + 3.0, 3, // drag + 5, 3, // expected start + 3, 3, // expected end + false, // regular selection + ); + // including click pin cell but not drag pin cell + try testMouseSelection( + 5.9, 3, // click + 3.9, 3, // drag + 5, 3, // expected start + 4, 3, // expected end + false, // regular selection + ); + // including drag pin cell but not click pin cell + try testMouseSelection( + 5.0, 3, // click + 3.0, 3, // drag + 4, 3, // expected start + 3, 3, // expected end + false, // regular selection + ); + // including neither click nor drag pin cells + try testMouseSelection( + 5.0, 3, // click + 3.9, 3, // drag + 4, 3, // expected start + 4, 3, // expected end + false, // regular selection + ); + // empty selection (single cell on only left half) + try testMouseSelectionIsNull( + 3.1, 3, // click + 3.0, 3, // drag + false, // regular selection + ); + // empty selection (single cell on only right half) + try testMouseSelectionIsNull( + 3.9, 3, // click + 3.8, 3, // drag + false, // regular selection + ); + // empty selection (between two cells, not crossing threshold) + try testMouseSelectionIsNull( + 4.0, 3, // click + 3.9, 3, // drag + false, // regular selection + ); + + // -- Wrapping + // LTR, wrap excluded cells + try testMouseSelection( + 9.9, 2, // click + 0.0, 4, // drag + 0, 3, // expected start + 9, 3, // expected end + false, // regular selection + ); + // RTL, wrap excluded cells + try testMouseSelection( + 0.0, 4, // click + 9.9, 2, // drag + 9, 3, // expected start + 0, 3, // expected end + false, // regular selection + ); +} + +test "Surface: rectangle selection logic" { + // We disable format to make these easier to + // read by pairing sets of coordinates per line. + // zig fmt: off + + // -- LTR + // single column selection + try testMouseSelection( + 3.0, 2, // click + 3.9, 4, // drag + 3, 2, // expected start + 3, 4, // expected end + true, //rectangle selection + ); + // including click and drag pin columns + try testMouseSelection( + 3.0, 2, // click + 5.9, 4, // drag + 3, 2, // expected start + 5, 4, // expected end + true, //rectangle selection + ); + // including click pin column but not drag pin column + try testMouseSelection( + 3.0, 2, // click + 5.0, 4, // drag + 3, 2, // expected start + 4, 4, // expected end + true, //rectangle selection + ); + // including drag pin column but not click pin column + try testMouseSelection( + 3.9, 2, // click + 5.9, 4, // drag + 4, 2, // expected start + 5, 4, // expected end + true, //rectangle selection + ); + // including neither click nor drag pin columns + try testMouseSelection( + 3.9, 2, // click + 5.0, 4, // drag + 4, 2, // expected start + 4, 4, // expected end + true, //rectangle selection + ); + // empty selection (single column on only left half) + try testMouseSelectionIsNull( + 3.0, 2, // click + 3.1, 4, // drag + true, //rectangle selection + ); + // empty selection (single column on only right half) + try testMouseSelectionIsNull( + 3.8, 2, // click + 3.9, 4, // drag + true, //rectangle selection + ); + // empty selection (between two columns, not crossing threshold) + try testMouseSelectionIsNull( + 3.9, 2, // click + 4.0, 4, // drag + true, //rectangle selection + ); + + // -- RTL + // single column selection + try testMouseSelection( + 3.9, 2, // click + 3.0, 4, // drag + 3, 2, // expected start + 3, 4, // expected end + true, //rectangle selection + ); + // including click and drag pin columns + try testMouseSelection( + 5.9, 2, // click + 3.0, 4, // drag + 5, 2, // expected start + 3, 4, // expected end + true, //rectangle selection + ); + // including click pin column but not drag pin column + try testMouseSelection( + 5.9, 2, // click + 3.9, 4, // drag + 5, 2, // expected start + 4, 4, // expected end + true, //rectangle selection + ); + // including drag pin column but not click pin column + try testMouseSelection( + 5.0, 2, // click + 3.0, 4, // drag + 4, 2, // expected start + 3, 4, // expected end + true, //rectangle selection + ); + // including neither click nor drag pin columns + try testMouseSelection( + 5.0, 2, // click + 3.9, 4, // drag + 4, 2, // expected start + 4, 4, // expected end + true, //rectangle selection + ); + // empty selection (single column on only left half) + try testMouseSelectionIsNull( + 3.1, 2, // click + 3.0, 4, // drag + true, //rectangle selection + ); + // empty selection (single column on only right half) + try testMouseSelectionIsNull( + 3.9, 2, // click + 3.8, 4, // drag + true, //rectangle selection + ); + // empty selection (between two columns, not crossing threshold) + try testMouseSelectionIsNull( + 4.0, 2, // click + 3.9, 4, // drag + true, //rectangle selection + ); + + // -- Wrapping + // LTR, do not wrap + try testMouseSelection( + 9.9, 2, // click + 0.0, 4, // drag + 9, 2, // expected start + 0, 4, // expected end + true, //rectangle selection + ); + // RTL, do not wrap + try testMouseSelection( + 0.0, 4, // click + 9.9, 2, // drag + 0, 4, // expected start + 9, 2, // expected end + true, //rectangle selection + ); +} diff --git a/vendor/libghostty-vt/src/apprt.zig b/vendor/libghostty-vt/src/apprt.zig new file mode 100644 index 00000000..c467f180 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt.zig @@ -0,0 +1,59 @@ +//! "apprt" is the "application runtime" package. This abstracts the +//! application runtime and lifecycle management such as creating windows, +//! getting user input (mouse/keyboard), etc. +//! +//! This enables compile-time interfaces to be built to swap out the underlying +//! application runtime. For example: pure macOS Cocoa, GTK+, browser, etc. +//! +//! The goal is to have different implementations share as much of the core +//! logic as possible, and to only reach out to platform-specific implementation +//! code when absolutely necessary. +const build_config = @import("build_config.zig"); + +const structs = @import("apprt/structs.zig"); + +pub const action = @import("apprt/action.zig"); +pub const ipc = @import("apprt/ipc.zig"); +pub const gtk = @import("apprt/gtk.zig"); +pub const none = @import("apprt/none.zig"); +pub const browser = @import("apprt/browser.zig"); +pub const embedded = @import("apprt/embedded.zig"); +pub const surface = @import("apprt/surface.zig"); + +pub const Action = action.Action; +pub const Runtime = @import("apprt/runtime.zig").Runtime; +pub const Target = action.Target; + +pub const ContentScale = structs.ContentScale; +pub const Clipboard = structs.Clipboard; +pub const ClipboardContent = structs.ClipboardContent; +pub const ClipboardRequest = structs.ClipboardRequest; +pub const ClipboardRequestType = structs.ClipboardRequestType; +pub const ColorScheme = structs.ColorScheme; +pub const CursorPos = structs.CursorPos; +pub const IMEPos = structs.IMEPos; +pub const Selection = structs.Selection; +pub const SurfaceSize = structs.SurfaceSize; + +/// The implementation to use for the app runtime. This is comptime chosen +/// so that every build has exactly one application runtime implementation. +/// Note: it is very rare to use Runtime directly; most usage will use +/// Window or something. +pub const runtime = switch (build_config.artifact) { + .exe => switch (build_config.app_runtime) { + .none => none, + .gtk => gtk, + }, + .lib => embedded, + .wasm_module => browser, +}; + +pub const App = runtime.App; +pub const Surface = runtime.Surface; + +test { + _ = Runtime; + _ = runtime; + _ = action; + _ = structs; +} diff --git a/vendor/libghostty-vt/src/apprt/action.zig b/vendor/libghostty-vt/src/apprt/action.zig new file mode 100644 index 00000000..f6865af8 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/action.zig @@ -0,0 +1,1007 @@ +const std = @import("std"); +const build_config = @import("../build_config.zig"); +const assert = @import("../quirks.zig").inlineAssert; +const apprt = @import("../apprt.zig"); +const configpkg = @import("../config.zig"); +const input = @import("../input.zig"); +const renderer = @import("../renderer.zig"); +const terminal = @import("../terminal/main.zig"); +const CoreSurface = @import("../Surface.zig"); +const lib = @import("../lib/main.zig"); + +/// The target for an action. This is generally the thing that had focus +/// while the action was made but the concept of "focus" is not guaranteed +/// since actions can also be triggered by timers, scripts, etc. +pub const Target = union(Key) { + app, + surface: *CoreSurface, + + // Sync with: ghostty_target_tag_e + pub const Key = enum(c_int) { + app, + surface, + + test "ghostty.h Target.Key" { + try lib.checkGhosttyHEnum(Key, "GHOSTTY_TARGET_"); + } + }; + + // Sync with: ghostty_target_u + pub const CValue = extern union { + app: void, + surface: *apprt.Surface, + }; + + // Sync with: ghostty_target_s + pub const C = extern struct { + key: Key, + value: CValue, + }; + + /// Convert to ghostty_target_s. + pub fn cval(self: Target) C { + return .{ + .key = @as(Key, self), + .value = switch (self) { + .app => .{ .app = {} }, + .surface => |v| .{ .surface = v.rt_surface }, + }, + }; + } +}; + +/// The possible actions an apprt has to react to. Actions are one-way +/// messages that are sent to the app runtime to trigger some behavior. +/// +/// Actions are very often key binding actions but can also be triggered +/// by lifecycle events. For example, the `quit_timer` action is not bindable. +/// +/// Importantly, actions are generally OPTIONAL to implement by an apprt. +/// Required functionality is called directly on the runtime structure so +/// there is a compiler error if an action is not implemented. +pub const Action = union(Key) { + // A GUIDE TO ADDING NEW ACTIONS: + // + // 1. Add the action to the `Key` enum. The order of the enum matters + // because it maps directly to the libghostty C enum. For ABI + // compatibility, new actions should be added to the end of the enum. + // + // 2. Add the action and optional value to the Action union. + // + // 3. If the value type is not void, ensure the value is C ABI + // compatible (extern). If it is not, add a `C` decl to the value + // and a `cval` function to convert to the C ABI compatible value. + // + // 4. Update `include/ghostty.h`: add the new key, value, and union + // entry. If the value type is void then only the key needs to be + // added. Ensure the order matches exactly with the Zig code. + + /// Quit the application. + quit, + + /// Open a new window. The target determines whether properties such + /// as font size should be inherited. + new_window, + + /// Open a new tab. If the target is a surface it should be opened in + /// the same window as the surface. If the target is the app then + /// the tab should be opened in a new window. + new_tab, + + /// Closes the tab belonging to the currently focused split, or all other + /// tabs, depending on the mode. + close_tab: CloseTabMode, + + /// Create a new split. The value determines the location of the split + /// relative to the target. + new_split: SplitDirection, + + /// Close all open windows. + close_all_windows, + + /// Toggle maximized window state. + toggle_maximize, + + /// Toggle fullscreen mode. + toggle_fullscreen: Fullscreen, + + /// Toggle tab overview. + toggle_tab_overview, + + /// Toggle whether window directions are shown. + toggle_window_decorations, + + /// Toggle the quick terminal in or out. + toggle_quick_terminal, + + /// Toggle the command palette. + toggle_command_palette, + + /// Toggle the visibility of all Ghostty terminal windows. + toggle_visibility, + + /// Toggle the window background opacity. This only has an effect + /// if the window started as transparent (non-opaque), and toggles + /// it between fully opaque and the configured background opacity. + toggle_background_opacity, + + /// Moves a tab by a relative offset. + /// + /// Adjusts the tab position based on `offset` (e.g., -1 for left, +1 + /// for right). If the new position is out of bounds, it wraps around + /// cyclically within the tab range. + move_tab: MoveTab, + + /// Jump to a specific tab. Must handle the scenario that the tab + /// value is invalid. + goto_tab: GotoTab, + + /// Jump to a specific split. + goto_split: GotoSplit, + + /// Jump to next/previous window. + goto_window: GotoWindow, + + /// Resize the split in the given direction. + resize_split: ResizeSplit, + + /// Equalize all the splits in the target window. + equalize_splits, + + /// Toggle whether a split is zoomed or not. A zoomed split is resized + /// to take up the entire window. + toggle_split_zoom, + + /// Present the target terminal whether its a tab, split, or window. + present_terminal, + + /// Sets a size limit (in pixels) for the target terminal. + size_limit: SizeLimit, + + /// Resets the window size to the default size. See the + /// `reset_window_size` keybinding for more information. + reset_window_size, + + /// Specifies the initial size of the target terminal. + /// + /// This may be sent once during the initialization of a surface + /// (as part of the init call) to indicate the initial size requested + /// for the window if it is not maximized or fullscreen. + /// + /// This may also be sent at any time after the surface is initialized + /// to note the new "default size" of the window. This should in general + /// be ignored, but may be useful if the apprt wants to support + /// a "return to default size" action. + initial_size: InitialSize, + + /// The cell size has changed to the given dimensions in pixels. + cell_size: CellSize, + + /// The scrollbar is updating. + scrollbar: terminal.Scrollbar, + + /// The target should be re-rendered. This usually has a specific + /// surface target but if the app is targeted then all active + /// surfaces should be redrawn. + render, + + /// Control whether the inspector is shown or hidden. + inspector: Inspector, + + /// Show the GTK inspector. + show_gtk_inspector, + + /// The inspector for the given target has changes and should be + /// rendered at the next opportunity. + render_inspector, + + /// Show a desktop notification. + desktop_notification: DesktopNotification, + + /// Set the title of the target to the requested value. + set_title: SetTitle, + + /// Set the tab title override for the target's tab. + set_tab_title: SetTitle, + + /// Set the title of the target to a prompted value. It is up to + /// the apprt to prompt. The value specifies whether to prompt for the + /// surface title or the tab title. + prompt_title: PromptTitle, + + /// The current working directory has changed for the target terminal. + pwd: Pwd, + + /// Set the mouse cursor shape. + mouse_shape: terminal.MouseShape, + + /// Set whether the mouse cursor is visible or not. + mouse_visibility: MouseVisibility, + + /// Called when the mouse is over or recently left a link. + mouse_over_link: MouseOverLink, + + /// The health of the renderer has changed. + renderer_health: renderer.Health, + + /// Open the Ghostty configuration. This is platform-specific about + /// what it means; it can mean opening a dedicated UI or just opening + /// a file in a text editor. + open_config, + + /// Called when there are no more surfaces and the app should quit + /// after the configured delay. + /// + /// Despite the name, this is the notification that libghostty sends + /// when there are no more surfaces regardless of if the configuration + /// wants to quit after close, has any delay set, etc. It's up to the + /// apprt to implement the proper logic based on the config. + /// + /// This can be cancelled by sending another quit_timer action with "stop". + /// Multiple "starts" shouldn't happen and can be ignored or cause a + /// restart it isn't that important. + quit_timer: QuitTimer, + + /// Set the window floating state. A floating window is one that is + /// always on top of other windows even when not focused. + float_window: FloatWindow, + + /// Set the secure input functionality on or off. "Secure input" means + /// that the user is currently at some sort of prompt where they may be + /// entering a password or other sensitive information. This can be used + /// by the app runtime to change the appearance of the cursor, setup + /// system APIs to not log the input, etc. + secure_input: SecureInput, + + /// A sequenced key binding has started, continued, or stopped. + /// The UI should show some indication that the user is in a sequenced + /// key mode because other input may be ignored. + key_sequence: KeySequence, + + /// A key table has been activated or deactivated. + key_table: KeyTable, + + /// A terminal color was changed programmatically through things + /// such as OSC 10/11. + color_change: ColorChange, + + /// A request to reload the configuration. The reload request can be + /// from a user or for some internal reason. The reload request may + /// request it is a soft reload or a full reload. See the struct for + /// more documentation. + /// + /// The configuration should be passed to updateConfig either at the + /// app or surface level depending on the target. + reload_config: ReloadConfig, + + /// The configuration has changed. The value is a pointer to the new + /// configuration. The pointer is only valid for the duration of the + /// action and should not be stored. + /// + /// This should be used by apprts to update any internal state that + /// depends on configuration for the given target (i.e. headerbar colors). + /// The apprt should copy any data it needs since the memory lifetime + /// is only valid for the duration of the action. + /// + /// This allows an apprt to have config-dependent state reactively + /// change without having to store the entire configuration or poll + /// for changes. + config_change: ConfigChange, + + /// Closes the currently focused window. + close_window, + + /// Called when the bell character is seen. The apprt should do whatever + /// it needs to ring the bell. This is usually a sound or visual effect. + ring_bell, + + /// Undo the last action. See the "undo" keybinding for more + /// details on what can and cannot be undone. + undo, + + /// Redo the last undone action. + redo, + + check_for_updates, + + /// Open a URL using the native OS mechanisms. On macOS this might be `open` + /// or on Linux this might be `xdg-open`. The exact mechanism is up to the + /// apprt. + open_url: OpenUrl, + + /// Show a native GUI notification that the child process has exited. + show_child_exited: apprt.surface.Message.ChildExited, + + /// Show a native GUI notification about the progress of some TUI operation. + progress_report: terminal.osc.Command.ProgressReport, + + /// Show the on-screen keyboard. + show_on_screen_keyboard, + + /// A command has finished, + command_finished: CommandFinished, + + /// Start the search overlay with an optional initial needle. If the + /// search is already active and the needle is non-empty, update the + /// current search needle and focus the search input. + start_search: StartSearch, + + /// End the search overlay, clearing the search state and hiding it. + end_search, + + /// The total number of matches found by the search. + search_total: SearchTotal, + + /// The currently selected search match index (1-based). + search_selected: SearchSelected, + + /// The readonly state of the surface has changed. + readonly: Readonly, + + /// Copy the effective title of the surface to the clipboard. + /// The effective title is the user-overridden title if set, + /// otherwise the terminal-set title. + copy_title_to_clipboard, + + /// Sync with: ghostty_action_tag_e + pub const Key = enum(c_int) { + quit, + new_window, + new_tab, + close_tab, + new_split, + close_all_windows, + toggle_maximize, + toggle_fullscreen, + toggle_tab_overview, + toggle_window_decorations, + toggle_quick_terminal, + toggle_command_palette, + toggle_visibility, + toggle_background_opacity, + move_tab, + goto_tab, + goto_split, + goto_window, + resize_split, + equalize_splits, + toggle_split_zoom, + present_terminal, + size_limit, + reset_window_size, + initial_size, + cell_size, + scrollbar, + render, + inspector, + show_gtk_inspector, + render_inspector, + desktop_notification, + set_title, + set_tab_title, + prompt_title, + pwd, + mouse_shape, + mouse_visibility, + mouse_over_link, + renderer_health, + open_config, + quit_timer, + float_window, + secure_input, + key_sequence, + key_table, + color_change, + reload_config, + config_change, + close_window, + ring_bell, + undo, + redo, + check_for_updates, + open_url, + show_child_exited, + progress_report, + show_on_screen_keyboard, + command_finished, + start_search, + end_search, + search_total, + search_selected, + readonly, + copy_title_to_clipboard, + + test "ghostty.h Action.Key" { + try lib.checkGhosttyHEnum(Key, "GHOSTTY_ACTION_"); + } + }; + + /// Sync with: ghostty_action_u + pub const CValue = cvalue: { + const key_fields = @typeInfo(Key).@"enum".fields; + var union_fields: [key_fields.len]std.builtin.Type.UnionField = undefined; + for (key_fields, 0..) |field, i| { + const action = @unionInit(Action, field.name, undefined); + const Type = t: { + const Type = @TypeOf(@field(action, field.name)); + // Types can provide custom types for their CValue. + if (Type != void and @hasDecl(Type, "C")) break :t Type.C; + break :t Type; + }; + + union_fields[i] = .{ + .name = field.name, + .type = Type, + .alignment = @alignOf(Type), + }; + } + + break :cvalue @Type(.{ .@"union" = .{ + .layout = .@"extern", + .tag_type = null, + .fields = &union_fields, + .decls = &.{}, + } }); + }; + + /// Sync with: ghostty_action_s + pub const C = extern struct { + key: Key, + value: CValue, + }; + + comptime { + // For ABI compatibility, we expect that this is our union size. + // At the time of writing, we don't promise ABI compatibility + // so we can change this but I want to be aware of it. + assert(@sizeOf(CValue) == switch (@sizeOf(usize)) { + 4 => 16, + 8 => 24, + else => unreachable, + }); + } + + /// Returns the value type for the given key. + pub fn Value(comptime key: Key) type { + inline for (@typeInfo(Action).@"union".fields) |field| { + const field_key = @field(Key, field.name); + if (field_key == key) return field.type; + } + + unreachable; + } + + /// Convert to ghostty_action_s. + pub fn cval(self: Action) C { + const value: CValue = switch (self) { + inline else => |v, tag| @unionInit( + CValue, + @tagName(tag), + if (@TypeOf(v) != void and @hasDecl(@TypeOf(v), "cval")) v.cval() else v, + ), + }; + + return .{ + .key = @as(Key, self), + .value = value, + }; + } +}; + +// This is made extern (c_int) to make interop easier with our embedded +// runtime. The small size cost doesn't make a difference in our union. +pub const SplitDirection = enum(c_int) { + right, + down, + left, + up, + + test "ghostty.h SplitDirection" { + try lib.checkGhosttyHEnum(SplitDirection, "GHOSTTY_SPLIT_DIRECTION_"); + } +}; + +// This is made extern (c_int) to make interop easier with our embedded +// runtime. The small size cost doesn't make a difference in our union. +pub const GotoSplit = enum(c_int) { + previous, + next, + + up, + left, + down, + right, + + test "ghostty.h GotoSplit" { + try lib.checkGhosttyHEnum(GotoSplit, "GHOSTTY_GOTO_SPLIT_"); + } +}; + +// This is made extern (c_int) to make interop easier with our embedded +// runtime. The small size cost doesn't make a difference in our union. +pub const GotoWindow = enum(c_int) { + previous, + next, + + test "ghostty.h GotoWindow" { + try lib.checkGhosttyHEnum(GotoWindow, "GHOSTTY_GOTO_WINDOW_"); + } +}; + +/// The amount to resize the split by and the direction to resize it in. +pub const ResizeSplit = extern struct { + amount: u16, + direction: Direction, + + pub const Direction = enum(c_int) { + up, + down, + left, + right, + + test "ghostty.h ResizeSplit.Direction" { + try lib.checkGhosttyHEnum(Direction, "GHOSTTY_RESIZE_SPLIT_"); + } + }; +}; + +pub const MoveTab = extern struct { + amount: isize, +}; + +/// The tab to jump to. This is non-exhaustive so that integer values represent +/// the index (zero-based) of the tab to jump to. Negative values are special +/// values. +pub const GotoTab = enum(c_int) { + previous = -1, + next = -2, + last = -3, + _, + + // TODO: check non-exhaustive enums + // test "ghostty.h GotoTab" { + // try lib.checkGhosttyHEnum(GotoTab, "GHOSTTY_GOTO_TAB_"); + // } +}; + +/// The fullscreen mode to toggle to if we're moving to fullscreen. +pub const Fullscreen = enum(c_int) { + native, + + /// macOS has a non-native fullscreen mode that is more like a maximized + /// window. This is much faster to enter and exit than the native mode. + macos_non_native, + macos_non_native_visible_menu, + macos_non_native_padded_notch, + + test "ghostty.h Fullscreen" { + try lib.checkGhosttyHEnum(Fullscreen, "GHOSTTY_FULLSCREEN_"); + } +}; + +pub const FloatWindow = enum(c_int) { + on, + off, + toggle, + + test "ghostty.h FloatWindow" { + try lib.checkGhosttyHEnum(FloatWindow, "GHOSTTY_FLOAT_WINDOW_"); + } +}; + +pub const SecureInput = enum(c_int) { + on, + off, + toggle, + + test "ghostty.h SecureInput" { + try lib.checkGhosttyHEnum(SecureInput, "GHOSTTY_SECURE_INPUT_"); + } +}; + +/// The inspector mode to toggle to if we're toggling the inspector. +pub const Inspector = enum(c_int) { + toggle, + show, + hide, + + test "ghostty.h Inspector" { + try lib.checkGhosttyHEnum(Inspector, "GHOSTTY_INSPECTOR_"); + } +}; + +pub const QuitTimer = enum(c_int) { + start, + stop, + + test "ghostty.h QuitTimer" { + try lib.checkGhosttyHEnum(QuitTimer, "GHOSTTY_QUIT_TIMER_"); + } +}; + +pub const Readonly = enum(c_int) { + off, + on, + + test "ghostty.h Readonly" { + try lib.checkGhosttyHEnum(Readonly, "GHOSTTY_READONLY_"); + } +}; + +pub const MouseVisibility = enum(c_int) { + visible, + hidden, + + test "ghostty.h MouseVisibility" { + try lib.checkGhosttyHEnum(MouseVisibility, "GHOSTTY_MOUSE_"); + } +}; + +/// Whether to prompt for the surface title or tab title. +pub const PromptTitle = enum(c_int) { + surface, + tab, + + test "ghostty.h PromptTitle" { + try lib.checkGhosttyHEnum(PromptTitle, "GHOSTTY_PROMPT_TITLE_"); + } +}; + +pub const MouseOverLink = struct { + url: [:0]const u8, + + // Sync with: ghostty_action_mouse_over_link_s + pub const C = extern struct { + url: [*]const u8, + len: usize, + }; + + pub fn cval(self: MouseOverLink) C { + return .{ + .url = self.url.ptr, + .len = self.url.len, + }; + } +}; + +pub const SizeLimit = extern struct { + min_width: u32, + min_height: u32, + max_width: u32, + max_height: u32, +}; + +pub const InitialSize = extern struct { + width: u32, + height: u32, + + /// Make this a valid gobject if we're in a GTK environment. + pub const getGObjectType = switch (build_config.app_runtime) { + .gtk => @import("gobject").ext.defineBoxed( + InitialSize, + .{ .name = "GhosttyApprtInitialSize" }, + ), + + .none => void, + }; +}; + +pub const CellSize = extern struct { + width: u32, + height: u32, +}; + +pub const SetTitle = struct { + title: [:0]const u8, + + // Sync with: ghostty_action_set_title_s + pub const C = extern struct { + title: [*:0]const u8, + }; + + pub fn cval(self: SetTitle) C { + return .{ + .title = self.title.ptr, + }; + } + + pub fn format( + value: @This(), + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: *std.Io.Writer, + ) !void { + try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.title }); + } +}; + +pub const Pwd = struct { + pwd: [:0]const u8, + + // Sync with: ghostty_action_set_pwd_s + pub const C = extern struct { + pwd: [*:0]const u8, + }; + + pub fn cval(self: Pwd) C { + return .{ + .pwd = self.pwd.ptr, + }; + } + + pub fn format( + value: @This(), + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: *std.Io.Writer, + ) !void { + try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.pwd }); + } +}; + +/// The desktop notification to show. +pub const DesktopNotification = struct { + title: [:0]const u8, + body: [:0]const u8, + + // Sync with: ghostty_action_desktop_notification_s + pub const C = extern struct { + title: [*:0]const u8, + body: [*:0]const u8, + }; + + pub fn cval(self: DesktopNotification) C { + return .{ + .title = self.title.ptr, + .body = self.body.ptr, + }; + } + + pub fn format( + value: @This(), + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: *std.Io.Writer, + ) !void { + try writer.print("{s}{{ title: {s}, body: {s} }}", .{ + @typeName(@This()), + value.title, + value.body, + }); + } +}; + +pub const KeySequence = union(enum) { + trigger: input.Trigger, + end, + + // Sync with: ghostty_action_key_sequence_s + pub const C = extern struct { + active: bool, + trigger: input.Trigger.C, + }; + + pub fn cval(self: KeySequence) C { + return switch (self) { + .trigger => |t| .{ .active = true, .trigger = t.cval() }, + .end => .{ .active = false, .trigger = .{} }, + }; + } +}; + +pub const KeyTable = union(enum) { + activate: []const u8, + deactivate, + deactivate_all, + + // Sync with: ghostty_action_key_table_tag_e + pub const Tag = enum(c_int) { + activate, + deactivate, + deactivate_all, + }; + + // Sync with: ghostty_action_key_table_u + pub const CValue = extern union { + activate: extern struct { + name: [*]const u8, + len: usize, + }, + }; + + // Sync with: ghostty_action_key_table_s + pub const C = extern struct { + tag: Tag, + value: CValue, + }; + + pub fn cval(self: KeyTable) C { + return switch (self) { + .activate => |name| .{ + .tag = .activate, + .value = .{ .activate = .{ .name = name.ptr, .len = name.len } }, + }, + .deactivate => .{ + .tag = .deactivate, + .value = undefined, + }, + .deactivate_all => .{ + .tag = .deactivate_all, + .value = undefined, + }, + }; + } +}; + +pub const ColorChange = extern struct { + kind: ColorKind, + r: u8, + g: u8, + b: u8, +}; + +pub const ColorKind = enum(c_int) { + // Negative numbers indicate some named kind + foreground = -1, + background = -2, + cursor = -3, + + // 0+ values indicate a palette index + _, + + // TODO: check non-non-exhaustive enums + // test "ghostty.h ColorKind" { + // try lib.checkGhosttyHEnum(ColorKind, "GHOSTTY_COLOR_KIND_"); + // } +}; + +pub const ReloadConfig = extern struct { + /// A soft reload means that the configuration doesn't need to be + /// read off disk, but libghostty needs the full config again so call + /// updateConfig with it. + soft: bool = false, +}; + +pub const ConfigChange = struct { + config: *const configpkg.Config, + + // Sync with: ghostty_action_config_change_s + pub const C = extern struct { + config: *const configpkg.Config, + }; + + pub fn cval(self: ConfigChange) C { + return .{ + .config = self.config, + }; + } +}; + +/// Open a URL +pub const OpenUrl = struct { + /// The type of data that the URL refers to. + kind: Kind, + + /// The URL. + url: []const u8, + + /// The type of the data at the URL to open. This is used as a hint to + /// potentially open the URL in a different way. + /// + /// Sync with: ghostty_action_open_url_kind_e + pub const Kind = enum(c_int) { + /// The type is unknown. This is the default and apprts should + /// open the URL in the most generic way possible. For example, + /// on macOS this would be the equivalent of `open` or on Linux + /// this would be `xdg-open`. + unknown, + + /// The URL is known to be a text file. In this case, the apprt + /// should try to open the URL in a text editor or viewer or + /// some equivalent, if possible. + text, + + /// The URL is known to contain HTML content. + html, + + test "ghostty.h OpenUrl.Kind" { + try lib.checkGhosttyHEnum(Kind, "GHOSTTY_ACTION_OPEN_URL_KIND_"); + } + }; + + // Sync with: ghostty_action_open_url_s + pub const C = extern struct { + kind: Kind, + url: [*]const u8, + len: usize, + }; + + pub fn cval(self: OpenUrl) C { + return .{ + .kind = self.kind, + .url = self.url.ptr, + .len = self.url.len, + }; + } +}; + +/// sync with ghostty_action_close_tab_mode_e in ghostty.h +pub const CloseTabMode = enum(c_int) { + /// Close the current tab. + this, + /// Close all other tabs. + other, + /// Close all tabs to the right of the current tab. + right, + + test "ghostty.h CloseTabMode" { + try lib.checkGhosttyHEnum(CloseTabMode, "GHOSTTY_ACTION_CLOSE_TAB_MODE_"); + } +}; + +pub const CommandFinished = struct { + exit_code: ?u8, + duration: configpkg.Config.Duration, + + /// sync with ghostty_action_command_finished_s in ghostty.h + pub const C = extern struct { + exit_code: i16, + duration: u64, + }; + + pub fn cval(self: CommandFinished) C { + return .{ + .exit_code = self.exit_code orelse -1, + .duration = self.duration.duration, + }; + } +}; + +pub const StartSearch = struct { + needle: [:0]const u8, + + // Sync with: ghostty_action_start_search_s + pub const C = extern struct { + needle: [*:0]const u8, + }; + + pub fn cval(self: StartSearch) C { + return .{ + .needle = self.needle.ptr, + }; + } +}; + +pub const SearchTotal = struct { + total: ?usize, + + // Sync with: ghostty_action_search_total_s + pub const C = extern struct { + total: isize, + }; + + pub fn cval(self: SearchTotal) C { + return .{ + .total = if (self.total) |t| @intCast(t) else -1, + }; + } +}; + +pub const SearchSelected = struct { + selected: ?usize, + + // Sync with: ghostty_action_search_selected_s + pub const C = extern struct { + selected: isize, + }; + + pub fn cval(self: SearchSelected) C { + return .{ + .selected = if (self.selected) |s| @intCast(s) else -1, + }; + } +}; + +test { + _ = std.testing.refAllDeclsRecursive(@This()); +} diff --git a/vendor/libghostty-vt/src/apprt/browser.zig b/vendor/libghostty-vt/src/apprt/browser.zig new file mode 100644 index 00000000..3b1aa468 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/browser.zig @@ -0,0 +1,4 @@ +const internal_os = @import("../os/main.zig"); +pub const resourcesDir = internal_os.resourcesDir; +pub const App = struct {}; +pub const Window = struct {}; diff --git a/vendor/libghostty-vt/src/apprt/embedded.zig b/vendor/libghostty-vt/src/apprt/embedded.zig new file mode 100644 index 00000000..519a35f2 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/embedded.zig @@ -0,0 +1,2231 @@ +//! Application runtime for the embedded version of Ghostty. The embedded +//! version is when Ghostty is embedded within a parent host application, +//! rather than owning the application lifecycle itself. This is used for +//! example for the macOS build of Ghostty so that we can use a native +//! Swift+XCode-based application. + +const std = @import("std"); +const builtin = @import("builtin"); +const assert = @import("../quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const objc = @import("objc"); +const apprt = @import("../apprt.zig"); +const font = @import("../font/main.zig"); +const input = @import("../input.zig"); +const internal_os = @import("../os/main.zig"); +const renderer = @import("../renderer.zig"); +const terminal = @import("../terminal/main.zig"); +const CoreApp = @import("../App.zig"); +const CoreInspector = @import("../inspector/main.zig").Inspector; +const CoreSurface = @import("../Surface.zig"); +const configpkg = @import("../config.zig"); +const Config = configpkg.Config; + +const log = std.log.scoped(.embedded_window); + +pub const resourcesDir = internal_os.resourcesDir; + +pub const App = struct { + /// Because we only expect the embedding API to be used in embedded + /// environments, the options are extern so that we can expose it + /// directly to a C callconv and not pay for any translation costs. + /// + /// C type: ghostty_runtime_config_s + pub const Options = extern struct { + /// These are just aliases to make the function signatures below + /// more obvious what values will be sent. + const AppUD = ?*anyopaque; + const SurfaceUD = ?*anyopaque; + + /// Userdata that is passed to all the callbacks. + userdata: AppUD = null, + + /// True if the selection clipboard is supported. + supports_selection_clipboard: bool = false, + + /// Callback called to wakeup the event loop. This should trigger + /// a full tick of the app loop. + wakeup: *const fn (AppUD) callconv(.c) void, + + /// Callback called to handle an action. + action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.c) bool, + + /// Read the clipboard value. Returns true if the clipboard request + /// was started and complete_clipboard_request may be called with the + /// given state pointer. Returns false if the clipboard request couldn't + /// be started (such as when no text is available for a paste request). + read_clipboard: *const fn (SurfaceUD, c_int, *apprt.ClipboardRequest) callconv(.c) bool, + + /// This may be called after a read clipboard call to request + /// confirmation that the clipboard value is safe to read. The embedder + /// must call complete_clipboard_request with the given request. + confirm_read_clipboard: *const fn ( + SurfaceUD, + [*:0]const u8, + *apprt.ClipboardRequest, + apprt.ClipboardRequestType, + ) callconv(.c) void, + + /// Write the clipboard value. + write_clipboard: *const fn ( + SurfaceUD, + c_int, + [*]const CAPI.ClipboardContent, + usize, + bool, + ) callconv(.c) void, + + /// Close the current surface given by this function. + close_surface: ?*const fn (SurfaceUD, bool) callconv(.c) void = null, + }; + + /// This is the key event sent for ghostty_surface_key and + /// ghostty_app_key. + pub const KeyEvent = struct { + action: input.Action, + mods: input.Mods, + consumed_mods: input.Mods, + keycode: u32, + text: ?[:0]const u8, + unshifted_codepoint: u32, + composing: bool, + + /// Convert a libghostty key event into a core key event. + fn core(self: KeyEvent) ?input.KeyEvent { + const text: []const u8 = if (self.text) |v| v else ""; + const unshifted_codepoint: u21 = std.math.cast( + u21, + self.unshifted_codepoint, + ) orelse 0; + + // We want to get the physical unmapped key to process keybinds. + const physical_key = keycode: for (input.keycodes.entries) |entry| { + if (entry.native == self.keycode) break :keycode entry.key; + } else .unidentified; + + // Build our final key event + return .{ + .action = self.action, + .key = physical_key, + .mods = self.mods, + .consumed_mods = self.consumed_mods, + .composing = self.composing, + .utf8 = text, + .unshifted_codepoint = unshifted_codepoint, + }; + } + }; + + core_app: *CoreApp, + opts: Options, + keymap: input.Keymap, + + /// The configuration for the app. This is owned by this structure. + config: Config, + + pub fn init( + self: *App, + core_app: *CoreApp, + config: *const Config, + opts: Options, + ) !void { + // We have to clone the config. + const alloc = core_app.alloc; + var config_clone = try config.clone(alloc); + errdefer config_clone.deinit(); + + var keymap = try input.Keymap.init(); + errdefer keymap.deinit(); + + self.* = .{ + .core_app = core_app, + .config = config_clone, + .opts = opts, + .keymap = keymap, + }; + } + + pub fn terminate(self: *App) void { + self.keymap.deinit(); + self.config.deinit(); + } + + /// Returns true if there are any global keybinds in the configuration. + pub fn hasGlobalKeybinds(self: *const App) bool { + var it = self.config.keybind.set.bindings.iterator(); + while (it.next()) |entry| { + switch (entry.value_ptr.*) { + .leader => {}, + inline .leaf, .leaf_chained => |leaf| if (leaf.flags.global) return true, + } + } + + return false; + } + + /// The target of a key event. This is used to determine some subtly + /// different behavior between app and surface key events. + pub const KeyTarget = union(enum) { + app, + surface: *Surface, + }; + + /// See CoreApp.focusEvent + pub fn focusEvent(self: *App, focused: bool) void { + self.core_app.focusEvent(focused); + } + + /// See CoreApp.keyEvent. + pub fn keyEvent( + self: *App, + target: KeyTarget, + event: KeyEvent, + ) !bool { + // Convert our C key event into a Zig one. + const input_event: input.KeyEvent = event.core() orelse + return false; + + // Invoke the core Ghostty logic to handle this input. + const effect: CoreSurface.InputEffect = switch (target) { + .app => if (self.core_app.keyEvent( + self, + input_event, + )) .consumed else .ignored, + + .surface => |surface| try surface.core_surface.keyCallback( + input_event, + ), + }; + + return switch (effect) { + .closed => true, + .ignored => false, + .consumed => true, + }; + } + + /// This should be called whenever the keyboard layout was changed. + pub fn reloadKeymap(self: *App) !void { + // Reload the keymap + try self.keymap.reload(); + } + + /// Loads the keyboard layout. + /// + /// Kind of expensive so this should be avoided if possible. When I say + /// "kind of expensive" I mean that its not something you probably want + /// to run on every keypress. + pub fn keyboardLayout(self: *const App) input.KeyboardLayout { + // We only support keyboard layout detection on macOS. + if (comptime builtin.os.tag != .macos) return .unknown; + + // Any layout larger than this is not something we can handle. + var buf: [256]u8 = undefined; + const id = self.keymap.sourceId(&buf) catch |err| { + comptime assert(@TypeOf(err) == error{OutOfMemory}); + return .unknown; + }; + + return input.KeyboardLayout.mapAppleId(id) orelse .unknown; + } + + pub fn wakeup(self: *const App) void { + self.opts.wakeup(self.opts.userdata); + } + + pub fn wait(self: *const App) !void { + _ = self; + } + + /// Create a new surface for the app. + fn newSurface(self: *App, opts: Surface.Options) !*Surface { + // Grab a surface allocation because we're going to need it. + var surface = try self.core_app.alloc.create(Surface); + errdefer self.core_app.alloc.destroy(surface); + + // Create the surface + try surface.init(self, opts); + errdefer surface.deinit(); + + return surface; + } + + /// Close the given surface. + pub fn closeSurface(self: *App, surface: *Surface) void { + surface.deinit(); + self.core_app.alloc.destroy(surface); + } + + pub fn redrawInspector(self: *App, surface: *Surface) void { + _ = self; + surface.queueInspectorRender(); + } + + /// Perform a given action. Returns `true` if the action was able to be + /// performed, `false` otherwise. + pub fn performAction( + self: *App, + target: apprt.Target, + comptime action: apprt.Action.Key, + value: apprt.Action.Value(action), + ) !bool { + // Special case certain actions before they are sent to the + // embedded apprt. + self.performPreAction(target, action, value); + + log.debug("dispatching action target={t} action={} value={any}", .{ + target, + action, + value, + }); + return self.opts.action( + self, + target.cval(), + @unionInit(apprt.Action, @tagName(action), value).cval(), + ); + } + + fn performPreAction( + self: *App, + target: apprt.Target, + comptime action: apprt.Action.Key, + value: apprt.Action.Value(action), + ) void { + // Special case certain actions before they are sent to the embedder + switch (action) { + .set_title => switch (target) { + .app => {}, + .surface => |surface| { + // Dupe the title so that we can store it. If we get an allocation + // error we just ignore it, since this only breaks a few minor things. + const alloc = self.core_app.alloc; + if (surface.rt_surface.title) |v| alloc.free(v); + surface.rt_surface.title = alloc.dupeZ(u8, value.title) catch null; + }, + }, + + .config_change => switch (target) { + .surface => {}, + + // For app updates, we update our core config. We need to + // clone it because the caller owns the param. + .app => if (value.config.clone(self.core_app.alloc)) |config| { + self.config.deinit(); + self.config = config; + } else |err| { + log.err("error updating app config err={}", .{err}); + }, + }, + + else => {}, + } + } + + /// Send the given IPC to a running Ghostty. Returns `true` if the action was + /// able to be performed, `false` otherwise. + /// + /// Note that this is a static function. Since this is called from a CLI app (or + /// some other process that is not Ghostty) there is no full-featured apprt App + /// to use. + pub fn performIpc( + _: Allocator, + _: apprt.ipc.Target, + comptime action: apprt.ipc.Action.Key, + _: apprt.ipc.Action.Value(action), + ) (Allocator.Error || std.posix.WriteError || apprt.ipc.Errors)!bool { + switch (action) { + .new_window => return false, + } + } +}; + +/// Platform-specific configuration for libghostty. +pub const Platform = union(PlatformTag) { + macos: MacOS, + ios: IOS, + + // If our build target for libghostty is not darwin then we do + // not include macos support at all. + pub const MacOS = if (builtin.target.os.tag.isDarwin()) struct { + /// The view to render the surface on. + nsview: objc.Object, + } else void; + + pub const IOS = if (builtin.target.os.tag.isDarwin()) struct { + /// The view to render the surface on. + uiview: objc.Object, + } else void; + + // The C ABI compatible version of this union. The tag is expected + // to be stored elsewhere. + pub const C = extern union { + macos: extern struct { + nsview: ?*anyopaque, + }, + + ios: extern struct { + uiview: ?*anyopaque, + }, + }; + + /// Initialize a Platform a tag and configuration from the C ABI. + pub fn init(tag_int: c_int, c_platform: C) !Platform { + const tag = try std.meta.intToEnum(PlatformTag, tag_int); + return switch (tag) { + .macos => if (MacOS != void) macos: { + const config = c_platform.macos; + const nsview = objc.Object.fromId(config.nsview orelse + break :macos error.NSViewMustBeSet); + break :macos .{ .macos = .{ .nsview = nsview } }; + } else error.UnsupportedPlatform, + + .ios => if (IOS != void) ios: { + const config = c_platform.ios; + const uiview = objc.Object.fromId(config.uiview orelse + break :ios error.UIViewMustBeSet); + break :ios .{ .ios = .{ .uiview = uiview } }; + } else error.UnsupportedPlatform, + }; + } +}; + +pub const PlatformTag = enum(c_int) { + // "0" is reserved for invalid so we can detect unset values + // from the C API. + + macos = 1, + ios = 2, +}; + +pub const EnvVar = extern struct { + /// The name of the environment variable. + key: [*:0]const u8, + + /// The value of the environment variable. + value: [*:0]const u8, +}; + +pub const Surface = struct { + app: *App, + platform: Platform, + userdata: ?*anyopaque = null, + core_surface: CoreSurface, + content_scale: apprt.ContentScale, + size: apprt.SurfaceSize, + cursor_pos: apprt.CursorPos, + inspector: ?*Inspector = null, + + /// The current title of the surface. The embedded apprt saves this so + /// that getTitle works without the implementer needing to save it. + title: ?[:0]const u8 = null, + + /// Surface initialization options. + pub const Options = extern struct { + /// The platform that this surface is being initialized for and + /// the associated platform-specific configuration. + platform_tag: c_int = 0, + platform: Platform.C = undefined, + + /// Userdata passed to some of the callbacks. + userdata: ?*anyopaque = null, + + /// The scale factor of the screen. + scale_factor: f64 = 1, + + /// The font size to inherit. If 0, default font size will be used. + font_size: f32 = 0, + + /// The working directory to load into. + working_directory: ?[*:0]const u8 = null, + + /// The command to run in the new surface. If this is set then + /// the "wait-after-command" option is also automatically set to true, + /// since this is used for scripting. + /// + /// This command always run in a shell (e.g. via `/bin/sh -c`), + /// despite Ghostty allowing directly executed commands via config. + /// This is a legacy thing and we should probably change it in the + /// future once we have a concrete use case. + command: ?[*:0]const u8 = null, + + /// Extra environment variables to set for the surface. + env_vars: ?[*]EnvVar = null, + env_var_count: usize = 0, + + /// Input to send to the command after it is started. + initial_input: ?[*:0]const u8 = null, + + /// Wait after the command exits + wait_after_command: bool = false, + + /// Context for the new surface + context: apprt.surface.NewSurfaceContext = .window, + }; + + pub fn init(self: *Surface, app: *App, opts: Options) !void { + self.* = .{ + .app = app, + .platform = try .init(opts.platform_tag, opts.platform), + .userdata = opts.userdata, + .core_surface = undefined, + .content_scale = .{ + .x = @floatCast(opts.scale_factor), + .y = @floatCast(opts.scale_factor), + }, + .size = .{ .width = 800, .height = 600 }, + .cursor_pos = .{ .x = -1, .y = -1 }, + }; + + // Add ourselves to the list of surfaces on the app. + try app.core_app.addSurface(self); + errdefer app.core_app.deleteSurface(self); + + // Shallow copy the config so that we can modify it. + var config = try apprt.surface.newConfig(app.core_app, &app.config, opts.context); + defer config.deinit(); + + // If we have a working directory from the options then we set it. + if (opts.working_directory) |c_wd| { + const wd = std.mem.sliceTo(c_wd, 0); + if (wd.len > 0) wd: { + var dir = std.fs.openDirAbsolute(wd, .{}) catch |err| { + log.warn( + "error opening requested working directory dir={s} err={}", + .{ wd, err }, + ); + break :wd; + }; + defer dir.close(); + + const stat = dir.stat() catch |err| { + log.warn( + "failed to stat requested working directory dir={s} err={}", + .{ wd, err }, + ); + break :wd; + }; + + if (stat.kind != .directory) { + log.warn( + "requested working directory is not a directory dir={s}", + .{wd}, + ); + break :wd; + } + + var wd_val: configpkg.WorkingDirectory = .{ .path = wd }; + if (wd_val.finalize(config.arenaAlloc())) |_| { + config.@"working-directory" = wd_val; + } else |err| { + log.warn( + "error finalizing working directory config dir={s} err={}", + .{ wd_val.path, err }, + ); + } + } + } + + // If we have a command from the options then we set it. + if (opts.command) |c_command| { + const cmd = std.mem.sliceTo(c_command, 0); + if (cmd.len > 0) { + config.command = .{ .shell = cmd }; + config.@"wait-after-command" = true; + } + } + + // Apply any environment variables that were requested. + if (opts.env_var_count > 0) { + const alloc = config.arenaAlloc(); + for (opts.env_vars.?[0..opts.env_var_count]) |env_var| { + const key = std.mem.sliceTo(env_var.key, 0); + const value = std.mem.sliceTo(env_var.value, 0); + try config.env.map.put( + alloc, + try alloc.dupeZ(u8, key), + try alloc.dupeZ(u8, value), + ); + } + } + + // If we have an initial input then we set it. + if (opts.initial_input) |c_input| { + const alloc = config.arenaAlloc(); + + // We need to escape the string because the "raw" field + // expects a Zig string. + var buf: std.Io.Writer.Allocating = .init(alloc); + defer buf.deinit(); + try std.zig.stringEscape( + std.mem.sliceTo(c_input, 0), + &buf.writer, + ); + + config.input.list.clearRetainingCapacity(); + try config.input.list.append( + alloc, + .{ .raw = try buf.toOwnedSliceSentinel(0) }, + ); + } + + // Wait after command + if (opts.wait_after_command) { + config.@"wait-after-command" = true; + } + + // Initialize our surface right away. We're given a view that is + // ready to use. + try self.core_surface.init( + app.core_app.alloc, + &config, + app.core_app, + app, + self, + ); + errdefer self.core_surface.deinit(); + + // If our options requested a specific font-size, set that. + if (opts.font_size != 0) { + var font_size = self.core_surface.font_size; + font_size.points = opts.font_size; + try self.core_surface.setFontSize(font_size); + } + } + + pub fn deinit(self: *Surface) void { + // Shut down our inspector + self.freeInspector(); + + // Free our title + if (self.title) |v| self.app.core_app.alloc.free(v); + + // Remove ourselves from the list of known surfaces in the app. + self.app.core_app.deleteSurface(self); + + // Clean up our core surface so that all the rendering and IO stop. + self.core_surface.deinit(); + } + + /// Initialize the inspector instance. A surface can only have one + /// inspector at any given time, so this will return the previous inspector + /// if it was already initialized. + pub fn initInspector(self: *Surface) !*Inspector { + if (self.inspector) |v| return v; + + const alloc = self.app.core_app.alloc; + const inspector = try alloc.create(Inspector); + errdefer alloc.destroy(inspector); + inspector.* = try .init(self); + self.inspector = inspector; + return inspector; + } + + pub fn freeInspector(self: *Surface) void { + if (self.inspector) |v| { + v.deinit(); + self.app.core_app.alloc.destroy(v); + self.inspector = null; + } + } + + pub fn core(self: *Surface) *CoreSurface { + return &self.core_surface; + } + + pub fn rtApp(self: *const Surface) *App { + return self.app; + } + + pub fn close(self: *const Surface, process_alive: bool) void { + const func = self.app.opts.close_surface orelse { + log.info("runtime embedder does not support closing a surface", .{}); + return; + }; + + func(self.userdata, process_alive); + } + + pub fn getContentScale(self: *const Surface) !apprt.ContentScale { + return self.content_scale; + } + + pub fn getSize(self: *const Surface) !apprt.SurfaceSize { + return self.size; + } + + pub fn getTitle(self: *Surface) ?[:0]const u8 { + return self.title; + } + + pub fn supportsClipboard( + self: *const Surface, + clipboard_type: apprt.Clipboard, + ) bool { + return switch (clipboard_type) { + .standard => true, + .selection, .primary => self.app.opts.supports_selection_clipboard, + }; + } + + pub fn clipboardRequest( + self: *Surface, + clipboard_type: apprt.Clipboard, + state: apprt.ClipboardRequest, + ) !bool { + // We need to allocate to get a pointer to store our clipboard request + // so that it is stable until the read_clipboard callback and call + // complete_clipboard_request. This sucks but clipboard requests aren't + // high throughput so it's probably fine. + const alloc = self.app.core_app.alloc; + const state_ptr = try alloc.create(apprt.ClipboardRequest); + errdefer alloc.destroy(state_ptr); + state_ptr.* = state; + + const started = self.app.opts.read_clipboard( + self.userdata, + @intCast(@intFromEnum(clipboard_type)), + state_ptr, + ); + if (!started) { + alloc.destroy(state_ptr); + return false; + } + + return true; + } + + fn completeClipboardRequest( + self: *Surface, + str: [:0]const u8, + state: *apprt.ClipboardRequest, + confirmed: bool, + ) void { + const alloc = self.app.core_app.alloc; + + // Attempt to complete the request, but we may request + // confirmation. + self.core_surface.completeClipboardRequest( + state.*, + str, + confirmed, + ) catch |err| switch (err) { + error.UnsafePaste, + error.UnauthorizedPaste, + => { + self.app.opts.confirm_read_clipboard( + self.userdata, + str.ptr, + state, + state.*, + ); + + return; + }, + + else => log.err("error completing clipboard request err={}", .{err}), + }; + + // We don't defer this because the clipboard confirmation route + // preserves the clipboard request. + alloc.destroy(state); + } + + pub fn setClipboard( + self: *const Surface, + clipboard_type: apprt.Clipboard, + contents: []const apprt.ClipboardContent, + confirm: bool, + ) !void { + const alloc = self.app.core_app.alloc; + const array = try alloc.alloc(CAPI.ClipboardContent, contents.len); + defer alloc.free(array); + for (contents, 0..) |content, i| { + array[i] = .{ + .mime = content.mime, + .data = content.data, + }; + } + + self.app.opts.write_clipboard( + self.userdata, + @intCast(@intFromEnum(clipboard_type)), + array.ptr, + array.len, + confirm, + ); + } + + pub fn getCursorPos(self: *const Surface) !apprt.CursorPos { + return self.cursor_pos; + } + + pub fn refresh(self: *Surface) void { + self.core_surface.refreshCallback() catch |err| { + log.err("error in refresh callback err={}", .{err}); + return; + }; + } + + pub fn draw(self: *Surface) void { + self.core_surface.draw() catch |err| { + log.err("error in draw err={}", .{err}); + return; + }; + } + + pub fn updateContentScale(self: *Surface, x: f64, y: f64) void { + // We are an embedded API so the caller can send us all sorts of + // garbage. We want to make sure that the float values are valid + // and we don't want to support fractional scaling below 1. + const x_scaled = @max(1, if (std.math.isNan(x)) 1 else x); + const y_scaled = @max(1, if (std.math.isNan(y)) 1 else y); + + self.content_scale = .{ + .x = @floatCast(x_scaled), + .y = @floatCast(y_scaled), + }; + + self.core_surface.contentScaleCallback(self.content_scale) catch |err| { + log.err("error in content scale callback err={}", .{err}); + return; + }; + } + + pub fn updateSize(self: *Surface, width: u32, height: u32) void { + // Runtimes sometimes generate superfluous resize events even + // if the size did not actually change (SwiftUI). We check + // that the size actually changed from what we last recorded + // since resizes are expensive. + if (self.size.width == width and self.size.height == height) return; + + self.size = .{ + .width = width, + .height = height, + }; + + // Call the primary callback. + self.core_surface.sizeCallback(self.size) catch |err| { + log.err("error in size callback err={}", .{err}); + return; + }; + } + + pub fn colorSchemeCallback(self: *Surface, scheme: apprt.ColorScheme) void { + self.core_surface.colorSchemeCallback(scheme) catch |err| { + log.err("error setting color scheme err={}", .{err}); + return; + }; + } + + pub fn mouseButtonCallback( + self: *Surface, + action: input.MouseButtonState, + button: input.MouseButton, + mods: input.Mods, + ) bool { + return self.core_surface.mouseButtonCallback(action, button, mods) catch |err| { + log.err("error in mouse button callback err={}", .{err}); + return false; + }; + } + + pub fn mousePressureCallback( + self: *Surface, + stage: input.MousePressureStage, + pressure: f64, + ) void { + self.core_surface.mousePressureCallback(stage, pressure) catch |err| { + log.err("error in mouse pressure callback err={}", .{err}); + return; + }; + } + + pub fn scrollCallback( + self: *Surface, + xoff: f64, + yoff: f64, + mods: input.ScrollMods, + ) void { + self.core_surface.scrollCallback(xoff, yoff, mods) catch |err| { + log.err("error in scroll callback err={}", .{err}); + return; + }; + } + + pub fn cursorPosCallback( + self: *Surface, + x: f64, + y: f64, + mods: input.Mods, + ) void { + // Convert our unscaled x/y to scaled. + const pos = self.cursorPosToPixels(.{ + .x = @floatCast(x), + .y = @floatCast(y), + }) catch |err| { + log.err( + "error converting cursor pos to scaled pixels in cursor pos callback err={}", + .{err}, + ); + return; + }; + + // There are cases where the platform reports a mouse motion event + // without the cursor actually moving. For example, on macOS, updating + // the window title can trigger a phantom mouse-move event at the same + // coordinates. This can cause the mouse to incorrectly unhide when + // mouse-hide-while-typing is enabled (commonly seen with TUI apps + // like Zellij that frequently update the title). To prevent incorrect + // behavior, we only continue with callback logic if the cursor has + // actually moved. + if (@abs(self.cursor_pos.x - pos.x) < 1 and + @abs(self.cursor_pos.y - pos.y) < 1) return; + + self.cursor_pos = pos; + + self.core_surface.cursorPosCallback(self.cursor_pos, mods) catch |err| { + log.err("error in cursor pos callback err={}", .{err}); + return; + }; + } + + pub fn preeditCallback(self: *Surface, preedit_: ?[]const u8) void { + _ = self.core_surface.preeditCallback(preedit_) catch |err| { + log.err("error in preedit callback err={}", .{err}); + return; + }; + } + + pub fn textCallback(self: *Surface, text: []const u8) void { + _ = self.core_surface.textCallback(text) catch |err| { + log.err("error in key callback err={}", .{err}); + return; + }; + } + + pub fn focusCallback(self: *Surface, focused: bool) void { + self.core_surface.focusCallback(focused) catch |err| { + log.err("error in focus callback err={}", .{err}); + return; + }; + } + + pub fn occlusionCallback(self: *Surface, visible: bool) void { + self.core_surface.occlusionCallback(visible) catch |err| { + log.err("error in occlusion callback err={}", .{err}); + return; + }; + } + + fn queueInspectorRender(self: *Surface) void { + _ = self.app.performAction( + .{ .surface = &self.core_surface }, + .render_inspector, + {}, + ) catch |err| { + log.err("error rendering the inspector err={}", .{err}); + return; + }; + } + + pub fn newSurfaceOptions(self: *const Surface, context: apprt.surface.NewSurfaceContext) apprt.Surface.Options { + const font_size: f32 = font_size: { + if (!self.app.config.@"window-inherit-font-size") break :font_size 0; + break :font_size self.core_surface.font_size.points; + }; + + const working_directory: ?[*:0]const u8 = wd: { + if (!apprt.surface.shouldInheritWorkingDirectory(context, &self.app.config)) break :wd null; + const cwd = self.core_surface.pwd(self.app.core_app.alloc) catch null orelse break :wd null; + defer self.app.core_app.alloc.free(cwd); + break :wd self.app.core_app.alloc.dupeZ(u8, cwd) catch null; + }; + + return .{ + .font_size = font_size, + .working_directory = working_directory, + .context = context, + }; + } + + pub fn defaultTermioEnv(self: *const Surface) !std.process.EnvMap { + const alloc = self.app.core_app.alloc; + var env = try internal_os.getEnvMap(alloc); + errdefer env.deinit(); + + if (comptime builtin.target.os.tag.isDarwin()) { + if (env.get("__XCODE_BUILT_PRODUCTS_DIR_PATHS") != null) { + env.remove("__XCODE_BUILT_PRODUCTS_DIR_PATHS"); + env.remove("__XPC_DYLD_LIBRARY_PATH"); + env.remove("DYLD_FRAMEWORK_PATH"); + env.remove("DYLD_INSERT_LIBRARIES"); + env.remove("DYLD_LIBRARY_PATH"); + env.remove("LD_LIBRARY_PATH"); + env.remove("SECURITYSESSIONID"); + env.remove("XPC_SERVICE_NAME"); + } + + // Remove this so that running `ghostty` within Ghostty works. + env.remove("GHOSTTY_MAC_LAUNCH_SOURCE"); + + // If we were launched from the desktop then we want to + // remove the LANGUAGE env var so that we don't inherit + // our translation settings for Ghostty. If we aren't from + // the desktop then we didn't set our LANGUAGE var so we + // don't need to remove it. + if (internal_os.launchedFromDesktop()) env.remove("LANGUAGE"); + } + + return env; + } + + /// The cursor position from the host directly is in screen coordinates but + /// all our interface works in pixels. + fn cursorPosToPixels(self: *const Surface, pos: apprt.CursorPos) !apprt.CursorPos { + const scale = try self.getContentScale(); + return .{ .x = pos.x * scale.x, .y = pos.y * scale.y }; + } +}; + +/// Inspector is the state required for the terminal inspector. A terminal +/// inspector is 1:1 with a Surface. +pub const Inspector = struct { + const cimgui = @import("dcimgui"); + + surface: *Surface, + ig_ctx: *cimgui.c.ImGuiContext, + backend: ?Backend = null, + content_scale: f64 = 1, + + /// Our previous instant used to calculate delta time for animations. + instant: ?std.time.Instant = null, + + const Backend = enum { + metal, + + pub fn deinit(self: Backend) void { + switch (self) { + .metal => if (builtin.target.os.tag.isDarwin()) cimgui.ImGui_ImplMetal_Shutdown(), + } + } + }; + + pub fn init(surface: *Surface) !Inspector { + const ig_ctx = cimgui.c.ImGui_CreateContext(null) orelse return error.OutOfMemory; + errdefer cimgui.c.ImGui_DestroyContext(ig_ctx); + cimgui.c.ImGui_SetCurrentContext(ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + io.BackendPlatformName = "ghostty_embedded"; + + // Setup our core inspector + CoreInspector.setup(); + surface.core_surface.activateInspector() catch |err| { + log.err("failed to activate inspector err={}", .{err}); + }; + + return .{ + .surface = surface, + .ig_ctx = ig_ctx, + }; + } + + pub fn deinit(self: *Inspector) void { + self.surface.core_surface.deactivateInspector(); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + if (self.backend) |v| v.deinit(); + cimgui.c.ImGui_DestroyContext(self.ig_ctx); + } + + /// Queue a render for the next frame. + pub fn queueRender(self: *Inspector) void { + self.surface.queueInspectorRender(); + } + + /// Initialize the inspector for a metal backend. + pub fn initMetal(self: *Inspector, device: objc.Object) bool { + defer device.msgSend(void, objc.sel("release"), .{}); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + + if (self.backend) |v| { + v.deinit(); + self.backend = null; + } + + if (!cimgui.ImGui_ImplMetal_Init(device.value)) { + log.warn("failed to initialize metal backend", .{}); + return false; + } + self.backend = .metal; + + log.debug("initialized metal backend", .{}); + return true; + } + + pub fn renderMetal( + self: *Inspector, + command_buffer: objc.Object, + desc: objc.Object, + ) !void { + defer { + command_buffer.msgSend(void, objc.sel("release"), .{}); + desc.msgSend(void, objc.sel("release"), .{}); + } + assert(self.backend == .metal); + //log.debug("render", .{}); + + // Setup our imgui frame. We need to render multiple frames to ensure + // ImGui completes all its state processing. I don't know how to fix + // this. + for (0..2) |_| { + cimgui.ImGui_ImplMetal_NewFrame(desc.value); + try self.newFrame(); + cimgui.c.ImGui_NewFrame(); + + // Build our UI + render: { + const surface = &self.surface.core_surface; + const inspector = surface.inspector orelse break :render; + inspector.render(surface); + } + + // Render + cimgui.c.ImGui_Render(); + } + + // MTLRenderCommandEncoder + const encoder = command_buffer.msgSend( + objc.Object, + objc.sel("renderCommandEncoderWithDescriptor:"), + .{desc.value}, + ); + defer encoder.msgSend(void, objc.sel("endEncoding"), .{}); + cimgui.ImGui_ImplMetal_RenderDrawData( + cimgui.c.ImGui_GetDrawData(), + command_buffer.value, + encoder.value, + ); + } + + pub fn updateContentScale(self: *Inspector, x: f64, y: f64) void { + _ = y; + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + + // Cache our scale because we use it for cursor position calculations. + self.content_scale = x; + + // Setup a new style and scale it appropriately. We must use the + // ImGuiStyle constructor to get proper default values (e.g., + // CurveTessellationTol) rather than zero-initialized values. + var style: cimgui.c.ImGuiStyle = undefined; + cimgui.ext.ImGuiStyle_ImGuiStyle(&style); + cimgui.c.ImGuiStyle_ScaleAllSizes(&style, @floatCast(x)); + const active_style = cimgui.c.ImGui_GetStyle(); + active_style.* = style; + } + + pub fn updateSize(self: *Inspector, width: u32, height: u32) void { + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + io.DisplaySize = .{ .x = @floatFromInt(width), .y = @floatFromInt(height) }; + } + + pub fn mouseButtonCallback( + self: *Inspector, + action: input.MouseButtonState, + button: input.MouseButton, + mods: input.Mods, + ) void { + _ = mods; + + self.queueRender(); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + + const imgui_button = switch (button) { + .left => cimgui.c.ImGuiMouseButton_Left, + .middle => cimgui.c.ImGuiMouseButton_Middle, + .right => cimgui.c.ImGuiMouseButton_Right, + else => return, // unsupported + }; + + cimgui.c.ImGuiIO_AddMouseButtonEvent(io, imgui_button, action == .press); + } + + pub fn scrollCallback( + self: *Inspector, + xoff: f64, + yoff: f64, + mods: input.ScrollMods, + ) void { + self.queueRender(); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + + // For precision scrolling (trackpads), the values are in pixels which + // scroll way too fast. Scale them down to approximate discrete wheel + // notches. imgui expects 1.0 to scroll ~5 lines of text. + const scale: f64 = if (mods.precision) 0.1 else 1.0; + cimgui.c.ImGuiIO_AddMouseWheelEvent( + io, + @floatCast(xoff * scale), + @floatCast(yoff * scale), + ); + } + + pub fn cursorPosCallback(self: *Inspector, x: f64, y: f64) void { + self.queueRender(); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + cimgui.c.ImGuiIO_AddMousePosEvent( + io, + @floatCast(x * self.content_scale), + @floatCast(y * self.content_scale), + ); + } + + pub fn focusCallback(self: *Inspector, focused: bool) void { + self.queueRender(); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + cimgui.c.ImGuiIO_AddFocusEvent(io, focused); + } + + pub fn textCallback(self: *Inspector, text: [:0]const u8) void { + self.queueRender(); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + cimgui.c.ImGuiIO_AddInputCharactersUTF8(io, text.ptr); + } + + pub fn keyCallback( + self: *Inspector, + action: input.Action, + key: input.Key, + mods: input.Mods, + ) !void { + self.queueRender(); + cimgui.c.ImGui_SetCurrentContext(self.ig_ctx); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + + // Update all our modifiers + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftShift, mods.shift); + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftCtrl, mods.ctrl); + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftAlt, mods.alt); + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftSuper, mods.super); + + // Send our keypress + if (key.imguiKey()) |imgui_key| { + cimgui.c.ImGuiIO_AddKeyEvent( + io, + imgui_key, + action == .press or action == .repeat, + ); + } + } + + fn newFrame(self: *Inspector) !void { + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + + // Determine our delta time + const now = try std.time.Instant.now(); + io.DeltaTime = if (self.instant) |prev| delta: { + const since_ns: f64 = @floatFromInt(now.since(prev)); + const ns_per_s: f64 = @floatFromInt(std.time.ns_per_s); + const since_s: f32 = @floatCast(since_ns / ns_per_s); + break :delta @max(0.00001, since_s); + } else (1.0 / 60.0); + self.instant = now; + } +}; + +// C API +pub const CAPI = struct { + const global = &@import("../global.zig").state; + + /// This is the same as Surface.KeyEvent but this is the raw C API version. + const KeyEvent = extern struct { + action: input.Action, + mods: c_int, + consumed_mods: c_int, + keycode: u32, + text: ?[*:0]const u8, + unshifted_codepoint: u32, + composing: bool, + + /// Convert to Zig key event. + fn keyEvent(self: KeyEvent) App.KeyEvent { + return .{ + .action = self.action, + .mods = @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(self.mods))), + )), + .consumed_mods = @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(self.consumed_mods))), + )), + .keycode = self.keycode, + .text = if (self.text) |ptr| std.mem.sliceTo(ptr, 0) else null, + .unshifted_codepoint = self.unshifted_codepoint, + .composing = self.composing, + }; + } + }; + + const SurfaceSize = extern struct { + columns: u16, + rows: u16, + width_px: u32, + height_px: u32, + cell_width_px: u32, + cell_height_px: u32, + }; + + // ghostty_clipboard_content_s + const ClipboardContent = extern struct { + mime: [*:0]const u8, + data: [*:0]const u8, + }; + + // ghostty_text_s + const Text = extern struct { + tl_px_x: f64, + tl_px_y: f64, + offset_start: u32, + offset_len: u32, + text: ?[*:0]const u8, + text_len: usize, + + pub fn deinit(self: *Text) void { + if (self.text) |ptr| { + global.alloc.free(ptr[0..self.text_len :0]); + } + } + }; + + // ghostty_point_s + const Point = extern struct { + tag: Tag, + coord_tag: CoordTag, + x: u32, + y: u32, + + const Tag = enum(c_int) { + active = 0, + viewport = 1, + screen = 2, + history = 3, + }; + + const CoordTag = enum(c_int) { + exact = 0, + top_left = 1, + bottom_right = 2, + }; + + fn pin( + self: Point, + screen: *const terminal.Screen, + ) ?terminal.Pin { + // The core point tag. + const tag: terminal.point.Tag = switch (self.tag) { + inline else => |tag| @field( + terminal.point.Tag, + @tagName(tag), + ), + }; + + // Clamp our point to the screen bounds. + const clamped_x = @min(self.x, screen.pages.cols -| 1); + const clamped_y = @min(self.y, screen.pages.rows -| 1); + + return switch (self.coord_tag) { + // Exact coordinates require a specific pin. + .exact => exact: { + const pt_x = std.math.cast( + terminal.size.CellCountInt, + clamped_x, + ) orelse std.math.maxInt(terminal.size.CellCountInt); + + const pt: terminal.Point = switch (tag) { + inline else => |v| @unionInit( + terminal.Point, + @tagName(v), + .{ .x = pt_x, .y = clamped_y }, + ), + }; + + break :exact screen.pages.pin(pt) orelse null; + }, + + .top_left => screen.pages.getTopLeft(tag), + + .bottom_right => screen.pages.getBottomRight(tag), + }; + } + }; + + // ghostty_selection_s + const Selection = extern struct { + tl: Point, + br: Point, + rectangle: bool, + + fn core( + self: Selection, + screen: *const terminal.Screen, + ) ?terminal.Selection { + return .{ + .bounds = .{ .untracked = .{ + .start = self.tl.pin(screen) orelse return null, + .end = self.br.pin(screen) orelse return null, + } }, + .rectangle = self.rectangle, + }; + } + }; + + // Reference the conditional exports based on target platform + // so they're included in the C API. + comptime { + if (builtin.target.os.tag.isDarwin()) { + _ = Darwin; + } + } + + /// Create a new app. + export fn ghostty_app_new( + opts: *const apprt.runtime.App.Options, + config: *const Config, + ) ?*App { + return app_new_(opts, config) catch |err| { + log.err("error initializing app err={}", .{err}); + return null; + }; + } + + fn app_new_( + opts: *const apprt.runtime.App.Options, + config: *const Config, + ) !*App { + const core_app = try CoreApp.create(global.alloc); + errdefer core_app.destroy(); + + // Create our runtime app + var app = try global.alloc.create(App); + errdefer global.alloc.destroy(app); + try app.init(core_app, config, opts.*); + errdefer app.terminate(); + + return app; + } + + /// Tick the event loop. This should be called whenever the "wakeup" + /// callback is invoked for the runtime. + export fn ghostty_app_tick(v: *App) void { + v.core_app.tick(v) catch |err| { + log.err("error app tick err={}", .{err}); + }; + } + + /// Return the userdata associated with the app. + export fn ghostty_app_userdata(v: *App) ?*anyopaque { + return v.opts.userdata; + } + + export fn ghostty_app_free(v: *App) void { + const core_app = v.core_app; + v.terminate(); + global.alloc.destroy(v); + core_app.destroy(); + } + + /// Update the focused state of the app. + export fn ghostty_app_set_focus( + app: *App, + focused: bool, + ) void { + app.focusEvent(focused); + } + + /// Notify the app of a global keypress capture. This will return + /// true if the key was captured by the app, in which case the caller + /// should not process the key. + export fn ghostty_app_key( + app: *App, + event: KeyEvent, + ) bool { + return app.keyEvent(.app, event.keyEvent()) catch |err| { + log.warn("error processing key event err={}", .{err}); + return false; + }; + } + + /// Returns true if the given key event would trigger a binding + /// if it were sent to the surface right now. The "right now" + /// is important because things like trigger sequences are only + /// valid until the next key event. + export fn ghostty_app_key_is_binding( + app: *App, + event: KeyEvent, + ) bool { + const core_event = event.keyEvent().core() orelse { + log.warn("error processing key event", .{}); + return false; + }; + + return app.core_app.keyEventIsBinding(app, core_event); + } + + /// Notify the app that the keyboard was changed. This causes the + /// keyboard layout to be reloaded from the OS. + export fn ghostty_app_keyboard_changed(v: *App) void { + v.reloadKeymap() catch |err| { + log.err("error reloading keyboard map err={}", .{err}); + return; + }; + } + + /// Open the configuration. + export fn ghostty_app_open_config(v: *App) void { + _ = v.performAction(.app, .open_config, {}) catch |err| { + log.err("error reloading config err={}", .{err}); + return; + }; + } + + /// Update the configuration to the provided config. This will propagate + /// to all surfaces as well. + export fn ghostty_app_update_config( + v: *App, + config: *const Config, + ) void { + v.core_app.updateConfig(v, config) catch |err| { + log.err("error updating config err={}", .{err}); + return; + }; + } + + /// Returns true if the app needs to confirm quitting. + export fn ghostty_app_needs_confirm_quit(v: *App) bool { + return v.core_app.needsConfirmQuit(); + } + + /// Returns true if the app has global keybinds. + export fn ghostty_app_has_global_keybinds(v: *App) bool { + return v.hasGlobalKeybinds(); + } + + /// Update the color scheme of the app. + export fn ghostty_app_set_color_scheme(v: *App, scheme_raw: c_int) void { + const scheme = std.meta.intToEnum(apprt.ColorScheme, scheme_raw) catch { + log.warn( + "invalid color scheme to ghostty_surface_set_color_scheme value={}", + .{scheme_raw}, + ); + return; + }; + + v.core_app.colorSchemeEvent(v, scheme) catch |err| { + log.err("error setting color scheme err={}", .{err}); + return; + }; + } + + /// Returns initial surface options. + export fn ghostty_surface_config_new() apprt.Surface.Options { + return .{}; + } + + /// Create a new surface as part of an app. + export fn ghostty_surface_new( + app: *App, + opts: *const apprt.Surface.Options, + ) ?*Surface { + return surface_new_(app, opts) catch |err| { + log.err("error initializing surface err={}", .{err}); + return null; + }; + } + + fn surface_new_( + app: *App, + opts: *const apprt.Surface.Options, + ) !*Surface { + return try app.newSurface(opts.*); + } + + export fn ghostty_surface_free(ptr: *Surface) void { + ptr.app.closeSurface(ptr); + } + + /// Returns the userdata associated with the surface. + export fn ghostty_surface_userdata(surface: *Surface) ?*anyopaque { + return surface.userdata; + } + + /// Returns the app associated with a surface. + export fn ghostty_surface_app(surface: *Surface) *App { + return surface.app; + } + + /// Returns the config to use for surfaces that inherit from this one. + export fn ghostty_surface_inherited_config( + surface: *Surface, + source: apprt.surface.NewSurfaceContext, + ) Surface.Options { + return surface.newSurfaceOptions(source); + } + + /// Update the configuration to the provided config for only this surface. + export fn ghostty_surface_update_config( + surface: *Surface, + config: *const Config, + ) void { + surface.core_surface.updateConfig(config) catch |err| { + log.err("error updating config err={}", .{err}); + return; + }; + } + + /// Returns true if the surface needs to confirm quitting. + export fn ghostty_surface_needs_confirm_quit(surface: *Surface) bool { + return surface.core_surface.needsConfirmQuit(); + } + + /// Returns true if the surface process has exited. + export fn ghostty_surface_process_exited(surface: *Surface) bool { + return surface.core_surface.child_exited; + } + + /// Returns true if the surface has a selection. + export fn ghostty_surface_has_selection(surface: *Surface) bool { + return surface.core_surface.hasSelection(); + } + + /// Same as ghostty_surface_read_text but reads from the user selection, + /// if any. + export fn ghostty_surface_read_selection( + surface: *Surface, + result: *Text, + ) bool { + const core_surface = &surface.core_surface; + core_surface.renderer_state.mutex.lock(); + defer core_surface.renderer_state.mutex.unlock(); + + // If we don't have a selection, do nothing. + const core_sel = core_surface.io.terminal.screens.active.selection orelse return false; + + // Read the text from the selection. + return readTextLocked(surface, core_sel, result); + } + + /// Read some arbitrary text from the surface. + /// + /// This is an expensive operation so it shouldn't be called too + /// often. We recommend that callers cache the result and throttle + /// calls to this function. + export fn ghostty_surface_read_text( + surface: *Surface, + sel: Selection, + result: *Text, + ) bool { + surface.core_surface.renderer_state.mutex.lock(); + defer surface.core_surface.renderer_state.mutex.unlock(); + + const core_sel = sel.core( + surface.core_surface.renderer_state.terminal.screens.active, + ) orelse return false; + + return readTextLocked(surface, core_sel, result); + } + + fn readTextLocked( + surface: *Surface, + core_sel: terminal.Selection, + result: *Text, + ) bool { + const core_surface = &surface.core_surface; + + // Get our text directly from the core surface. + const text = core_surface.dumpTextLocked( + global.alloc, + core_sel, + ) catch |err| { + log.warn("error reading text err={}", .{err}); + return false; + }; + + const vp: CoreSurface.Text.Viewport = text.viewport orelse .{ + .tl_px_x = -1, + .tl_px_y = -1, + .offset_start = 0, + .offset_len = 0, + }; + + result.* = .{ + .tl_px_x = vp.tl_px_x, + .tl_px_y = vp.tl_px_y, + .offset_start = vp.offset_start, + .offset_len = vp.offset_len, + .text = text.text.ptr, + .text_len = text.text.len, + }; + + return true; + } + + export fn ghostty_surface_free_text(_: *Surface, ptr: *Text) void { + ptr.deinit(); + } + + /// Tell the surface that it needs to schedule a render + export fn ghostty_surface_refresh(surface: *Surface) void { + surface.refresh(); + } + + /// Tell the surface that it needs to schedule a render + /// call as soon as possible (NOW if possible). + export fn ghostty_surface_draw(surface: *Surface) void { + surface.draw(); + } + + /// Update the size of a surface. This will trigger resize notifications + /// to the pty and the renderer. + export fn ghostty_surface_set_size(surface: *Surface, w: u32, h: u32) void { + surface.updateSize(w, h); + } + + /// Return the size information a surface has. + export fn ghostty_surface_size(surface: *Surface) SurfaceSize { + const grid_size = surface.core_surface.size.grid(); + return .{ + .columns = grid_size.columns, + .rows = grid_size.rows, + .width_px = surface.core_surface.size.screen.width, + .height_px = surface.core_surface.size.screen.height, + .cell_width_px = surface.core_surface.size.cell.width, + .cell_height_px = surface.core_surface.size.cell.height, + }; + } + + /// Update the color scheme of the surface. + export fn ghostty_surface_set_color_scheme(surface: *Surface, scheme_raw: c_int) void { + const scheme = std.meta.intToEnum(apprt.ColorScheme, scheme_raw) catch { + log.warn( + "invalid color scheme to ghostty_surface_set_color_scheme value={}", + .{scheme_raw}, + ); + return; + }; + + surface.colorSchemeCallback(scheme); + } + + /// Update the content scale of the surface. + export fn ghostty_surface_set_content_scale(surface: *Surface, x: f64, y: f64) void { + surface.updateContentScale(x, y); + } + + /// Update the focused state of a surface. + export fn ghostty_surface_set_focus(surface: *Surface, focused: bool) void { + surface.focusCallback(focused); + } + + /// Update the occlusion state of a surface. + export fn ghostty_surface_set_occlusion(surface: *Surface, visible: bool) void { + surface.occlusionCallback(visible); + } + + /// Filter the mods if necessary. This handles settings such as + /// `macos-option-as-alt`. The filtered mods should be used for + /// key translation but should NOT be sent back via the `_key` + /// function -- the original mods should be used for that. + export fn ghostty_surface_key_translation_mods( + surface: *Surface, + mods_raw: c_int, + ) c_int { + const mods: input.Mods = @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(mods_raw))), + )); + const result = mods.translation( + surface.core_surface.config.macos_option_as_alt orelse + surface.app.keyboardLayout().detectOptionAsAlt(), + ); + return @intCast(@as(input.Mods.Backing, @bitCast(result))); + } + + /// Send this for raw keypresses (i.e. the keyDown event on macOS). + /// This will handle the keymap translation and send the appropriate + /// key and char events. + export fn ghostty_surface_key( + surface: *Surface, + event: KeyEvent, + ) bool { + return surface.app.keyEvent( + .{ .surface = surface }, + event.keyEvent(), + ) catch |err| { + log.warn("error processing key event err={}", .{err}); + return false; + }; + } + + /// Returns true if the given key event would trigger a binding + /// if it were sent to the surface right now. The "right now" + /// is important because things like trigger sequences are only + /// valid until the next key event. + export fn ghostty_surface_key_is_binding( + surface: *Surface, + event: KeyEvent, + c_flags: ?*input.Binding.Flags.C, + ) bool { + const core_event = event.keyEvent().core() orelse { + log.warn("error processing key event", .{}); + return false; + }; + + const flags = surface.core_surface.keyEventIsBinding( + core_event, + ) orelse return false; + if (c_flags) |ptr| ptr.* = flags.cval(); + return true; + } + + /// Send raw text to the terminal. This is treated like a paste + /// so this isn't useful for sending escape sequences. For that, + /// individual key input should be used. + export fn ghostty_surface_text( + surface: *Surface, + ptr: [*]const u8, + len: usize, + ) void { + surface.textCallback(ptr[0..len]); + } + + /// Set the preedit text for the surface. This is used for IME + /// composition. If the length is 0, then the preedit text is cleared. + export fn ghostty_surface_preedit( + surface: *Surface, + ptr: [*]const u8, + len: usize, + ) void { + surface.preeditCallback(if (len == 0) null else ptr[0..len]); + } + + /// Returns true if the surface currently has mouse capturing + /// enabled. + export fn ghostty_surface_mouse_captured(surface: *Surface) bool { + return surface.core_surface.mouseCaptured(); + } + + /// Tell the surface that it needs to schedule a render + export fn ghostty_surface_mouse_button( + surface: *Surface, + action: input.MouseButtonState, + button: input.MouseButton, + mods: c_int, + ) bool { + return surface.mouseButtonCallback( + action, + button, + @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(mods))), + )), + ); + } + + /// Update the mouse position within the view. + export fn ghostty_surface_mouse_pos( + surface: *Surface, + x: f64, + y: f64, + mods: c_int, + ) void { + surface.cursorPosCallback( + x, + y, + @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(mods))), + )), + ); + } + + export fn ghostty_surface_mouse_scroll( + surface: *Surface, + x: f64, + y: f64, + scroll_mods: c_int, + ) void { + surface.scrollCallback( + x, + y, + @bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(scroll_mods))))), + ); + } + + export fn ghostty_surface_mouse_pressure( + surface: *Surface, + stage_raw: u32, + pressure: f64, + ) void { + const stage = std.meta.intToEnum( + input.MousePressureStage, + stage_raw, + ) catch { + log.warn( + "invalid mouse pressure stage value={}", + .{stage_raw}, + ); + return; + }; + + surface.mousePressureCallback(stage, pressure); + } + + export fn ghostty_surface_ime_point( + surface: *Surface, + x: *f64, + y: *f64, + width: *f64, + height: *f64, + ) void { + const pos = surface.core_surface.imePoint(); + x.* = pos.x; + y.* = pos.y; + width.* = pos.width; + height.* = pos.height; + } + + /// Request that the surface become closed. This will go through the + /// normal trigger process that a close surface input binding would. + export fn ghostty_surface_request_close(ptr: *Surface) void { + ptr.core_surface.close(); + } + + /// Request that the surface split in the given direction. + export fn ghostty_surface_split(ptr: *Surface, direction: apprt.action.SplitDirection) void { + _ = ptr.app.performAction( + .{ .surface = &ptr.core_surface }, + .new_split, + direction, + ) catch |err| { + log.err("error creating new split err={}", .{err}); + return; + }; + } + + /// Focus on the next split (if any). + export fn ghostty_surface_split_focus( + ptr: *Surface, + direction: apprt.action.GotoSplit, + ) void { + _ = ptr.app.performAction( + .{ .surface = &ptr.core_surface }, + .goto_split, + direction, + ) catch |err| { + log.err("error creating new split err={}", .{err}); + return; + }; + } + + /// Resize the current split by moving the split divider in the given + /// direction. `direction` specifies which direction the split divider will + /// move relative to the focused split. `amount` is a fractional value + /// between 0 and 1 that specifies by how much the divider will move. + export fn ghostty_surface_split_resize( + ptr: *Surface, + direction: apprt.action.ResizeSplit.Direction, + amount: u16, + ) void { + _ = ptr.app.performAction( + .{ .surface = &ptr.core_surface }, + .resize_split, + .{ .direction = direction, .amount = amount }, + ) catch |err| { + log.err("error resizing split err={}", .{err}); + return; + }; + } + + /// Equalize the size of all splits in the current window. + export fn ghostty_surface_split_equalize(ptr: *Surface) void { + _ = ptr.app.performAction( + .{ .surface = &ptr.core_surface }, + .equalize_splits, + {}, + ) catch |err| { + log.err("error equalizing splits err={}", .{err}); + return; + }; + } + + /// Invoke an action on the surface. + export fn ghostty_surface_binding_action( + ptr: *Surface, + action_ptr: [*]const u8, + action_len: usize, + ) bool { + const action_str = action_ptr[0..action_len]; + const action = input.Binding.Action.parse(action_str) catch |err| { + log.err("error parsing binding action action={s} err={}", .{ action_str, err }); + return false; + }; + + return ptr.core_surface.performBindingAction(action) catch |err| { + log.err("error performing binding action action={f} err={}", .{ action, err }); + return false; + }; + } + + /// Complete a clipboard read request started via the read callback. + /// This can only be called once for a given request. Once it is called + /// with a request the request pointer will be invalidated. + export fn ghostty_surface_complete_clipboard_request( + ptr: *Surface, + str: [*:0]const u8, + state: *apprt.ClipboardRequest, + confirmed: bool, + ) void { + ptr.completeClipboardRequest( + std.mem.sliceTo(str, 0), + state, + confirmed, + ); + } + + export fn ghostty_surface_inspector(ptr: *Surface) ?*Inspector { + return ptr.initInspector() catch |err| { + log.err("error initializing inspector err={}", .{err}); + return null; + }; + } + + export fn ghostty_inspector_free(ptr: *Surface) void { + ptr.freeInspector(); + } + + export fn ghostty_inspector_set_size(ptr: *Inspector, w: u32, h: u32) void { + ptr.updateSize(w, h); + } + + export fn ghostty_inspector_set_content_scale(ptr: *Inspector, x: f64, y: f64) void { + ptr.updateContentScale(x, y); + } + + export fn ghostty_inspector_mouse_button( + ptr: *Inspector, + action: input.MouseButtonState, + button: input.MouseButton, + mods: c_int, + ) void { + ptr.mouseButtonCallback( + action, + button, + @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(mods))), + )), + ); + } + + export fn ghostty_inspector_mouse_pos(ptr: *Inspector, x: f64, y: f64) void { + ptr.cursorPosCallback(x, y); + } + + export fn ghostty_inspector_mouse_scroll( + ptr: *Inspector, + x: f64, + y: f64, + scroll_mods: c_int, + ) void { + ptr.scrollCallback( + x, + y, + @bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(scroll_mods))))), + ); + } + + export fn ghostty_inspector_key( + ptr: *Inspector, + action: input.Action, + key: input.Key, + c_mods: c_int, + ) void { + ptr.keyCallback( + action, + key, + @bitCast(@as( + input.Mods.Backing, + @truncate(@as(c_uint, @bitCast(c_mods))), + )), + ) catch |err| { + log.err("error processing key event err={}", .{err}); + return; + }; + } + + export fn ghostty_inspector_text( + ptr: *Inspector, + str: [*:0]const u8, + ) void { + ptr.textCallback(std.mem.sliceTo(str, 0)); + } + + export fn ghostty_inspector_set_focus(ptr: *Inspector, focused: bool) void { + ptr.focusCallback(focused); + } + + /// Sets the window background blur on macOS to the desired value. + /// I do this in Zig as an extern function because I don't know how to + /// call these functions in Swift. + /// + /// This uses an undocumented, non-public API because this is what + /// every terminal appears to use, including Terminal.app. + export fn ghostty_set_window_background_blur( + app: *App, + window: *anyopaque, + ) void { + // This is only supported on macOS + if (comptime builtin.target.os.tag != .macos) return; + + const config = &app.config; + + // Do nothing if we don't have background transparency enabled + if (config.@"background-opacity" >= 1.0) return; + + const nswindow = objc.Object.fromId(window); + _ = CGSSetWindowBackgroundBlurRadius( + CGSDefaultConnectionForThread(), + nswindow.msgSend(usize, objc.sel("windowNumber"), .{}), + @intCast(config.@"background-blur".cval()), + ); + } + + /// See ghostty_set_window_background_blur + extern "c" fn CGSSetWindowBackgroundBlurRadius(*anyopaque, usize, c_int) i32; + extern "c" fn CGSDefaultConnectionForThread() *anyopaque; + + // Darwin-only C APIs. + const Darwin = struct { + export fn ghostty_surface_set_display_id(ptr: *Surface, display_id: u32) void { + const surface = &ptr.core_surface; + _ = surface.renderer_thread.mailbox.push( + .{ .macos_display_id = display_id }, + .{ .forever = {} }, + ); + surface.renderer_thread.wakeup.notify() catch {}; + } + + /// This returns a CTFontRef that should be used for quicklook + /// highlighted text. This is always the primary font in use + /// regardless of the selected text. If coretext is not in use + /// then this will return nothing. + export fn ghostty_surface_quicklook_font(ptr: *Surface) ?*anyopaque { + // For non-CoreText we just return null. + if (comptime font.options.backend != .coretext) { + return null; + } + + // We'll need content scale so fail early if we can't get it. + const content_scale = ptr.getContentScale() catch return null; + + // Get the shared font grid. We acquire a read lock to + // read the font face. It should not be deferred since + // we're loading the primary face. + const grid = ptr.core_surface.renderer.font_grid; + grid.lock.lockShared(); + defer grid.lock.unlockShared(); + + const collection = &grid.resolver.collection; + const face = collection.getFace(.{}) catch return null; + + // We need to unscale the content scale. We apply the + // content scale to our font stack because we are rendering + // at 1x but callers of this should be using scaled or apply + // scale themselves. + const size: f32 = size: { + const num = face.font.copyAttribute(.size) orelse + break :size 12; + defer num.release(); + var v: f32 = 12; + _ = num.getValue(.float, &v); + break :size v; + }; + + const copy = face.font.copyWithAttributes( + size / content_scale.y, + null, + null, + ) catch return null; + + return copy; + } + + /// This returns the selected word for quicklook. This will populate + /// the buffer with the word under the cursor and the selection + /// info so that quicklook can be rendered. + /// + /// This does not modify the selection active on the surface (if any). + export fn ghostty_surface_quicklook_word( + ptr: *Surface, + result: *Text, + ) bool { + const surface = &ptr.core_surface; + surface.renderer_state.mutex.lock(); + defer surface.renderer_state.mutex.unlock(); + + // Get our word selection + const sel = sel: { + const screen: *terminal.Screen = surface.renderer_state.terminal.screens.active; + const pos = try ptr.getCursorPos(); + const pt_viewport = surface.posToViewport(pos.x, pos.y); + const pin = screen.pages.pin(.{ + .viewport = .{ + .x = pt_viewport.x, + .y = pt_viewport.y, + }, + }) orelse { + if (comptime std.debug.runtime_safety) unreachable; + return false; + }; + break :sel surface.io.terminal.screens.active.selectWord( + pin, + surface.config.selection_word_chars, + ) orelse return false; + }; + + // Read the selection + return readTextLocked(ptr, sel, result); + } + + export fn ghostty_inspector_metal_init(ptr: *Inspector, device: objc.c.id) bool { + return ptr.initMetal(.fromId(device)); + } + + export fn ghostty_inspector_metal_render( + ptr: *Inspector, + command_buffer: objc.c.id, + descriptor: objc.c.id, + ) void { + return ptr.renderMetal( + .fromId(command_buffer), + .fromId(descriptor), + ) catch |err| { + log.err("error rendering inspector err={}", .{err}); + return; + }; + } + + export fn ghostty_inspector_metal_shutdown(ptr: *Inspector) void { + if (ptr.backend) |v| { + v.deinit(); + ptr.backend = null; + } + } + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk.zig b/vendor/libghostty-vt/src/apprt/gtk.zig new file mode 100644 index 00000000..eb33c4e4 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk.zig @@ -0,0 +1,17 @@ +// The required comptime API for any apprt. +pub const App = @import("gtk/App.zig"); +pub const Surface = @import("gtk/Surface.zig"); +pub const resourcesDir = @import("gtk/flatpak.zig").resourcesDir; + +// The exported API, custom for the apprt. +pub const class = @import("gtk/class.zig"); +pub const WeakRef = @import("gtk/weak_ref.zig").WeakRef; +pub const pre_exec = @import("gtk/pre_exec.zig"); +pub const post_fork = @import("gtk/post_fork.zig"); + +test { + @import("std").testing.refAllDecls(@This()); + _ = @import("gtk/ext.zig"); + _ = @import("gtk/key.zig"); + _ = @import("gtk/portal.zig"); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/App.zig b/vendor/libghostty-vt/src/apprt/gtk/App.zig new file mode 100644 index 00000000..39c13c19 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/App.zig @@ -0,0 +1,93 @@ +/// This is the main entrypoint to the apprt for Ghostty. Ghostty will +/// initialize this in main to start the application.. +const App = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; +const apprt = @import("../../apprt.zig"); +const configpkg = @import("../../config.zig"); +const Config = configpkg.Config; +const CoreApp = @import("../../App.zig"); + +const Application = @import("class/application.zig").Application; +const Surface = @import("Surface.zig"); +const ipcNewWindow = @import("ipc/new_window.zig").newWindow; + +const log = std.log.scoped(.gtk); + +/// This is detected by the Renderer, in which case it sends a `redraw_surface` +/// message so that we can call `drawFrame` ourselves from the app thread, +/// because GTK's `GLArea` does not support drawing from a different thread. +pub const must_draw_from_app_thread = true; + +/// GTK application ID +pub const application_id = @import("build/info.zig").application_id; + +/// GTK object path +pub const object_path = @import("build/info.zig").object_path; + +/// The GObject Application instance +app: *Application, + +pub fn init( + self: *App, + core_app: *CoreApp, + + // Required by the apprt interface but we don't use it. + opts: struct {}, +) !void { + _ = opts; + + const app: *Application = try .new(self, core_app); + errdefer app.unref(); + self.* = .{ .app = app }; + return; +} + +pub fn run(self: *App) !void { + try self.app.run(); +} + +pub fn terminate(self: *App) void { + // We force deinitialize the app. We don't unref because other things + // tend to have a reference at this point, so this just forces the + // disposal now. + self.app.deinit(); +} + +/// Called by CoreApp to wake up the event loop. +pub fn wakeup(self: *App) void { + self.app.wakeup(); +} + +pub fn performAction( + self: *App, + target: apprt.Target, + comptime action: apprt.Action.Key, + value: apprt.Action.Value(action), +) !bool { + return try self.app.performAction(target, action, value); +} + +/// Send the given IPC to a running Ghostty. Returns `true` if the action was +/// able to be performed, `false` otherwise. +/// +/// Note that this is a static function. Since this is called from a CLI app (or +/// some other process that is not Ghostty) there is no full-featured apprt App +/// to use. +pub fn performIpc( + alloc: Allocator, + target: apprt.ipc.Target, + comptime action: apprt.ipc.Action.Key, + value: apprt.ipc.Action.Value(action), +) !bool { + switch (action) { + .new_window => return try ipcNewWindow(alloc, target, value), + } +} + +/// Redraw the inspector for the given surface. +pub fn redrawInspector(_: *App, surface: *Surface) void { + surface.redrawInspector(); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/Surface.zig b/vendor/libghostty-vt/src/apprt/gtk/Surface.zig new file mode 100644 index 00000000..71597367 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/Surface.zig @@ -0,0 +1,104 @@ +const Self = @This(); + +const std = @import("std"); +const apprt = @import("../../apprt.zig"); +const configpkg = @import("../../config.zig"); +const CoreSurface = @import("../../Surface.zig"); +const ApprtApp = @import("App.zig"); +const Application = @import("class/application.zig").Application; +const Surface = @import("class/surface.zig").Surface; + +/// The GObject Surface +surface: *Surface, + +pub fn deinit(self: *Self) void { + _ = self; +} + +/// Returns the GObject surface for this apprt surface. This is a function +/// so we can add some extra logic if we ever have to here. +pub fn gobj(self: *Self) *Surface { + return self.surface; +} + +pub fn core(self: *Self) *CoreSurface { + // This asserts the non-optional because libghostty should only + // be calling this for initialized surfaces. + return self.surface.core().?; +} + +pub fn rtApp(self: *Self) *ApprtApp { + _ = self; + return Application.default().rt(); +} + +pub fn close(self: *Self, process_active: bool) void { + _ = process_active; + self.surface.close(); +} + +pub fn cgroup(self: *Self) ?[]const u8 { + return self.surface.cgroupPath(); +} + +pub fn getTitle(self: *Self) ?[:0]const u8 { + return self.surface.getTitle(); +} + +pub fn getContentScale(self: *const Self) !apprt.ContentScale { + return self.surface.getContentScale(); +} + +pub fn getSize(self: *const Self) !apprt.SurfaceSize { + return self.surface.getSize(); +} + +pub fn getCursorPos(self: *const Self) !apprt.CursorPos { + return self.surface.getCursorPos(); +} + +pub fn supportsClipboard( + self: *const Self, + clipboard_type: apprt.Clipboard, +) bool { + _ = self; + return switch (clipboard_type) { + .standard, + .selection, + .primary, + => true, + }; +} + +pub fn clipboardRequest( + self: *Self, + clipboard_type: apprt.Clipboard, + state: apprt.ClipboardRequest, +) !bool { + return try self.surface.clipboardRequest( + clipboard_type, + state, + ); +} + +pub fn setClipboard( + self: *Self, + clipboard_type: apprt.Clipboard, + contents: []const apprt.ClipboardContent, + confirm: bool, +) !void { + self.surface.setClipboard( + clipboard_type, + contents, + confirm, + ); +} + +pub fn defaultTermioEnv(self: *Self) !std.process.EnvMap { + return try self.surface.defaultTermioEnv(); +} + +/// Redraw the inspector for our surface. +pub fn redrawInspector(self: *Self) void { + self.surface.redrawInspector(); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/adw_version.zig b/vendor/libghostty-vt/src/apprt/gtk/adw_version.zig new file mode 100644 index 00000000..6f7be52d --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/adw_version.zig @@ -0,0 +1,122 @@ +const std = @import("std"); + +// Until the gobject bindings are built at the same time we are building +// Ghostty, we need to import `adwaita.h` directly to ensure that the version +// macros match the version of `libadwaita` that we are building/linking +// against. +const c = @cImport({ + @cInclude("adwaita.h"); +}); + +const adw = @import("adw"); + +const log = std.log.scoped(.gtk); + +pub const comptime_version: std.SemanticVersion = .{ + .major = c.ADW_MAJOR_VERSION, + .minor = c.ADW_MINOR_VERSION, + .patch = c.ADW_MICRO_VERSION, +}; + +pub fn getRuntimeVersion() std.SemanticVersion { + return .{ + .major = adw.getMajorVersion(), + .minor = adw.getMinorVersion(), + .patch = adw.getMicroVersion(), + }; +} + +pub fn logVersion() void { + log.info("libadwaita version build={f} runtime={f}", .{ + comptime_version, + getRuntimeVersion(), + }); +} + +/// Verifies that the running libadwaita version is at least the given +/// version. This will return false if Ghostty is configured to not build with +/// libadwaita. +/// +/// This can be run in both a comptime and runtime context. If it is run in a +/// comptime context, it will only check the version in the headers. If it is +/// run in a runtime context, it will check the actual version of the library we +/// are linked against. So generally you probably want to do both checks! +/// +/// This is inlined so that the comptime checks will disable the runtime checks +/// if the comptime checks fail. +pub inline fn atLeast( + comptime major: u16, + comptime minor: u16, + comptime micro: u16, +) bool { + // If our header has lower versions than the given version, we can return + // false immediately. This prevents us from compiling against unknown + // symbols and makes runtime checks very slightly faster. + if (comptime comptime_version.order(.{ + .major = major, + .minor = minor, + .patch = micro, + }) == .lt) return false; + + // If we're in comptime then we can't check the runtime version. + if (@inComptime()) return true; + + return runtimeAtLeast(major, minor, micro); +} + +/// Verifies that the libadwaita version at runtime is at least the given version. +/// +/// This function should be used in cases where the only the runtime behavior +/// is affected by the version check. For checks which would affect code +/// generation, use `atLeast`. +pub inline fn runtimeAtLeast( + comptime major: u16, + comptime minor: u16, + comptime micro: u16, +) bool { + // We use the functions instead of the constants such as c.GTK_MINOR_VERSION + // because the function gets the actual runtime version. + const runtime_version = getRuntimeVersion(); + return runtime_version.order(.{ + .major = major, + .minor = minor, + .patch = micro, + }) != .lt; +} + +test "versionAtLeast" { + const testing = std.testing; + + const funs = &.{ atLeast, runtimeAtLeast }; + inline for (funs) |fun| { + try testing.expect(fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION)); + try testing.expect(!fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION + 1)); + try testing.expect(!fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION + 1, c.ADW_MICRO_VERSION)); + try testing.expect(!fun(c.ADW_MAJOR_VERSION + 1, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION)); + try testing.expect(fun(c.ADW_MAJOR_VERSION - 1, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION)); + try testing.expect(fun(c.ADW_MAJOR_VERSION - 1, c.ADW_MINOR_VERSION + 1, c.ADW_MICRO_VERSION)); + try testing.expect(fun(c.ADW_MAJOR_VERSION - 1, c.ADW_MINOR_VERSION, c.ADW_MICRO_VERSION + 1)); + try testing.expect(fun(c.ADW_MAJOR_VERSION, c.ADW_MINOR_VERSION - 1, c.ADW_MICRO_VERSION + 1)); + } +} + +// Whether AdwDialog, AdwAlertDialog, etc. are supported (1.5+) +pub inline fn supportsDialogs() bool { + return atLeast(1, 5, 0); +} + +pub inline fn supportsTabOverview() bool { + return atLeast(1, 4, 0); +} + +pub inline fn supportsSwitchRow() bool { + return atLeast(1, 4, 0); +} + +pub inline fn supportsToolbarView() bool { + return atLeast(1, 4, 0); +} + +pub inline fn supportsBanner() bool { + return atLeast(1, 3, 0); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/build/blueprint.zig b/vendor/libghostty-vt/src/apprt/gtk/build/blueprint.zig new file mode 100644 index 00000000..4920ce6f --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/build/blueprint.zig @@ -0,0 +1,174 @@ +//! Compiles a blueprint file using `blueprint-compiler`. This performs +//! additional checks to ensure that various minimum versions are met. +//! +//! Usage: blueprint.zig +//! +//! Example: blueprint.zig 1 5 output.ui input.blp + +const std = @import("std"); + +pub const c = @cImport({ + @cInclude("adwaita.h"); +}); + +pub const blueprint_compiler_help = + \\ + \\When building from a Git checkout, Ghostty requires + \\version {f} or newer of `blueprint-compiler` as a + \\build-time dependency. Please install it, ensure that it + \\is available on your PATH, and then retry building Ghostty. + \\See `HACKING.md` for more details. + \\ + \\This message should *not* appear for normal users, who + \\should build Ghostty from official release tarballs instead. + \\Please consult https://ghostty.org/docs/install/build for + \\more information on the recommended build instructions. +; + +const adwaita_version = std.SemanticVersion{ + .major = c.ADW_MAJOR_VERSION, + .minor = c.ADW_MINOR_VERSION, + .patch = c.ADW_MICRO_VERSION, +}; +const required_blueprint_version = std.SemanticVersion{ + .major = 0, + .minor = 16, + .patch = 0, +}; + +pub fn main() !void { + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer _ = debug_allocator.deinit(); + const alloc = debug_allocator.allocator(); + + // Get our args + var it = try std.process.argsWithAllocator(alloc); + defer it.deinit(); + _ = it.next(); // Skip argv0 + const arg_major = it.next() orelse return error.NoMajorVersion; + const arg_minor = it.next() orelse return error.NoMinorVersion; + const output = it.next() orelse return error.NoOutput; + const input = it.next() orelse return error.NoInput; + + const required_adwaita_version = std.SemanticVersion{ + .major = try std.fmt.parseUnsigned(u8, arg_major, 10), + .minor = try std.fmt.parseUnsigned(u8, arg_minor, 10), + .patch = 0, + }; + if (adwaita_version.order(required_adwaita_version) == .lt) { + std.debug.print( + \\`libadwaita` is too old. + \\ + \\Ghostty requires a version {f} or newer of `libadwaita` to + \\compile this blueprint. Please install it, ensure that it is + \\available on your PATH, and then retry building Ghostty. + , .{required_adwaita_version}); + std.posix.exit(1); + } + + // Version checks + { + var stdout: std.ArrayListUnmanaged(u8) = .empty; + defer stdout.deinit(alloc); + var stderr: std.ArrayListUnmanaged(u8) = .empty; + defer stderr.deinit(alloc); + + var blueprint_compiler = std.process.Child.init( + &.{ + "blueprint-compiler", + "--version", + }, + alloc, + ); + blueprint_compiler.stdout_behavior = .Pipe; + blueprint_compiler.stderr_behavior = .Pipe; + try blueprint_compiler.spawn(); + try blueprint_compiler.collectOutput( + alloc, + &stdout, + &stderr, + std.math.maxInt(u16), + ); + const term = blueprint_compiler.wait() catch |err| switch (err) { + error.FileNotFound => { + std.debug.print( + \\`blueprint-compiler` not found. + ++ blueprint_compiler_help, + .{required_blueprint_version}, + ); + std.posix.exit(1); + }, + else => return err, + }; + switch (term) { + .Exited => |rc| if (rc != 0) std.process.exit(1), + else => std.process.exit(1), + } + + const version = try std.SemanticVersion.parse(std.mem.trim( + u8, + stdout.items, + &std.ascii.whitespace, + )); + if (version.order(required_blueprint_version) == .lt) { + std.debug.print( + \\`blueprint-compiler` is the wrong version. + ++ blueprint_compiler_help, + .{required_blueprint_version}, + ); + std.posix.exit(1); + } + } + + // Compilation + { + var stdout: std.ArrayListUnmanaged(u8) = .empty; + defer stdout.deinit(alloc); + var stderr: std.ArrayListUnmanaged(u8) = .empty; + defer stderr.deinit(alloc); + + var blueprint_compiler = std.process.Child.init( + &.{ + "blueprint-compiler", + "compile", + "--output", + output, + input, + }, + alloc, + ); + blueprint_compiler.stdout_behavior = .Pipe; + blueprint_compiler.stderr_behavior = .Pipe; + try blueprint_compiler.spawn(); + try blueprint_compiler.collectOutput( + alloc, + &stdout, + &stderr, + std.math.maxInt(u16), + ); + const term = blueprint_compiler.wait() catch |err| switch (err) { + error.FileNotFound => { + std.debug.print( + \\`blueprint-compiler` not found. + ++ blueprint_compiler_help, + .{required_blueprint_version}, + ); + std.posix.exit(1); + }, + else => return err, + }; + + switch (term) { + .Exited => |rc| { + if (rc != 0) { + std.debug.print("{s}", .{stderr.items}); + std.process.exit(1); + } + }, + else => { + std.debug.print("{s}", .{stderr.items}); + std.process.exit(1); + }, + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/build/gresource.zig b/vendor/libghostty-vt/src/apprt/gtk/build/gresource.zig new file mode 100644 index 00000000..c50ea8cd --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/build/gresource.zig @@ -0,0 +1,277 @@ +//! This file contains a binary helper that builds our gresource XML +//! file that we can then use with `glib-compile-resources`. +//! +//! This binary is expected to be run from the Ghostty source root. +//! Litmus test: `src/apprt/gtk` should exist relative to the pwd. + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const build_info = @import("info.zig"); + +/// The path to the Blueprint files. The folder structure is expected to be +/// `{version}/{name}.blp` where `version` is the major and minor +/// minimum adwaita version. +pub const ui_path = "src/apprt/gtk/ui"; + +/// The path to the CSS files. +pub const css_path = "src/apprt/gtk/css"; + +/// The possible icon sizes we'll embed into the gresource file. +/// If any size doesn't exist then it will be an error. We could +/// infer this completely from available files but we wouldn't be +/// able to error when they don't exist that way. +pub const icon_sizes: []const comptime_int = &.{ 16, 32, 128, 256, 512, 1024 }; + +/// The blueprint files that we will embed into the gresource file. +/// We can't look these up at runtime [easily] because we require the +/// compiled UI files as input. We can refactor this lator to maybe do +/// all of this automatically and ensure we have the right dependencies +/// setup in the build system. +/// +/// These will be asserted to exist at runtime. +pub const blueprints: []const Blueprint = &.{ + .{ .major = 1, .minor = 0, .name = "clipboard-confirmation-dialog" }, + .{ .major = 1, .minor = 4, .name = "clipboard-confirmation-dialog" }, + .{ .major = 1, .minor = 2, .name = "close-confirmation-dialog" }, + .{ .major = 1, .minor = 2, .name = "config-errors-dialog" }, + .{ .major = 1, .minor = 2, .name = "debug-warning" }, + .{ .major = 1, .minor = 3, .name = "debug-warning" }, + .{ .major = 1, .minor = 5, .name = "imgui-widget" }, + .{ .major = 1, .minor = 5, .name = "inspector-widget" }, + .{ .major = 1, .minor = 5, .name = "inspector-window" }, + .{ .major = 1, .minor = 2, .name = "resize-overlay" }, + .{ .major = 1, .minor = 2, .name = "search-overlay" }, + .{ .major = 1, .minor = 2, .name = "key-state-overlay" }, + .{ .major = 1, .minor = 5, .name = "split-tree" }, + .{ .major = 1, .minor = 5, .name = "split-tree-split" }, + .{ .major = 1, .minor = 2, .name = "surface" }, + .{ .major = 1, .minor = 5, .name = "surface-scrolled-window" }, + .{ .major = 1, .minor = 3, .name = "surface-child-exited" }, + .{ .major = 1, .minor = 5, .name = "tab" }, + .{ .major = 1, .minor = 5, .name = "title-dialog" }, + .{ .major = 1, .minor = 5, .name = "window" }, + .{ .major = 1, .minor = 5, .name = "command-palette" }, +}; + +/// CSS files in css_path +pub const css = [_][]const u8{ + "style.css", + "style-dark.css", + "style-hc.css", + "style-hc-dark.css", +}; + +pub const Blueprint = struct { + major: u16, + minor: u16, + name: []const u8, +}; + +/// The list of filepaths that we depend on. Used for the build +/// system to have proper caching. +pub const file_inputs = deps: { + const total = (icon_sizes.len * 2) + blueprints.len + css.len; + var deps: [total][]const u8 = undefined; + var index: usize = 0; + for (icon_sizes) |size| { + deps[index] = std.fmt.comptimePrint("images/gnome/{d}.png", .{size}); + deps[index + 1] = std.fmt.comptimePrint("images/gnome/{d}.png", .{size * 2}); + index += 2; + } + for (blueprints) |bp| { + deps[index] = std.fmt.comptimePrint("{s}/{d}.{d}/{s}.blp", .{ + ui_path, + bp.major, + bp.minor, + bp.name, + }); + index += 1; + } + for (css) |name| { + deps[index] = std.fmt.comptimePrint("{s}/{s}", .{ css_path, name }); + index += 1; + } + break :deps deps; +}; + +/// Returns the matching blueprint resource path for the given blueprint +/// definition. This will fail at compile time if the blueprint is not +/// found. +/// +/// Must be called at comptime. +pub fn blueprint(comptime bp: Blueprint) [:0]const u8 { + // The comptime block around this whole thing forces an error if + // the caller attempts to call this function at runtime. + comptime { + for (blueprints) |candidate| { + if (candidate.major == bp.major and + candidate.minor == bp.minor and + std.mem.eql(u8, candidate.name, bp.name)) + { + return std.fmt.comptimePrint("{s}/ui/{d}.{d}/{s}.ui", .{ + build_info.resource_path, + candidate.major, + candidate.minor, + candidate.name, + }); + } + } + + @compileError("invalid blueprint"); + } +} + +pub fn main() !void { + var debug_allocator: std.heap.DebugAllocator(.{}) = .init; + defer _ = debug_allocator.deinit(); + const alloc = debug_allocator.allocator(); + + // Collect the UI files that are passed in as arguments. + var ui_files: std.ArrayListUnmanaged([]const u8) = .empty; + defer { + for (ui_files.items) |item| alloc.free(item); + ui_files.deinit(alloc); + } + var it = try std.process.argsWithAllocator(alloc); + defer it.deinit(); + while (it.next()) |arg| { + if (!std.mem.endsWith(u8, arg, ".ui")) continue; + try ui_files.append( + alloc, + try alloc.dupe(u8, arg), + ); + } + + var buf: [4096]u8 = undefined; + var stdout = std.fs.File.stdout().writer(&buf); + const writer = &stdout.interface; + try writer.writeAll( + \\ + \\ + \\ + ); + + try genRoot(writer); + try genIcons(writer); + try genUi(alloc, writer, &ui_files); + + try writer.writeAll( + \\ + \\ + ); + + try stdout.end(); +} + +/// Generate the icon resources. This works by looking up all the icons +/// specified by `icon_sizes` in `images/icons/`. They are asserted to exist +/// by trying to access the file. +fn genIcons(writer: *std.Io.Writer) !void { + try writer.print( + \\ + \\ + , .{build_info.resource_path}); + + const cwd = std.fs.cwd(); + inline for (icon_sizes) |size| { + // 1x + { + const alias = std.fmt.comptimePrint("{d}x{d}", .{ size, size }); + const source = std.fmt.comptimePrint("images/gnome/{d}.png", .{size}); + try cwd.access(source, .{}); + try writer.print( + \\ {s} + \\ + , + .{ alias, build_info.base_application_id, source }, + ); + } + + // 2x + { + const alias = std.fmt.comptimePrint("{d}x{d}@2", .{ size, size }); + const source = std.fmt.comptimePrint("images/gnome/{d}.png", .{size * 2}); + try cwd.access(source, .{}); + try writer.print( + \\ {s} + \\ + , + .{ alias, build_info.base_application_id, source }, + ); + } + } + + try writer.writeAll( + \\ + \\ + ); +} + +/// Generate the resources at the root prefix. +fn genRoot(writer: *std.Io.Writer) !void { + try writer.print( + \\ + \\ + , .{build_info.resource_path}); + + const cwd = std.fs.cwd(); + inline for (css) |name| { + const source = std.fmt.comptimePrint( + "{s}/{s}", + .{ css_path, name }, + ); + try cwd.access(source, .{}); + try writer.print( + \\ {s} + \\ + , + .{ name, source }, + ); + } + + try writer.writeAll( + \\ + \\ + ); +} + +/// Generate all the UI resources. This works by looking up all the +/// blueprint files in `${ui_path}/{major}.{minor}/{name}.blp` and +/// assuming these will be +fn genUi( + alloc: Allocator, + writer: *std.Io.Writer, + files: *const std.ArrayListUnmanaged([]const u8), +) !void { + try writer.print( + \\ + \\ + , .{build_info.resource_path}); + + for (files.items) |ui_file| { + for (blueprints) |bp| { + const expected = try std.fmt.allocPrint( + alloc, + "/{d}.{d}/{s}.ui", + .{ bp.major, bp.minor, bp.name }, + ); + defer alloc.free(expected); + if (!std.mem.endsWith(u8, ui_file, expected)) continue; + try writer.print( + " {s}\n", + .{ bp.major, bp.minor, bp.name, ui_file }, + ); + break; + } else { + // The for loop never broke which means it didn't find + // a matching blueprint for this input. + return error.BlueprintNotFound; + } + } + + try writer.writeAll( + \\ + \\ + ); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/build/info.zig b/vendor/libghostty-vt/src/apprt/gtk/build/info.zig new file mode 100644 index 00000000..fc6478d8 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/build/info.zig @@ -0,0 +1,18 @@ +const builtin = @import("builtin"); + +/// Base application ID +pub const base_application_id = "com.mitchellh.ghostty"; + +/// GTK application ID +pub const application_id = switch (builtin.mode) { + .Debug, .ReleaseSafe => base_application_id ++ "-debug", + .ReleaseFast, .ReleaseSmall => base_application_id, +}; + +pub const resource_path = "/com/mitchellh/ghostty"; + +/// GTK object path +pub const object_path = switch (builtin.mode) { + .Debug, .ReleaseSafe => resource_path ++ "_debug", + .ReleaseFast, .ReleaseSmall => resource_path, +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/cgroup.zig b/vendor/libghostty-vt/src/apprt/gtk/cgroup.zig new file mode 100644 index 00000000..868aa268 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/cgroup.zig @@ -0,0 +1,115 @@ +/// Contains all the logic for putting individual surfaces into +/// transient systemd scopes. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = @import("../../quirks.zig").inlineAssert; + +const gio = @import("gio"); +const glib = @import("glib"); + +const internal_os = @import("../../os/main.zig"); + +const log = std.log.scoped(.gtk_systemd_cgroup); + +pub const Options = struct { + memory_high: ?u64 = null, + tasks_max: ?u64 = null, +}; + +pub fn fmtScope(buf: []u8, pid: u32) [:0]const u8 { + const fmt = "app-ghostty-surface-transient-{}.scope"; + + assert(buf.len >= fmt.len - 2 + std.math.log10_int(@as(usize, std.math.maxInt(@TypeOf(pid)))) + 1); + + return std.fmt.bufPrintZ(buf, fmt, .{pid}) catch unreachable; +} + +/// Create a transient systemd scope unit for the given process and +/// move the process into it. +pub fn createScope( + dbus: *gio.DBusConnection, + pid: u32, + options: Options, +) error{DbusCallFailed}!void { + // The unit name needs to be unique. We use the PID for this. + var name_buf: [256]u8 = undefined; + const name = fmtScope(&name_buf, pid); + + const builder_type = glib.VariantType.new("(ssa(sv)a(sa(sv)))"); + defer glib.free(builder_type); + + // Initialize our builder to build up our parameters + var builder: glib.VariantBuilder = undefined; + builder.init(builder_type); + + builder.add("s", name.ptr); + builder.add("s", "fail"); + + { + // Properties + const properties_type = glib.VariantType.new("a(sv)"); + defer glib.free(properties_type); + + builder.open(properties_type); + defer builder.close(); + + if (options.memory_high) |value| { + builder.add("(sv)", "MemoryHigh", glib.Variant.newUint64(value)); + } + + if (options.tasks_max) |value| { + builder.add("(sv)", "TasksMax", glib.Variant.newUint64(value)); + } + + // https://www.freedesktop.org/software/systemd/man/latest/systemd-oomd.service.html + builder.add("(sv)", "ManagedOOMMemoryPressure", glib.Variant.newString("kill")); + + // PID to move into the unit + const pids_value_type = glib.VariantType.new("u"); + defer glib.free(pids_value_type); + + const pids_value = glib.Variant.newFixedArray(pids_value_type, &pid, 1, @sizeOf(u32)); + + builder.add("(sv)", "PIDs", pids_value); + } + + { + // Aux - unused but must be present + const aux_type = glib.VariantType.new("a(sa(sv))"); + defer glib.free(aux_type); + + builder.open(aux_type); + defer builder.close(); + } + + var err: ?*glib.Error = null; + defer if (err) |e| e.free(); + + const reply_type = glib.VariantType.new("(o)"); + defer glib.free(reply_type); + + const value = builder.end(); + + const reply = dbus.callSync( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit", + value, + reply_type, + .{}, + -1, + null, + &err, + ) orelse { + if (err) |e| log.err( + "creating transient cgroup scope failed code={} err={s}", + .{ + e.f_code, + if (e.f_message) |msg| msg else "(no message)", + }, + ); + return error.DbusCallFailed; + }; + defer reply.unref(); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/class.zig b/vendor/libghostty-vt/src/apprt/gtk/class.zig new file mode 100644 index 00000000..942666cf --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class.zig @@ -0,0 +1,367 @@ +//! This files contains all the GObject classes for the GTK apprt +//! along with helpers to work with them. + +const std = @import("std"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const ext = @import("ext.zig"); +pub const Application = @import("class/application.zig").Application; +pub const Window = @import("class/window.zig").Window; +pub const Config = @import("class/config.zig").Config; +pub const Surface = @import("class/surface.zig").Surface; + +/// Common methods for all GObject classes we create. +pub fn Common( + comptime Self: type, + comptime Private: ?type, +) type { + return struct { + /// Upcast our type to a parent type or interface. This will fail at + /// compile time if the cast isn't 100% safe. For unsafe casts, + /// use `gobject.ext.cast` instead. We don't have a helper for that + /// because its uncommon and unsafe behavior should be noisier. + pub fn as(self: *Self, comptime T: type) *T { + return gobject.ext.as(T, self); + } + + /// Increase the reference count of the object. + pub fn ref(self: *Self) *Self { + return @ptrCast(@alignCast(gobject.Object.ref(self.as(gobject.Object)))); + } + + /// If the reference count is 1 and the object is floating, clear the + /// floating attribute. Otherwise, increase the reference count by 1. + pub fn refSink(self: *Self) *Self { + return @ptrCast(@alignCast(gobject.Object.refSink(self.as(gobject.Object)))); + } + + /// Decrease the reference count of the object. + pub fn unref(self: *Self) void { + gobject.Object.unref(self.as(gobject.Object)); + } + + /// Access the private data of the object. This should be forwarded + /// via a non-pub const usually. + pub const private = if (Private) |P| (struct { + fn private(self: *Self) *P { + return gobject.ext.impl_helpers.getPrivate( + self, + P, + P.offset, + ); + } + }).private else {}; + + /// Get the class for the object. + /// + /// This _seems_ ugly and unsafe but this is how GObject + /// works under the hood. From the [GObject Type System + /// Concepts](https://docs.gtk.org/gobject/concepts.html) documentation: + /// + /// Every object must define two structures: its class structure + /// and its instance structure. All class structures must contain + /// as first member a GTypeClass structure. All instance structures + /// must contain as first member a GTypeInstance structure. + /// โ€ฆ + /// These constraints allow the type system to make sure that + /// every object instance (identified by a pointer to the objectโ€™s + /// instance structure) contains in its first bytes a pointer to the + /// objectโ€™s class structure. + /// โ€ฆ + /// The C standard mandates that the first field of a C structure is + /// stored starting in the first byte of the buffer used to hold the + /// structureโ€™s fields in memory. This means that the first field of + /// an instance of an object B is Aโ€™s first field which in turn is + /// GTypeInstanceโ€˜s first field which in turn is g_class, a pointer + /// to Bโ€™s class structure. + /// + /// This means that to access the class structure for an object you cast it + /// to `*gobject.TypeInstance` and then access the `f_g_class` field. + /// + /// https://gitlab.gnome.org/GNOME/glib/-/blob/2c08654b62d52a31c4e4d13d7d85e12b989e72be/gobject/gtype.h#L555-571 + /// https://gitlab.gnome.org/GNOME/glib/-/blob/2c08654b62d52a31c4e4d13d7d85e12b989e72be/gobject/gtype.h#L2673 + /// + pub fn getClass(self: *Self) ?*Self.Class { + const type_instance: *gobject.TypeInstance = @ptrCast(self); + return @ptrCast(type_instance.f_g_class orelse return null); + } + + /// Define a virtual method. The `Self.Class` type must have a field + /// named `name` which is a function pointer in the following form: + /// + /// ?*const fn (*Self) callconv(.c) void + /// + /// The virtual method may take additional parameters and specify + /// a non-void return type. The parameters and return type must be + /// valid for the C calling convention. + pub fn defineVirtualMethod( + comptime name: [:0]const u8, + ) type { + return struct { + pub fn call( + class: anytype, + object: *ClassInstance(@TypeOf(class)), + params: anytype, + ) (fn_info.return_type orelse void) { + const func = @field( + gobject.ext.as(Self.Class, class), + name, + ).?; + @call(.auto, func, .{ + gobject.ext.as(Self, object), + } ++ params); + } + + pub fn implement( + class: anytype, + implementation: *const ImplementFunc(@TypeOf(class)), + ) void { + @field(gobject.ext.as( + Self.Class, + class, + ), name) = @ptrCast(implementation); + } + + /// The type info of the virtual method. + const fn_info = fn_info: { + // This is broken down like this so its slightly more + // readable. We expect a field named "name" on the Class + // with the rough type of `?*const fn` and we need the + // function info. + const Field = @FieldType(Self.Class, name); + const opt = @typeInfo(Field).optional; + const ptr = @typeInfo(opt.child).pointer; + break :fn_info @typeInfo(ptr.child).@"fn"; + }; + + /// The instance type for a class. + fn ClassInstance(comptime T: type) type { + return @typeInfo(T).pointer.child.Instance; + } + + /// The function type for implementations. This is the same type + /// as the virtual method but the self parameter points to the + /// target instead of the original class. + fn ImplementFunc(comptime T: type) type { + var params: [fn_info.params.len]std.builtin.Type.Fn.Param = undefined; + @memcpy(¶ms, fn_info.params); + params[0].type = *ClassInstance(T); + return @Type(.{ .@"fn" = .{ + .calling_convention = fn_info.calling_convention, + .is_generic = fn_info.is_generic, + .is_var_args = fn_info.is_var_args, + .return_type = fn_info.return_type, + .params = ¶ms, + } }); + } + }; + } + + /// A helper that creates a property that reads and writes a + /// private field with only shallow copies. This is good for primitives + /// such as bools, numbers, etc. + pub fn privateShallowFieldAccessor( + comptime name: []const u8, + ) gobject.ext.Accessor( + Self, + @FieldType(Private.?, name), + ) { + return gobject.ext.privateFieldAccessor( + Self, + Private.?, + &Private.?.offset, + name, + ); + } + + /// A helper that can be used to create a property that reads and + /// writes a private boxed gobject field type. + /// + /// Reading the property will result in allocating a pointer and + /// setting it will free the previous pointer. + /// + /// The object class (Self) must still free the private field + /// in finalize! + pub fn privateBoxedFieldAccessor( + comptime name: []const u8, + ) gobject.ext.Accessor( + Self, + @FieldType(Private.?, name), + ) { + return .{ + .getter = &struct { + fn get(self: *Self, value: *gobject.Value) void { + gobject.ext.Value.set( + value, + @field(private(self), name), + ); + } + }.get, + .setter = &struct { + fn set(self: *Self, value: *const gobject.Value) void { + const priv = private(self); + if (@field(priv, name)) |v| { + ext.boxedFree( + @typeInfo(@TypeOf(v)).pointer.child, + v, + ); + } + + const T = @TypeOf(@field(priv, name)); + @field( + priv, + name, + ) = gobject.ext.Value.dup(value, T); + } + }.set, + }; + } + + /// A helper that can be used to create a property that reads and + /// writes a private field gobject field type (reference counted). + /// + /// Reading the property will result in taking a reference to the + /// value and writing the property will unref the previous value. + /// + /// The object class (Self) must still free the private field + /// in finalize! + pub fn privateObjFieldAccessor( + comptime name: []const u8, + ) gobject.ext.Accessor( + Self, + @FieldType(Private.?, name), + ) { + return .{ + .getter = &struct { + fn get(self: *Self, value: *gobject.Value) void { + gobject.ext.Value.set( + value, + @field(private(self), name), + ); + } + }.get, + .setter = &struct { + fn set(self: *Self, value: *const gobject.Value) void { + const priv = private(self); + if (@field(priv, name)) |v| v.unref(); + + const T = @TypeOf(@field(priv, name)); + @field( + priv, + name, + ) = gobject.ext.Value.dup(value, T); + } + }.set, + }; + } + + /// A helper that can be used to create a property that reads and + /// writes a private `?[:0]const u8` field type. + /// + /// Reading the property will result in a copy of the string + /// and callers are responsible for freeing it. + /// + /// Writing the property will free the previous value and copy + /// the new value into the private field. + /// + /// The object class (Self) must still free the private field + /// in finalize! + pub fn privateStringFieldAccessor( + comptime name: []const u8, + ) gobject.ext.Accessor( + Self, + @FieldType(Private.?, name), + ) { + const S = struct { + fn getter(self: *Self) ?[:0]const u8 { + return @field(private(self), name); + } + + fn setter(self: *Self, value: ?[:0]const u8) void { + const priv = private(self); + if (@field(priv, name)) |v| { + glib.free(@ptrCast(@constCast(v))); + } + + // We don't need to copy this because it was already + // copied by the typedAccessor. + @field(priv, name) = value; + } + }; + + return gobject.ext.typedAccessor( + Self, + ?[:0]const u8, + .{ + .getter = S.getter, + .getter_transfer = .none, + .setter = S.setter, + .setter_transfer = .full, + }, + ); + } + + /// Common class functions. + pub const Class = struct { + pub fn as(class: *Self.Class, comptime T: type) *T { + return gobject.ext.as(T, class); + } + + /// Bind a template child to a private entry in the class. + pub const bindTemplateChildPrivate = if (Private) |P| (struct { + pub fn bindTemplateChildPrivate( + class: *Self.Class, + comptime name: [:0]const u8, + comptime options: gtk.ext.BindTemplateChildOptions, + ) void { + gtk.ext.impl_helpers.bindTemplateChildPrivate( + class, + name, + P, + P.offset, + options, + ); + } + }).bindTemplateChildPrivate else {}; + + /// Bind a function pointer to a template callback symbol. + pub fn bindTemplateCallback( + class: *Self.Class, + comptime name: [:0]const u8, + comptime func: anytype, + ) void { + { + const ptr_ti = @typeInfo(@TypeOf(func)); + if (ptr_ti != .pointer) { + @compileError("bound function must be a pointer type"); + } + if (ptr_ti.pointer.size != .one) { + @compileError("bound function must be a pointer to a function"); + } + + const func_ti = @typeInfo(ptr_ti.pointer.child); + if (func_ti != .@"fn") { + @compileError("bound function must be a function pointer"); + } + if (func_ti.@"fn".return_type == bool) { + // glib booleans are ints and returning a Zig bool type + // I think uses a byte and causes ABI issues. + @compileError("bound function must return c_int instead of bool"); + } + } + + gtk.Widget.Class.bindTemplateCallbackFull( + class.as(gtk.Widget.Class), + name, + @ptrCast(func), + ); + } + }; + }; +} + +test { + @import("std").testing.refAllDecls(@This()); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/application.zig b/vendor/libghostty-vt/src/apprt/gtk/class/application.zig new file mode 100644 index 00000000..873674ce --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/application.zig @@ -0,0 +1,2945 @@ +const std = @import("std"); +const assert = @import("../../../quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const adw = @import("adw"); +const gdk = @import("gdk"); +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const build_config = @import("../../../build_config.zig"); +const build_info = @import("../build/info.zig"); +const state = &@import("../../../global.zig").state; +const i18n = @import("../../../os/main.zig").i18n; +const apprt = @import("../../../apprt.zig"); +const CoreApp = @import("../../../App.zig"); +const configpkg = @import("../../../config.zig"); +const input = @import("../../../input.zig"); +const internal_os = @import("../../../os/main.zig"); +const systemd = @import("../../../os/systemd.zig"); +const terminal = @import("../../../terminal/main.zig"); +const xev = @import("../../../global.zig").xev; +const Binding = @import("../../../input.zig").Binding; +const CoreConfig = configpkg.Config; +const CoreSurface = @import("../../../Surface.zig"); +const lib = @import("../../../lib/main.zig"); + +const ext = @import("../ext.zig"); +const key = @import("../key.zig"); +const adw_version = @import("../adw_version.zig"); +const gtk_version = @import("../gtk_version.zig"); +const winprotopkg = @import("../winproto.zig"); +const ApprtApp = @import("../App.zig"); +const Common = @import("../class.zig").Common; +const WeakRef = @import("../weak_ref.zig").WeakRef; +const Config = @import("config.zig").Config; +const Surface = @import("surface.zig").Surface; +const SplitTree = @import("split_tree.zig").SplitTree; +const Window = @import("window.zig").Window; +const Tab = @import("tab.zig").Tab; +const CloseConfirmationDialog = @import("close_confirmation_dialog.zig").CloseConfirmationDialog; +const ConfigErrorsDialog = @import("config_errors_dialog.zig").ConfigErrorsDialog; +const GlobalShortcuts = @import("global_shortcuts.zig").GlobalShortcuts; +const OpenURI = @import("../portal.zig").OpenURI; + +const log = std.log.scoped(.gtk_ghostty_application); + +/// Function used to funnel GLib/GObject/GTK log messages into Zig's logging +/// system rather than just getting dumped directly to stderr. +fn glibLogWriterFunction( + level: glib.LogLevelFlags, + fields: [*]const glib.LogField, + n_fields: usize, + _: ?*anyopaque, +) callconv(.c) glib.LogWriterOutput { + const glib_log = std.log.scoped(.glib); + + var message_: ?[]const u8 = null; + var domain_: ?[]const u8 = null; + for (0..n_fields) |i| { + const field = fields[i]; + const k = std.mem.span(field.f_key orelse continue); + const v: []const u8 = v: { + if (field.f_length >= 0) { + const v: [*]const u8 = @ptrCast(field.f_value orelse continue); + break :v v[0..@intCast(field.f_length)]; + } + const v: [*:0]const u8 = @ptrCast(field.f_value orelse continue); + break :v std.mem.span(v); + }; + if (std.mem.eql(u8, k, "MESSAGE")) { + message_ = v; + continue; + } + if (std.mem.eql(u8, k, "GLIB_DOMAIN")) { + domain_ = v; + continue; + } + } + + const message = message_ orelse return .unhandled; + const domain = domain_ orelse "ยซunknownยป"; + + if (level.level_error) { + glib_log.err("ERROR: {s}: {s}", .{ domain, message }); + return .handled; + } + if (level.level_critical) { + glib_log.err("CRITICAL: {s}: {s}", .{ domain, message }); + return .handled; + } + if (level.level_warning) { + glib_log.warn("WARNING: {s}: {s}", .{ domain, message }); + return .handled; + } + if (level.level_message) { + glib_log.info("MESSAGE: {s}: {s}", .{ domain, message }); + return .handled; + } + if (level.level_info) { + glib_log.info("INFO: {s}: {s}", .{ domain, message }); + return .handled; + } + if (level.level_debug) { + glib_log.debug("DEBUG: {s}: {s}", .{ domain, message }); + return .handled; + } + glib_log.debug("UNKNOWN: {s}: {s}", .{ domain, message }); + return .handled; +} + +/// The primary entrypoint for the Ghostty GTK application. +/// +/// This requires a `ghostty.App` and `ghostty.Config` and takes +/// care of the rest. Call `run` to run the application to completion. +pub const Application = extern struct { + /// This type creates a new GObject class. Since the Application is + /// the primary entrypoint I'm going to use this as a place to document + /// how this all works and where you can find resources for it, but + /// this applies to any other GObject class within this apprt. + /// + /// The various fields (parent_instance) and constants (Parent, + /// getGObjectType, etc.) are mandatory "interfaces" for zig-gobject + /// to create a GObject class. + /// + /// I found these to be the best resources: + /// + /// * https://github.com/ianprime0509/zig-gobject/blob/d7f1edaf50193d49b56c60568dfaa9f23195565b/extensions/gobject2.zig + /// * https://github.com/ianprime0509/zig-gobject/blob/d7f1edaf50193d49b56c60568dfaa9f23195565b/example/src/custom_class.zig + /// + const Self = @This(); + + parent_instance: Parent, + pub const Parent = adw.Application; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyApplication", + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + "config", + Self, + ?*Config, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*Config, + .{ + .getter = Self.getConfig, + .getter_transfer = .full, + }, + ), + }, + ); + }; + }; + + const Private = struct { + /// The apprt App. This is annoying that we need this it'd be + /// nicer to just make THIS the apprt app but the current libghostty + /// API doesn't allow that. + rt_app: *ApprtApp, + + /// The libghostty App instance. + core_app: *CoreApp, + + /// The configuration for the application. + config: *Config, + + /// State and logic for the underlying windowing protocol. + winproto: winprotopkg.App, + + /// The global shortcut logic. + global_shortcuts: *GlobalShortcuts, + + /// This is set to true so long as we request a window exactly + /// once. This prevents quitting the app before we've shown one + /// window. + requested_window: bool = false, + + /// This is set to false internally when the event loop + /// should exit and the application should quit. This must + /// only be set by the main loop thread. + running: bool = false, + + /// The timer used to quit the application after the last window is + /// closed. Even if there is no quit delay set, this is the state + /// used to determine to close the app. + quit_timer: union(enum) { + off, + active: c_uint, + expired, + } = .off, + + /// If non-null, we're currently showing a config errors dialog. + /// This is a WeakRef because the dialog can close on its own + /// outside of our own lifecycle and that's okay. + config_errors_dialog: WeakRef(ConfigErrorsDialog) = .empty, + + /// glib source for our signal handler. + signal_source: ?c_uint = null, + + /// CSS Provider for any styles based on Ghostty configuration values. + css_provider: *gtk.CssProvider, + + /// Providers for loading custom stylesheets defined by user + custom_css_providers: std.ArrayListUnmanaged(*gtk.CssProvider) = .empty, + + /// A copy of the LANG environment variable that was provided to Ghostty + /// by the system. If this is null, the LANG environment variable did + /// not exist in Ghostty's environment variable. + saved_language: ?[:0]const u8 = null, + + open_uri: OpenURI = undefined, + + pub var offset: c_int = 0; + }; + + /// Get this application as the default, allowing access to its + /// properties globally. + /// + /// This asserts that there is a default application and that the + /// default application is a GhosttyApplication. The program would have + /// to be in a very bad state for this to be violated. + pub fn default() *Self { + const app = gio.Application.getDefault().?; + return gobject.ext.cast(Self, app).?; + } + + /// Creates a new Application instance. + /// + /// This does a lot more work than a typical class instantiation, + /// because we expect that this is the main program entrypoint. + /// + /// The only failure mode of initializing the application is early OOM. + /// Early OOM can't be recovered from. Every other error is mapped to + /// some degraded state where we can at least show a window with an error. + pub fn new( + rt_app: *ApprtApp, + core_app: *CoreApp, + ) Allocator.Error!*Self { + const alloc = core_app.alloc; + + // Capture GLib/GObject/GTK log messages and funnel them through Zig's + // logging system rather than just getting dumped directly to stderr. + _ = glib.logSetWriterFunc(glibLogWriterFunction, null, null); + + // Log our GTK versions + gtk_version.logVersion(); + adw_version.logVersion(); + + // Load our configuration. + var config = CoreConfig.load(alloc) catch |err| err: { + // If we fail to load the configuration, then we should log + // the error in the diagnostics so it can be shown to the user. + // We can still load a default which only fails for OOM, allowing + // us to startup. + var def: CoreConfig = try .default(alloc); + errdefer def.deinit(); + try def.addDiagnosticFmt( + "error loading user configuration: {}", + .{err}, + ); + + break :err def; + }; + defer config.deinit(); + + const saved_language: ?[:0]const u8 = saved_language: { + const old_language = old_language: { + const result = (internal_os.getenv(alloc, "LANG") catch break :old_language null) orelse break :old_language null; + defer result.deinit(alloc); + break :old_language alloc.dupeZ(u8, result.value) catch break :old_language null; + }; + + if (config.language) |language| _ = internal_os.setenv("LANG", language); + + break :saved_language old_language; + }; + + // Set gettext global domain to be our app so that our unqualified + // translations map to our translations. + internal_os.i18n.initGlobalDomain() catch |err| { + // Failures shuldn't stop application startup. Our app may + // not translate correctly but it should still work. In the + // future we may want to add this to the GUI to show. + log.warn("i18n initialization failed error={}", .{err}); + }; + + // Setup our GTK init env vars + setGtkEnv(&config) catch |err| switch (err) { + error.NoSpaceLeft => { + // If we fail to set GTK environment variables then we still + // try to start the application... + log.warn( + "error setting GTK environment variables err={}", + .{err}, + ); + }, + }; + adw.init(); + + const single_instance = switch (config.@"gtk-single-instance") { + .true => true, + .false => false, + // This should have been resolved to true/false during config loading. + .detect => unreachable, + }; + + // Setup the flags for our application. + const app_flags: gio.ApplicationFlags = app_flags: { + var flags: gio.ApplicationFlags = .flags_default_flags; + if (!single_instance) flags.non_unique = true; + break :app_flags flags; + }; + + // Our app ID determines uniqueness and maps to our desktop file. + // We append "-debug" to the ID if we're in debug mode so that we + // can develop Ghostty in Ghostty. + const app_id: [:0]const u8 = app_id: { + if (config.class) |class| { + if (gio.Application.idIsValid(class) != 0) { + break :app_id class; + } else { + log.warn("invalid 'class' in config, ignoring", .{}); + } + } + + break :app_id build_info.application_id; + }; + + const display: *gdk.Display = gdk.Display.getDefault() orelse { + // I'm unsure of any scenario where this happens. Because we don't + // want to litter null checks everywhere, we just exit here. + log.warn("gdk display is null, exiting", .{}); + std.posix.exit(1); + }; + + // Setup our windowing protocol logic + var wp: winprotopkg.App = winprotopkg.App.init( + alloc, + display, + app_id, + &config, + ) catch |err| wp: { + // If we fail to detect or setup the windowing protocol + // specifies, we fallback to a noop implementation so we can + // still launch. + log.warn("error initializing windowing protocol err={}", .{err}); + break :wp .{ .none = .{} }; + }; + errdefer wp.deinit(); + log.debug("windowing protocol={s}", .{@tagName(wp)}); + + // Create our GTK Application which encapsulates our process. + log.debug("creating GTK application id={s} single-instance={}", .{ + app_id, + single_instance, + }); + + // Wrap our configuration in a GObject. + const config_obj: *Config = try .new(alloc, &config); + errdefer config_obj.unref(); + + // Internally, GTK ensures that only one instance of this provider + // exists in the provider list for the display. + const css_provider = gtk.CssProvider.new(); + gtk.StyleContext.addProviderForDisplay( + display, + css_provider.as(gtk.StyleProvider), + gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + 3, + ); + errdefer css_provider.unref(); + + // Initialize the app. + const self = gobject.ext.newInstance(Self, .{ + .application_id = app_id.ptr, + .flags = app_flags, + + // Force the resource path to a known value so it doesn't depend + // on the app id (which changes between debug/release and can be + // user-configured) and force it to load in compiled resources. + .resource_base_path = build_info.resource_path, + }); + + // Setup our private state. More setup is done in the init + // callback that GObject calls, but we can't pass this data through + // to there (and we don't need it there directly) so this is here. + const priv: *Private = self.private(); + priv.* = .{ + .rt_app = rt_app, + .core_app = core_app, + .config = config_obj, + .winproto = wp, + .css_provider = css_provider, + .custom_css_providers = .empty, + .global_shortcuts = gobject.ext.newInstance(GlobalShortcuts, .{}), + .saved_language = saved_language, + .open_uri = .init(rt_app), + }; + + // Signals + _ = gobject.Object.signals.notify.connect( + self, + *Self, + propConfig, + self, + .{ .detail = "config" }, + ); + + _ = gtk.CssProvider.signals.parsing_error.connect( + css_provider, + *Self, + signalCssParsingError, + self, + .{}, + ); + + // Trigger initial config changes + self.as(gobject.Object).notifyByPspec(properties.config.impl.param_spec); + + return self; + } + + /// Force deinitialize the application. + /// + /// Normally in a GObject lifecycle, this would be called by the + /// finalizer. But applications are never fully unreferenced so this + /// ensures that our memory is cleaned up properly. + pub fn deinit(self: *Self) void { + const alloc = self.allocator(); + const priv: *Private = self.private(); + priv.config.unref(); + priv.winproto.deinit(); + priv.open_uri.deinit(); + priv.global_shortcuts.unref(); + if (priv.saved_language) |language| alloc.free(language); + if (gdk.Display.getDefault()) |display| { + gtk.StyleContext.removeProviderForDisplay( + display, + priv.css_provider.as(gtk.StyleProvider), + ); + + for (priv.custom_css_providers.items) |provider| { + gtk.StyleContext.removeProviderForDisplay( + display, + provider.as(gtk.StyleProvider), + ); + } + } + priv.css_provider.unref(); + for (priv.custom_css_providers.items) |provider| provider.unref(); + priv.custom_css_providers.deinit(alloc); + } + + /// The global allocator that all other classes should use by + /// calling `Application.default().allocator()`. Zig code should prefer + /// this wherever possible so we get leak detection in debug/tests. + pub fn allocator(self: *Self) std.mem.Allocator { + return self.private().core_app.alloc; + } + + /// Get the original language that Ghostty was launched with. This returns a + /// pointer to internal memory so it must be copied by callers. + pub fn savedLanguage(self: *Self) ?[:0]const u8 { + return self.private().saved_language; + } + + /// Run the application. This is a replacement for `gio.Application.run` + /// because we want more tight control over our event loop so we can + /// integrate it with libghostty. + pub fn run(self: *Self) !void { + // Based on the actual `gio.Application.run` implementation: + // https://github.com/GNOME/glib/blob/a8e8b742e7926e33eb635a8edceac74cf239d6ed/gio/gapplication.c#L2533 + + // Acquire the default context for the application + const ctx = glib.MainContext.default(); + if (glib.MainContext.acquire(ctx) == 0) return error.ContextAcquireFailed; + + // The final cleanup that is always required at the end of running. + defer { + // Ensure our timer source is removed + self.stopQuitTimer(); + + // Sync any remaining settings + gio.Settings.sync(); + + // Clear out the event loop, don't block. + while (glib.MainContext.iteration(ctx, 0) != 0) {} + + // Release the context so something else can use it. + defer glib.MainContext.release(ctx); + } + + // Register the application + var err_: ?*glib.Error = null; + if (self.as(gio.Application).register( + null, + &err_, + ) == 0) { + if (err_) |err| { + defer err.free(); + log.warn( + "error registering application: {s}", + .{err.f_message orelse "(unknown)"}, + ); + } + + return error.ApplicationRegisterFailed; + } + assert(err_ == null); + + // This just calls the `activate` signal but its part of the normal startup + // routine so we just call it, but only if the config allows it (this allows + // for launching Ghostty in the "background" without immediately opening + // a window). + // + // https://gitlab.gnome.org/GNOME/glib/-/blob/bd2ccc2f69ecfd78ca3f34ab59e42e2b462bad65/gio/gapplication.c#L2302 + const priv = self.private(); + { + // We need to scope any config access because once we run our + // event loop, this can change out from underneath us. + const config = priv.config.get(); + if (config.@"initial-window") self.as(gio.Application).activate(); + } + + // If we are NOT the primary instance, then we never want to run. + // This means that another instance of the GTK app is running. + if (self.as(gio.Application).getIsRemote() != 0) { + log.debug( + "application is remote, exiting run loop after activation", + .{}, + ); + return; + } + + // Tell systemd that we are ready. + systemd.notify.ready(); + + log.debug("entering runloop", .{}); + defer log.debug("exiting runloop", .{}); + priv.running = true; + while (priv.running) { + _ = glib.MainContext.iteration(ctx, 1); + + // Tick the core Ghostty terminal app + try priv.core_app.tick(priv.rt_app); + + // Check if we must quit based on the current state. + const must_quit = q: { + // If we are configured to always stay running, don't quit. + const config = priv.config.get(); + if (!config.@"quit-after-last-window-closed") break :q false; + + // If the quit timer has expired, quit. + if (priv.quit_timer == .expired) { + log.debug("must_quit due to quit timer expired", .{}); + break :q true; + } + + // If we have no windows attached to our app, also quit. + // We only do this if we don't have the closed delay set, + // because with the closed delay set we'll exit eventually. + if (config.@"quit-after-last-window-closed-delay" == null) { + if (priv.requested_window and @as( + ?*glib.List, + self.as(gtk.Application).getWindows(), + ) == null) { + log.debug("must_quit due to no app windows", .{}); + break :q true; + } + } + + // No quit conditions met + break :q false; + }; + + if (must_quit) { + // All must quit scenarios do not need confirmation. + // Furthermore, must quit scenarios may result in a situation + // where its unsafe to even access the app/surface memory + // since its in the process of being freed. We must simply + // begin our exit immediately. + self.quitNow(); + } + } + } + + /// Quit the application. This will start the process to stop the + /// run loop. It will not `posix.exit`. + pub fn quit(self: *Self) void { + const priv = self.private(); + + // If our run loop has already exited then we are done. + if (!priv.running) return; + + // If our core app doesn't need to confirm quit then we + // can exit immediately. + if (!priv.core_app.needsConfirmQuit()) { + self.quitNow(); + return; + } + + // Get the parent for our dialog + const parent: ?*gtk.Widget = parent: { + const list = gtk.Window.listToplevels(); + defer list.free(); + const focused = @as(?*glib.List, list.findCustom( + null, + findActiveWindow, + )) orelse { + // If we have an active surface then we should have + // a window available but in the rare case we don't we + // should exit so we don't crash. + break :parent null; + }; + break :parent @ptrCast(@alignCast(focused.f_data)); + }; + + // Show a confirmation dialog + const dialog: *CloseConfirmationDialog = .new(.app); + _ = CloseConfirmationDialog.signals.@"close-request".connect( + dialog, + *Application, + handleCloseConfirmation, + self, + .{}, + ); + + // Show it + dialog.present(parent); + } + + fn quitNow(self: *Self) void { + // Get all our windows and destroy them, forcing them to free. + const list = gtk.Window.listToplevels(); + defer list.free(); + list.foreach(struct { + fn callback(data: ?*anyopaque, _: ?*anyopaque) callconv(.c) void { + const ptr = data orelse return; + const window: *gtk.Window = @ptrCast(@alignCast(ptr)); + + // We only want to destroy our windows. These windows own + // every other type of window that is possible so this will + // trigger a proper shutdown sequence. + // + // We previously just destroyed ALL windows but this leads to + // a double-free with the fcitx ime, because it has a nested + // gtk.Window as a property that we don't own and it later + // tries to free on its own. I think this is probably a bug in + // the fcitx ime widget but still, we don't want a double free! + if (gobject.ext.isA(window, Window)) { + window.destroy(); + } + } + }.callback, null); + + // Trigger our runloop exit. + self.private().running = false; + } + + /// apprt API to perform an action. + pub fn performAction( + self: *Self, + target: apprt.Target, + comptime action: apprt.Action.Key, + value: apprt.Action.Value(action), + ) !bool { + switch (action) { + .close_tab => return Action.closeTab(target, value), + .close_window => return Action.closeWindow(target), + + .copy_title_to_clipboard => return Action.copyTitleToClipboard(target), + + .config_change => try Action.configChange( + self, + target, + value.config, + ), + + .desktop_notification => Action.desktopNotification(self, target, value), + + .equalize_splits => return Action.equalizeSplits(target), + + .goto_split => return Action.gotoSplit(target, value), + + .goto_window => return Action.gotoWindow(value), + + .goto_tab => return Action.gotoTab(target, value), + + .initial_size => return Action.initialSize(target, value), + + .inspector => return Action.controlInspector(target, value), + + .key_sequence => return Action.keySequence(target, value), + .key_table => return Action.keyTable(target, value), + + .mouse_over_link => Action.mouseOverLink(target, value), + .mouse_shape => Action.mouseShape(target, value), + .mouse_visibility => Action.mouseVisibility(target, value), + + .move_tab => return Action.moveTab(target, value), + + .new_split => return Action.newSplit(target, value), + + .new_tab => return Action.newTab(target), + + .new_window => try Action.newWindow( + self, + switch (target) { + .app => null, + .surface => |v| v, + }, + .none, + ), + + .open_config => return Action.openConfig(self), + + .open_url => Action.openUrl(self, value), + + .pwd => Action.pwd(target, value), + + .present_terminal => return Action.presentTerminal(target), + + .progress_report => return Action.progressReport(target, value), + + .prompt_title => return Action.promptTitle(target, value), + + .quit => self.quit(), + + .quit_timer => try Action.quitTimer(self, value), + + .reload_config => try Action.reloadConfig(self, target, value), + + .render => Action.render(target), + + .resize_split => return Action.resizeSplit(target, value), + + .ring_bell => Action.ringBell(target), + + .scrollbar => Action.scrollbar(target, value), + + .set_title => Action.setTitle(target, value), + .set_tab_title => return Action.setTabTitle(target, value), + + .show_child_exited => return Action.showChildExited(target, value), + + .show_gtk_inspector => Action.showGtkInspector(), + + .size_limit => return Action.sizeLimit(target, value), + + .toggle_maximize => Action.toggleMaximize(target), + .toggle_fullscreen => Action.toggleFullscreen(target), + .toggle_quick_terminal => return Action.toggleQuickTerminal(self), + .toggle_tab_overview => return Action.toggleTabOverview(target), + .toggle_window_decorations => return Action.toggleWindowDecorations(target), + .toggle_command_palette => return Action.toggleCommandPalette(target), + .toggle_split_zoom => return Action.toggleSplitZoom(target), + .show_on_screen_keyboard => return Action.showOnScreenKeyboard(target), + .command_finished => return Action.commandFinished(target, value), + .readonly => return Action.setReadonly(target, value), + + .start_search => Action.startSearch(target, value), + .end_search => Action.endSearch(target), + .search_total => Action.searchTotal(target, value), + .search_selected => Action.searchSelected(target, value), + + // Unimplemented + .secure_input, + .close_all_windows, + .float_window, + .toggle_visibility, + .toggle_background_opacity, + .cell_size, + .render_inspector, + .renderer_health, + .color_change, + .reset_window_size, + .check_for_updates, + .undo, + .redo, + => { + log.warn("unimplemented action={}", .{action}); + return false; + }, + } + + // Assume it was handled. The unhandled case must be explicit + // in the switch above. + return true; + } + + /// Returns the core app associated with this application. This is + /// not a reference-counted type so you should not store this. + pub fn core(self: *Self) *CoreApp { + return self.private().core_app; + } + + /// Returns the apprt application associated with this application. + pub fn rt(self: *Self) *ApprtApp { + return self.private().rt_app; + } + + /// Returns the app winproto implementation. + pub fn winproto(self: *Self) *winprotopkg.App { + return &self.private().winproto; + } + + /// Returns the open URI portal implementation. + pub fn openUri(self: *Self) *OpenURI { + return &self.private().open_uri; + } + + /// This will get called when there are no more open surfaces. + fn startQuitTimer(self: *Self) void { + const priv = self.private(); + const config = priv.config.get(); + + // Cancel any previous timer. + self.stopQuitTimer(); + + // This is a no-op unless we are configured to quit after last window is closed. + if (!config.@"quit-after-last-window-closed") return; + + // If a delay is configured, set a timeout function to quit after the delay. + if (config.@"quit-after-last-window-closed-delay") |v| { + priv.quit_timer = .{ + .active = glib.timeoutAdd( + v.asMilliseconds(), + handleQuitTimerExpired, + self, + ), + }; + } else { + // If no delay is configured, treat it as expired. + priv.quit_timer = .expired; + } + } + + /// This will get called when a new surface gets opened. + fn stopQuitTimer(self: *Self) void { + const priv = self.private(); + switch (priv.quit_timer) { + .off => {}, + .expired => priv.quit_timer = .off, + .active => |source| { + if (glib.Source.remove(source) == 0) { + log.warn( + "unable to remove quit timer source={d}", + .{source}, + ); + } + + priv.quit_timer = .off; + }, + } + } + + fn loadRuntimeCss(self: *Self) (Allocator.Error || std.Io.Writer.Error)!void { + const alloc = self.allocator(); + const priv: *Private = self.private(); + const config = priv.config.get(); + + var buf: std.Io.Writer.Allocating = try .initCapacity(alloc, 2048); + defer buf.deinit(); + + const writer = &buf.writer; + + // Load standard css first as it can override some of the user configured styling. + try loadRuntimeCss414(config, writer); + try loadRuntimeCss416(config, writer); + + const unfocused_fill: CoreConfig.Color = config.@"unfocused-split-fill" orelse config.background; + + try writer.print( + \\widget.unfocused-split {{ + \\ opacity: {d:.2}; + \\ background-color: rgb({d},{d},{d}); + \\}} + \\ + , .{ + 1.0 - config.@"unfocused-split-opacity", + unfocused_fill.r, + unfocused_fill.g, + unfocused_fill.b, + }); + + if (config.@"split-divider-color") |color| { + try writer.print( + \\.window .split paned > separator {{ + \\ color: rgb({[r]d},{[g]d},{[b]d}); + \\ background: rgb({[r]d},{[g]d},{[b]d}); + \\}} + \\ + , .{ + .r = color.r, + .g = color.g, + .b = color.b, + }); + } + + if (config.@"window-title-font-family") |font_family| { + try writer.print( + \\.window headerbar {{ + \\ font-family: "{[font_family]s}"; + \\}} + \\ + , .{ .font_family = font_family }); + } + + const contents = buf.written(); + + log.debug("runtime CSS is {d} bytes", .{contents.len}); + + const bytes = glib.Bytes.new(contents.ptr, contents.len); + defer bytes.unref(); + + // Clears any previously loaded CSS from this provider + priv.css_provider.loadFromBytes(bytes); + } + + /// Load runtime CSS for older than GTK 4.16 + fn loadRuntimeCss414( + config: *const CoreConfig, + writer: *std.Io.Writer, + ) std.Io.Writer.Error!void { + if (gtk_version.runtimeAtLeast(4, 16, 0)) return; + + const window_theme = config.@"window-theme"; + const headerbar_background = config.@"window-titlebar-background" orelse config.background; + const headerbar_foreground = config.@"window-titlebar-foreground" orelse config.foreground; + + switch (window_theme) { + .ghostty => try writer.print( + \\windowhandle {{ + \\ background-color: rgb({d},{d},{d}); + \\ color: rgb({d},{d},{d}); + \\}} + \\windowhandle:backdrop {{ + \\ background-color: oklab(from rgb({d},{d},{d}) calc(l * 0.9) a b / alpha); + \\}} + \\ + , .{ + headerbar_background.r, + headerbar_background.g, + headerbar_background.b, + headerbar_foreground.r, + headerbar_foreground.g, + headerbar_foreground.b, + headerbar_background.r, + headerbar_background.g, + headerbar_background.b, + }), + else => {}, + } + } + + /// Load runtime for GTK 4.16 and newer + fn loadRuntimeCss416( + config: *const CoreConfig, + writer: *std.Io.Writer, + ) std.Io.Writer.Error!void { + if (gtk_version.runtimeUntil(4, 16, 0)) return; + + const window_theme = config.@"window-theme"; + const headerbar_background = config.@"window-titlebar-background" orelse config.background; + const headerbar_foreground = config.@"window-titlebar-foreground" orelse config.foreground; + + try writer.writeAll( + \\/* + \\ * Child Exited Overlay + \\ */ + \\ + \\.child-exited.normal revealer widget { + \\ background-color: color-mix( + \\ in srgb, + \\ var(--success-bg-color), + \\ transparent 50% + \\ ); + \\} + \\ + \\.child-exited.abnormal revealer widget { + \\ background-color: color-mix( + \\ in srgb, + \\ var(--error-bg-color), + \\ transparent 50% + \\ ); + \\} + \\ + \\/* + \\ * Surface + \\ */ + \\ + \\.surface progressbar.error trough progress { + \\ background-color: color-mix( + \\ in srgb, + \\ var(--error-bg-color), + \\ transparent 50% + \\ ); + \\} + \\ + \\.surface .bell-overlay { + \\ border-color: color-mix( + \\ in srgb, + \\ var(--accent-color), + \\ transparent 50% + \\ ); + \\} + \\ + \\/* + \\ * Splits + \\ */ + \\ + \\.window .split paned > separator { + \\ background-color: color-mix( + \\ in srgb, + \\ var(--window-bg-color), + \\ transparent 0% + \\ ); + \\} + \\ + ); + + switch (window_theme) { + .ghostty => try writer.print( + \\:root {{ + \\ --ghostty-fg: rgb({d},{d},{d}); + \\ --ghostty-bg: rgb({d},{d},{d}); + \\ --headerbar-fg-color: var(--ghostty-fg); + \\ --headerbar-bg-color: var(--ghostty-bg); + \\ --headerbar-backdrop-color: oklab(from var(--headerbar-bg-color) calc(l * 0.9) a b / alpha); + \\ --overview-fg-color: var(--ghostty-fg); + \\ --overview-bg-color: var(--ghostty-bg); + \\ --popover-fg-color: var(--ghostty-fg); + \\ --popover-bg-color: var(--ghostty-bg); + \\ --window-fg-color: var(--ghostty-fg); + \\ --window-bg-color: var(--ghostty-bg); + \\}} + \\windowhandle {{ + \\ background-color: var(--headerbar-bg-color); + \\ color: var(--headerbar-fg-color); + \\}} + \\windowhandle:backdrop {{ + \\ background-color: var(--headerbar-backdrop-color); + \\}} + , .{ + headerbar_foreground.r, + headerbar_foreground.g, + headerbar_foreground.b, + headerbar_background.r, + headerbar_background.g, + headerbar_background.b, + }), + else => {}, + } + } + + fn loadCustomCss(self: *Self) (std.fs.File.ReadError || Allocator.Error)!void { + const priv: *Private = self.private(); + const alloc = self.allocator(); + const display = gdk.Display.getDefault() orelse { + log.warn("unable to get display", .{}); + return; + }; + + // unload the previously loaded style providers + for (priv.custom_css_providers.items) |provider| { + gtk.StyleContext.removeProviderForDisplay( + display, + provider.as(gtk.StyleProvider), + ); + provider.unref(); + } + priv.custom_css_providers.clearRetainingCapacity(); + + const config = priv.config.get(); + for (config.@"gtk-custom-css".value.items) |p| { + const path, const optional = switch (p) { + .optional => |path| .{ path, true }, + .required => |path| .{ path, false }, + }; + const file = std.fs.openFileAbsolute(path, .{}) catch |err| { + if (err != error.FileNotFound or !optional) { + log.warn( + "error opening gtk-custom-css file {s}: {}", + .{ path, err }, + ); + } + continue; + }; + defer file.close(); + + const css_file_size_limit = 5 * 1024 * 1024; // 5MB + + log.info("loading gtk-custom-css path={s}", .{path}); + const contents = file.readToEndAlloc( + alloc, + css_file_size_limit, + ) catch |err| switch (err) { + error.FileTooBig => { + log.warn("gtk-custom-css file {s} was larger than {Bi}", .{ path, css_file_size_limit }); + continue; + }, + else => |e| return e, + }; + defer alloc.free(contents); + + const bytes = glib.Bytes.new(contents.ptr, contents.len); + defer bytes.unref(); + + const css_provider = gtk.CssProvider.new(); + errdefer css_provider.unref(); + + _ = gtk.CssProvider.signals.parsing_error.connect( + css_provider, + *Self, + signalCssParsingError, + self, + .{}, + ); + + try priv.custom_css_providers.append(alloc, css_provider); + + css_provider.loadFromBytes(bytes); + + gtk.StyleContext.addProviderForDisplay( + display, + css_provider.as(gtk.StyleProvider), + gtk.STYLE_PROVIDER_PRIORITY_USER, + ); + } + } + + fn syncActionAccelerators(self: *Self) void { + self.syncActionAccelerator("app.quit", .{ .quit = {} }); + self.syncActionAccelerator("app.open-config", .{ .open_config = {} }); + self.syncActionAccelerator("app.reload-config", .{ .reload_config = {} }); + self.syncActionAccelerator("win.toggle-inspector", .{ .inspector = .toggle }); + self.syncActionAccelerator("app.show-gtk-inspector", .show_gtk_inspector); + self.syncActionAccelerator("win.toggle-command-palette", .toggle_command_palette); + self.syncActionAccelerator("win.close", .{ .close_window = {} }); + self.syncActionAccelerator("win.new-window", .{ .new_window = {} }); + self.syncActionAccelerator("win.new-tab", .{ .new_tab = {} }); + self.syncActionAccelerator("win.close-tab::this", .{ .close_tab = .this }); + self.syncActionAccelerator("tab.close::this", .{ .close_tab = .this }); + self.syncActionAccelerator("win.split-right", .{ .new_split = .right }); + self.syncActionAccelerator("win.split-down", .{ .new_split = .down }); + self.syncActionAccelerator("win.split-left", .{ .new_split = .left }); + self.syncActionAccelerator("win.split-up", .{ .new_split = .up }); + self.syncActionAccelerator("win.copy", .{ .copy_to_clipboard = .mixed }); + self.syncActionAccelerator("win.paste", .{ .paste_from_clipboard = {} }); + self.syncActionAccelerator("win.reset", .{ .reset = {} }); + self.syncActionAccelerator("win.clear", .{ .clear_screen = {} }); + self.syncActionAccelerator("win.prompt-title", .{ .prompt_surface_title = {} }); + self.syncActionAccelerator("split-tree.new-split::left", .{ .new_split = .left }); + self.syncActionAccelerator("split-tree.new-split::right", .{ .new_split = .right }); + self.syncActionAccelerator("split-tree.new-split::up", .{ .new_split = .up }); + self.syncActionAccelerator("split-tree.new-split::down", .{ .new_split = .down }); + } + + fn syncActionAccelerator( + self: *Self, + gtk_action: [:0]const u8, + action: input.Binding.Action, + ) void { + const gtk_app = self.as(gtk.Application); + + // Reset it initially + const zero = [_:null]?[*:0]const u8{}; + gtk_app.setAccelsForAction(gtk_action, &zero); + + const config = self.private().config.get(); + const trigger = config.keybind.set.getTrigger(action) orelse return; + var buf: [1024]u8 = undefined; + const accel = if (key.accelFromTrigger( + &buf, + trigger, + )) |accel_| + accel_ orelse return + else |err| switch (err) { + // This should really never, never happen. Its not critical enough + // to actually crash, but this is a bug somewhere. An accelerator + // for a trigger can't possibly be more than 1024 bytes. + error.WriteFailed => { + log.warn("accelerator somehow longer than 1024 bytes: {f}", .{trigger}); + return; + }, + }; + const accels = [_:null]?[*:0]const u8{accel}; + + gtk_app.setAccelsForAction(gtk_action, &accels); + } + + //--------------------------------------------------------------- + // Properties + + /// Returns the configuration for this application. + /// + /// The reference count is increased. + pub fn getConfig(self: *Self) *Config { + return self.private().config.ref(); + } + + /// Set the configuration for this application. The reference count + /// is increased on the new configuration and the old one is + /// unreferenced. + /// + /// If the config has errors this may show the config errors dialog. + fn setConfig(self: *Self, config: *Config) void { + const priv = self.private(); + priv.config.unref(); + priv.config = config.ref(); + self.as(gobject.Object).notifyByPspec(properties.config.impl.param_spec); + + // Show our errors if we have any + self.showConfigErrorsDialog(); + } + + fn propConfig( + _: *Application, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // Sync our accelerators for menu items. + self.syncActionAccelerators(); + + // Load our runtime and custom CSS. If this fails then our window is + // just stuck with the old CSS but we don't want to fail the entire + // config change operation. + self.loadRuntimeCss() catch |err| switch (err) { + error.WriteFailed, error.OutOfMemory => log.warn( + "out of memory loading runtime CSS, no runtime CSS applied", + .{}, + ), + }; + self.loadCustomCss() catch |err| { + log.warn( + "failed to load custom CSS, no custom CSS applied, err={}", + .{err}, + ); + }; + } + + /// Log CSS parsing error + fn signalCssParsingError( + _: *gtk.CssProvider, + css_section: *gtk.CssSection, + err: *glib.Error, + _: *Self, + ) callconv(.c) void { + const location = css_section.toString(); + defer glib.free(location); + if (comptime gtk_version.atLeast(4, 16, 0)) bytes: { + const bytes = css_section.getBytes() orelse break :bytes; + var len: usize = undefined; + const ptr = bytes.getData(&len) orelse break :bytes; + const data = ptr[0..len]; + log.warn("css parsing failed at {s}: {s} {d} {s}\n{s}", .{ + location, + glib.quarkToString(err.f_domain), + err.f_code, + err.f_message orelse "ยซunknownยป", + data, + }); + return; + } + log.warn("css parsing failed at {s}: {s} {d} {s}", .{ + location, + glib.quarkToString(err.f_domain), + err.f_code, + err.f_message orelse "ยซunknownยป", + }); + } + + //--------------------------------------------------------------- + // Libghostty Callbacks + + pub fn wakeup(self: *Self) void { + _ = self; + glib.MainContext.wakeup(null); + } + + //--------------------------------------------------------------- + // Virtual Methods + + fn startup(self: *Self) callconv(.c) void { + log.debug("startup", .{}); + + gio.Application.virtual_methods.startup.call( + Class.parent, + self.as(Parent), + ); + + // Set ourselves as the default application. + gio.Application.setDefault(self.as(gio.Application)); + + // The D-Bus connection is only valid after GApplication startup. + self.openUri().setDbusConnection( + self.as(gio.Application).getDbusConnection(), + ); + + // Setup our event loop + self.startupXev(); + + // Setup our style manager (light/dark mode) + self.startupStyleManager(); + + // Setup some signal handlers + self.startupSignals(); + + // Setup our action map + self.startupActionMap(); + + // Setup our global shortcuts + self.startupGlobalShortcuts(); + + // If we have any config diagnostics from loading, then we + // show the diagnostics dialog. We show this one as a general + // modal (not to any specific window) because we don't even + // know if the window will load. + self.showConfigErrorsDialog(); + } + + /// Configure libxev to use a specific backend. + /// + /// This must be called before any other xev APIs are used. + fn startupXev(self: *Self) void { + const priv = self.private(); + const config = priv.config.get(); + + // If our backend is auto then we have no setup to do. + if (config.@"async-backend" == .auto) return; + + // Setup our event loop backend to the preferred method + const result: bool = switch (config.@"async-backend") { + .auto => unreachable, + .epoll => if (comptime xev.dynamic) xev.prefer(.epoll) else false, + .io_uring => if (comptime xev.dynamic) xev.prefer(.io_uring) else false, + }; + + if (result) { + log.info( + "libxev manual backend={s}", + .{@tagName(xev.backend)}, + ); + } else { + log.warn( + "libxev manual backend failed, using default={s}", + .{@tagName(xev.backend)}, + ); + } + } + + /// Setup the style manager on startup. The primary task here is to + /// setup our initial light/dark mode based on the configuration and + /// setup listeners for changes to the style manager. + fn startupStyleManager(self: *Self) void { + const priv = self.private(); + const config = priv.config.get(); + + // Setup our initial light/dark + const style = self.as(adw.Application).getStyleManager(); + style.setColorScheme(switch (config.@"window-theme") { + .auto, .ghostty => auto: { + const lum = config.background.toTerminalRGB().perceivedLuminance(); + break :auto if (lum > 0.5) + .prefer_light + else + .prefer_dark; + }, + .system => .prefer_light, + .dark => .force_dark, + .light => .force_light, + }); + + // Setup color change notifications + _ = gobject.Object.signals.notify.connect( + style, + *Self, + handleStyleManagerDark, + self, + .{ .detail = "dark" }, + ); + + // Do an initial color scheme sync. This is idempotent and does nothing + // if our current theme matches what libghostty has so its safe to + // call. + handleStyleManagerDark(style, undefined, self); + } + + /// Setup signal handlers + fn startupSignals(self: *Self) void { + const priv = self.private(); + assert(priv.signal_source == null); + priv.signal_source = glib.unixSignalAdd( + std.posix.SIG.USR2, + handleSigusr2, + self, + ); + } + + /// Setup our action map. + fn startupActionMap(self: *Self) void { + const t_variant_type = glib.ext.VariantType.newFor(u64); + defer t_variant_type.free(); + + const as_variant_type = glib.VariantType.new("as"); + defer as_variant_type.free(); + + const actions = [_]ext.actions.Action(Self){ + .init("new-window", actionNewWindow, null), + .init("new-window-command", actionNewWindow, as_variant_type), + .init("open-config", actionOpenConfig, null), + .init("present-surface", actionPresentSurface, t_variant_type), + .init("quit", actionQuit, null), + .init("reload-config", actionReloadConfig, null), + }; + + ext.actions.add(Self, self, &actions); + } + + /// Setup our global shortcuts. + fn startupGlobalShortcuts(self: *Self) void { + const priv = self.private(); + + // On startup, our dbus connection should be available. + priv.global_shortcuts.setDbusConnection( + self.as(gio.Application).getDbusConnection(), + ); + + // Setup a binding so that the shortcut config always matches the app. + _ = gobject.Object.bindProperty( + self.as(gobject.Object), + "config", + priv.global_shortcuts.as(gobject.Object), + "config", + .{ .sync_create = true }, + ); + + // Setup the signal handler for global shortcut triggers + _ = GlobalShortcuts.signals.trigger.connect( + priv.global_shortcuts, + *Application, + globalShortcutTrigger, + self, + .{}, + ); + } + + fn activate(self: *Self) callconv(.c) void { + log.debug("activate", .{}); + + // Queue a new window + const priv = self.private(); + _ = priv.core_app.mailbox.push(.{ + .new_window = .{}, + }, .{ .forever = {} }); + + // Call the parent activate method. + gio.Application.virtual_methods.activate.call( + Class.parent, + self.as(Parent), + ); + } + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.config_errors_dialog.get()) |diag| { + diag.close(); + diag.unref(); // strong ref from get() + } + priv.config_errors_dialog.set(null); + if (priv.signal_source) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove signal source", .{}); + } + priv.signal_source = null; + } + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + self.deinit(); + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + // Signal Handlers + + /// SIGUSR2 signal handler via g_unix_signal_add + fn handleSigusr2(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse + return @intFromBool(glib.SOURCE_CONTINUE))); + + log.info("received SIGUSR2, reloading configuration", .{}); + Action.reloadConfig( + self, + .app, + .{}, + ) catch |err| { + // If we fail to reload the configuration, then we want the + // user to know it. For now we log but we should show another + // GUI. + log.warn("error reloading config: {}", .{err}); + }; + + return @intFromBool(glib.SOURCE_CONTINUE); + } + + fn handleCloseConfirmation( + _: *CloseConfirmationDialog, + self: *Self, + ) callconv(.c) void { + self.quitNow(); + } + + fn handleQuitTimerExpired(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud)); + const priv = self.private(); + priv.quit_timer = .expired; + return 0; + } + + fn handleStyleManagerDark( + style: *adw.StyleManager, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + const scheme: apprt.ColorScheme = if (style.getDark() == 0) + .light + else + .dark; + log.debug("style manager changed scheme={}", .{scheme}); + + const priv: *Private = self.private(); + const core_app = priv.core_app; + core_app.colorSchemeEvent(self.rt(), scheme) catch |err| { + log.warn("error updating app color scheme err={}", .{err}); + }; + for (core_app.surfaces.items) |surface| { + surface.core().colorSchemeCallback(scheme) catch |err| { + log.warn( + "unable to tell surface about color scheme change err={}", + .{err}, + ); + }; + } + + if (gtk_version.atLeast(4, 20, 0)) { + const gtk_scheme: gtk.InterfaceColorScheme = switch (scheme) { + .light => gtk.InterfaceColorScheme.light, + .dark => gtk.InterfaceColorScheme.dark, + }; + var value = gobject.ext.Value.newFrom(gtk_scheme); + gobject.Object.setProperty( + priv.css_provider.as(gobject.Object), + "prefers-color-scheme", + &value, + ); + for (priv.custom_css_providers.items) |css_provider| { + gobject.Object.setProperty( + css_provider.as(gobject.Object), + "prefers-color-scheme", + &value, + ); + } + } + } + + fn handleReloadConfig( + _: *ConfigErrorsDialog, + self: *Self, + ) callconv(.c) void { + // We clear our dialog reference because its going to close + // after response handling and we don't want to reuse it. + const priv = self.private(); + priv.config_errors_dialog.set(null); + + // Reload our config as if the app reloaded. + Action.reloadConfig( + self, + .app, + .{}, + ) catch |err| { + // If we fail to reload the configuration, then we want the + // user to know it. For now we log but we should show another + // GUI. + log.warn("error reloading config: {}", .{err}); + }; + } + + /// Show the config errors dialog if the config on our application + /// has diagnostics. + fn showConfigErrorsDialog(self: *Self) void { + const priv = self.private(); + + // If we already have a dialog, just update the config. + if (priv.config_errors_dialog.get()) |diag| { + defer diag.unref(); // get gets a strong ref + + var value = gobject.ext.Value.newFrom(priv.config); + defer value.unset(); + gobject.Object.setProperty( + diag.as(gobject.Object), + "config", + &value, + ); + + if (!priv.config.hasDiagnostics()) { + diag.close(); + } else { + diag.present(null); + } + + return; + } + + // No diagnostics, do nothing. + if (!priv.config.hasDiagnostics()) return; + + // No dialog yet, initialize a new one. There's no need to unref + // here because the widget that it becomes a part of takes ownership. + const dialog: *ConfigErrorsDialog = .new(priv.config); + priv.config_errors_dialog.set(dialog); + + // Connect to the reload signal so we know to reload our config. + _ = ConfigErrorsDialog.signals.@"reload-config".connect( + dialog, + *Application, + handleReloadConfig, + self, + .{}, + ); + + // Show it + dialog.present(null); + } + + fn globalShortcutTrigger( + _: *GlobalShortcuts, + action: *const Binding.Action, + self: *Self, + ) callconv(.c) void { + self.core().performAllAction(self.rt(), action.*) catch |err| { + log.warn("failed to perform action={}", .{err}); + }; + } + + fn actionReloadConfig( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + priv.core_app.performAction(self.rt(), .reload_config) catch |err| { + log.warn("error reloading config err={}", .{err}); + }; + } + + fn actionQuit( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + priv.core_app.performAction(self.rt(), .quit) catch |err| { + log.warn("error quitting err={}", .{err}); + }; + } + + /// Handle `app.new-window` and `app.new-window-command` GTK actions + pub fn actionNewWindow( + _: *gio.SimpleAction, + parameter_: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + log.debug("received new window action", .{}); + + var arena: std.heap.ArenaAllocator = .init(Application.default().allocator()); + defer arena.deinit(); + + const alloc = arena.allocator(); + + var working_directory: ?[:0]const u8 = null; + var title: ?[:0]const u8 = null; + var command: ?configpkg.Command = null; + var args: std.ArrayList([:0]const u8) = .empty; + + overrides: { + // were we given a parameter? + const parameter = parameter_ orelse break :overrides; + + const as_variant_type = glib.VariantType.new("as"); + defer as_variant_type.free(); + + // ensure that the supplied parameter is an array of strings + if (glib.Variant.isOfType(parameter, as_variant_type) == 0) { + log.warn("parameter is of type '{s}', not '{s}'", .{ + parameter.getTypeString(), + as_variant_type.peekString()[0..as_variant_type.getStringLength()], + }); + break :overrides; + } + + const s_variant_type = glib.VariantType.new("s"); + defer s_variant_type.free(); + + var it: glib.VariantIter = undefined; + _ = it.init(parameter); + + var e_seen: bool = false; + var i: usize = 0; + + while (it.nextValue()) |value| : (i += 1) { + defer value.unref(); + + // just to be sure + if (value.isOfType(s_variant_type) == 0) continue; + + var len: usize = undefined; + const buf = value.getString(&len); + const str = buf[0..len]; + + log.debug("new-window argument: {d} {s}", .{ i, str }); + + if (e_seen) { + const duplicated = alloc.dupeZ(u8, str) catch |err| { + log.warn("unable to duplicate argument {d} {s}: {t}", .{ i, str, err }); + break :overrides; + }; + args.append(alloc, duplicated) catch |err| { + log.warn("unable to append argument {d} {s}: {t}", .{ i, str, err }); + break :overrides; + }; + continue; + } + + if (std.mem.eql(u8, str, "-e")) { + e_seen = true; + continue; + } + + if (lib.cutPrefix(u8, str, "--command=")) |v| { + var cmd: configpkg.Command = undefined; + cmd.parseCLI(alloc, v) catch |err| { + log.warn("unable to parse command: {t}", .{err}); + continue; + }; + command = cmd; + continue; + } + if (lib.cutPrefix(u8, str, "--working-directory=")) |v| { + working_directory = alloc.dupeZ(u8, std.mem.trim(u8, v, &std.ascii.whitespace)) catch |err| wd: { + log.warn("unable to duplicate working directory: {t}", .{err}); + break :wd null; + }; + continue; + } + if (lib.cutPrefix(u8, str, "--title=")) |v| { + title = alloc.dupeZ(u8, std.mem.trim(u8, v, &std.ascii.whitespace)) catch |err| t: { + log.warn("unable to duplicate title: {t}", .{err}); + break :t null; + }; + continue; + } + } + } + + if (args.items.len > 0) { + command = .{ + .direct = args.items, + }; + } + + Action.newWindow(self, null, .{ + .command = command, + .working_directory = working_directory, + .title = title, + }) catch |err| { + log.warn("unable to create new window: {t}", .{err}); + }; + } + + pub fn actionOpenConfig( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + _ = self.core().mailbox.push(.open_config, .forever); + } + + fn actionPresentSurface( + _: *gio.SimpleAction, + parameter_: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const parameter = parameter_ orelse return; + + const t = glib.ext.VariantType.newFor(u64); + defer glib.VariantType.free(t); + + // Make sure that we've received a u64 from the system. + if (glib.Variant.isOfType(parameter, t) == 0) { + return; + } + + // Convert the u64 to a core surface by using it as a surface ID. + // A value of zero means that there was no target surface for the + // notification so we don't focus any surface. + const surface_id = parameter.getUint64(); + if (surface_id == 0) return; + const surface = self.core().findSurfaceByID(surface_id) orelse return; + + _ = self.core().mailbox.push( + .{ + .surface_message = .{ + .surface = surface, + .message = .present_surface, + }, + }, + .forever, + ); + } + + //---------------------------------------------------------------- + // Boilerplate/Noise + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + // Register our compiled resources exactly once. + { + const c = @cImport({ + // generated header files + @cInclude("ghostty_resources.h"); + }); + if (c.ghostty_get_resource()) |ptr| { + gio.resourcesRegister(@ptrCast(@alignCast(ptr))); + } else { + // If we fail to load resources then things will + // probably look really bad but it shouldn't stop our + // app from loading. + log.warn("unable to load resources", .{}); + } + } + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.config.impl, + }); + + // Virtual methods + gio.Application.virtual_methods.activate.implement(class, &activate); + gio.Application.virtual_methods.startup.implement(class, &startup); + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + }; + + pub fn openUrlFallback(self: *Application, kind: apprt.action.OpenUrl.Kind, url: []const u8) void { + // Fallback to the minimal cross-platform way of opening a URL. + // This is always a safe fallback and enables for example Windows + // to open URLs (GTK on Windows via WSL is a thing). + internal_os.open( + self.allocator(), + kind, + url, + ) catch |err| log.warn("unable to open url: {}", .{err}); + } +}; + +/// All apprt action handlers +const Action = struct { + pub fn closeTab(target: apprt.Target, value: apprt.Action.Value(.close_tab)) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + return surface.as(gtk.Widget).activateAction( + "tab.close", + glib.ext.VariantType.stringFor([:0]const u8), + @as([*:0]const u8, @tagName(value)), + ) != 0; + }, + } + } + + pub fn closeWindow(target: apprt.Target) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + return surface.as(gtk.Widget).activateAction("win.close", null) != 0; + }, + } + } + + pub fn copyTitleToClipboard(target: apprt.Target) bool { + return switch (target) { + .app => false, + .surface => |v| v.rt_surface.gobj().copyTitleToClipboard(), + }; + } + + pub fn configChange( + self: *Application, + target: apprt.Target, + new_config: *const CoreConfig, + ) !void { + // Wrap our config in a GObject. This will clone it. + const alloc = self.allocator(); + const config_obj: *Config = try .new(alloc, new_config); + defer config_obj.unref(); + + switch (target) { + .surface => |core| core.rt_surface.surface.setConfig(config_obj), + .app => self.setConfig(config_obj), + } + } + + pub fn desktopNotification( + self: *Application, + target: apprt.Target, + n: apprt.action.DesktopNotification, + ) void { + switch (target) { + .app => {}, + .surface => |v| { + v.rt_surface.gobj().sendDesktopNotification(n.title, n.body); + return; + }, + } + + // Set a default title if we don't already have one + const t = switch (n.title.len) { + 0 => "Ghostty", + else => n.title, + }; + + const notification = gio.Notification.new(t); + defer notification.unref(); + notification.setBody(n.body); + + const icon = gio.ThemedIcon.new("com.mitchellh.ghostty"); + defer icon.unref(); + notification.setIcon(icon.as(gio.Icon)); + notification.setDefaultActionAndTargetValue( + "app.present-surface", + glib.Variant.newUint64(0), + ); + + // We set the notification ID to the body content. If the content is the + // same, this notification may replace a previous notification + const gio_app = self.as(gio.Application); + gio_app.sendNotification(n.body, notification); + } + + pub fn equalizeSplits(target: apprt.Target) bool { + switch (target) { + .app => { + log.warn("equalize splits to app is unexpected", .{}); + return false; + }, + + .surface => |core| { + const surface = core.rt_surface.surface; + return surface.as(gtk.Widget).activateAction("split-tree.equalize", null) != 0; + }, + } + } + + pub fn gotoSplit( + target: apprt.Target, + to: apprt.action.GotoSplit, + ) bool { + switch (target) { + .app => return false, + .surface => |core| { + // Design note: we can't use widget actions here because + // we need to know whether there is a goto target for returning + // the proper perform result (boolean). + + const surface = core.rt_surface.surface; + const tree = ext.getAncestor( + SplitTree, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a split tree, ignoring goto_split", .{}); + return false; + }; + + return tree.goto(switch (to) { + .previous => .previous_wrapped, + .next => .next_wrapped, + .up => .{ .spatial = .up }, + .down => .{ .spatial = .down }, + .left => .{ .spatial = .left }, + .right => .{ .spatial = .right }, + }); + }, + } + } + + pub fn gotoTab( + target: apprt.Target, + tab: apprt.action.GotoTab, + ) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + const window = ext.getAncestor( + Window, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a window, ignoring new_tab", .{}); + return false; + }; + + return window.selectTab(switch (tab) { + .previous => .previous, + .next => .next, + .last => .last, + else => .{ .n = @intCast(@intFromEnum(tab)) }, + }); + }, + } + } + + pub fn gotoWindow(direction: apprt.action.GotoWindow) bool { + const glist = gtk.Window.listToplevels(); + defer glist.free(); + + // The window we're starting from is typically our active window. + const starting: *glib.List = @as(?*glib.List, glist.findCustom( + null, + findActiveWindow, + )) orelse glist; + + // Go forward or backwards in the list until we find a valid + // window that is visible. + var current_: ?*glib.List = starting; + while (current_) |node| : (current_ = switch (direction) { + .next => node.f_next, + .previous => node.f_prev, + }) { + const data = node.f_data orelse continue; + const gtk_window: *gtk.Window = @ptrCast(@alignCast(data)); + if (gotoWindowMaybe(gtk_window)) return true; + } + + // If we reached here, we didn't find a valid window to focus. + // Wrap around. + current_ = switch (direction) { + .next => glist, + .previous => last: { + var end: *glib.List = glist; + while (end.f_next) |next| end = next; + break :last end; + }, + }; + while (current_) |node| : (current_ = switch (direction) { + .next => node.f_next, + .previous => node.f_prev, + }) { + if (current_ == starting) break; + const data = node.f_data orelse continue; + const gtk_window: *gtk.Window = @ptrCast(@alignCast(data)); + if (gotoWindowMaybe(gtk_window)) return true; + } + + return false; + } + + fn gotoWindowMaybe(gtk_window: *gtk.Window) bool { + // If it is already active skip it. + if (gtk_window.isActive() != 0) return false; + // If it is hidden, skip it. + if (gtk_window.as(gtk.Widget).isVisible() == 0) return false; + // If it isn't a Ghostty window, skip it. + const window = gobject.ext.cast( + Window, + gtk_window, + ) orelse return false; + + // Focus our active surface + const surface = window.getActiveSurface() orelse return false; + gtk.Window.present(gtk_window); + surface.grabFocus(); + return true; + } + + pub fn initialSize( + target: apprt.Target, + value: apprt.action.InitialSize, + ) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + surface.setDefaultSize(.{ + .width = value.width, + .height = value.height, + }); + return true; + }, + } + } + + pub fn mouseOverLink( + target: apprt.Target, + value: apprt.action.MouseOverLink, + ) void { + switch (target) { + .app => log.warn("mouse over link to app is unexpected", .{}), + .surface => |surface| surface.rt_surface.gobj().setMouseHoverUrl( + if (value.url.len > 0) value.url else null, + ), + } + } + + pub fn mouseShape( + target: apprt.Target, + shape: terminal.MouseShape, + ) void { + switch (target) { + .app => log.warn("mouse shape to app is unexpected", .{}), + .surface => |surface| surface.rt_surface.gobj().setMouseShape(shape), + } + } + + pub fn mouseVisibility( + target: apprt.Target, + visibility: apprt.action.MouseVisibility, + ) void { + switch (target) { + .app => log.warn("mouse visibility to app is unexpected", .{}), + .surface => |surface| surface.rt_surface.gobj().setMouseHidden(switch (visibility) { + .visible => false, + .hidden => true, + }), + } + } + + pub fn moveTab( + target: apprt.Target, + value: apprt.action.MoveTab, + ) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + const window = ext.getAncestor( + Window, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a window, ignoring new_tab", .{}); + return false; + }; + + return window.moveTab( + surface, + @intCast(value.amount), + ); + }, + } + } + + pub fn newSplit( + target: apprt.Target, + direction: apprt.action.SplitDirection, + ) bool { + switch (target) { + .app => { + log.warn("new split to app is unexpected", .{}); + return false; + }, + + .surface => |core| { + const surface = core.rt_surface.surface; + + return surface.as(gtk.Widget).activateAction( + "split-tree.new-split", + "&s", + @tagName(direction).ptr, + ) != 0; + }, + } + } + + pub fn newTab(target: apprt.Target) bool { + switch (target) { + .app => { + log.warn("new tab to app is unexpected", .{}); + return false; + }, + + .surface => |core| { + // Get the window ancestor of the surface. Surfaces shouldn't + // be aware they might be in windows but at the app level we + // can do this. + const surface = core.rt_surface.surface; + const window = ext.getAncestor( + Window, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a window, ignoring new_tab", .{}); + return false; + }; + window.newTab(core); + return true; + }, + } + } + + pub fn newWindow( + self: *Application, + parent: ?*CoreSurface, + overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }, + ) !void { + // Note that we've requested a window at least once. This is used + // to trigger quit on no windows. Note I'm not sure if this is REALLY + // necessary, but I don't want to risk a bug where on a slow machine + // or something we quit immediately after starting up because there + // was a delay in the event loop before we created a Window. + self.private().requested_window = true; + + const win = Window.new(self, .{ + .title = overrides.title, + }); + initAndShowWindow( + self, + win, + parent, + .{ + .command = overrides.command, + .working_directory = overrides.working_directory, + .title = overrides.title, + }, + ); + } + + fn initAndShowWindow( + self: *Application, + win: *Window, + parent: ?*CoreSurface, + overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }, + ) void { + // Setup a binding so that whenever our config changes so does the + // window. There's never a time when the window config should be out + // of sync with the application config. + _ = gobject.Object.bindProperty( + self.as(gobject.Object), + "config", + win.as(gobject.Object), + "config", + .{}, + ); + + // Create a new tab with window context (first tab in new window) + win.newTabForWindow(parent, .{ + .command = overrides.command, + .working_directory = overrides.working_directory, + .title = overrides.title, + }); + + // Estimate the initial window size before presenting so the window + // manager can position it correctly. + if (win.getActiveSurface()) |surface| { + surface.estimateInitialSize(); + if (surface.getDefaultSize()) |size| { + win.as(gtk.Window).setDefaultSize( + @intCast(size.width), + @intCast(size.height), + ); + } + } + + // Show the window + gtk.Window.present(win.as(gtk.Window)); + } + + pub fn openConfig(self: *Application) bool { + // Get the config file path + const alloc = self.allocator(); + const path = configpkg.edit.openPath(alloc) catch |err| { + log.warn("error getting config file path: {}", .{err}); + return false; + }; + defer alloc.free(path); + + // Open it using openURL. "path" isn't actually a URL but + // at the time of writing that works just fine for GTK. + openUrl(self, .{ .kind = .text, .url = path }); + return true; + } + + pub fn openUrl( + self: *Application, + value: apprt.action.OpenUrl, + ) void { + if (std.mem.startsWith(u8, value.url, "/")) { + self.openUrlFallback(value.kind, value.url); + return; + } + if (std.mem.startsWith(u8, value.url, "file://")) { + self.openUrlFallback(value.kind, value.url); + return; + } + + self.openUri().start(value) catch |err| { + log.err("unable to open uri err={}", .{err}); + self.openUrlFallback(value.kind, value.url); + return; + }; + } + + pub fn pwd( + target: apprt.Target, + value: apprt.action.Pwd, + ) void { + switch (target) { + .app => log.warn("pwd to app is unexpected", .{}), + .surface => |surface| surface.rt_surface.gobj().setPwd(value.pwd), + } + } + + pub fn quitTimer( + self: *Application, + mode: apprt.action.QuitTimer, + ) !void { + switch (mode) { + .start => self.startQuitTimer(), + .stop => self.stopQuitTimer(), + } + } + + pub fn presentTerminal( + target: apprt.Target, + ) bool { + return switch (target) { + .app => false, + .surface => |v| surface: { + v.rt_surface.surface.present(); + break :surface true; + }, + }; + } + + pub fn progressReport( + target: apprt.Target, + value: terminal.osc.Command.ProgressReport, + ) bool { + return switch (target) { + .app => false, + .surface => |v| surface: { + v.rt_surface.surface.setProgressReport(value); + break :surface true; + }, + }; + } + + pub fn promptTitle(target: apprt.Target, value: apprt.action.PromptTitle) bool { + switch (value) { + .surface => switch (target) { + .app => return false, + .surface => |v| { + v.rt_surface.surface.promptTitle(); + return true; + }, + }, + .tab => { + switch (target) { + .app => return false, + .surface => |v| { + const surface = v.rt_surface.surface; + const tab = ext.getAncestor( + Tab, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a tab, ignoring prompt_tab_title", .{}); + return false; + }; + tab.promptTabTitle(); + return true; + }, + } + }, + } + } + + /// Reload the configuration for the application and propagate it + /// across the entire application and all terminals. + pub fn reloadConfig( + self: *Application, + target: apprt.Target, + opts: apprt.action.ReloadConfig, + ) !void { + // Tell systemd that reloading has started. + systemd.notify.reloading(); + + // When we exit this function tell systemd that reloading has finished. + defer systemd.notify.ready(); + + // Get our config object. + const config: *Config = config: { + // Soft-reloading applies conditional logic to the existing loaded + // config so we return that as-is (but take a reference). + if (opts.soft) { + break :config self.private().config.ref(); + } + + // Hard reload, load a new config completely. + const alloc = self.allocator(); + var config = try CoreConfig.load(alloc); + defer config.deinit(); + break :config try .new(alloc, &config); + }; + defer config.unref(); + + // Update the proper target. This will trigger a `config_change` + // apprt action which will propagate the config properly to our + // property system. + switch (target) { + .app => try self.core().updateConfig( + self.rt(), + config.get(), + ), + .surface => |core| try core.updateConfig(config.get()), + } + } + + pub fn render(target: apprt.Target) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.redraw(), + } + } + + pub fn resizeSplit( + target: apprt.Target, + value: apprt.action.ResizeSplit, + ) bool { + switch (target) { + .app => { + log.warn("resize_split to app is unexpected", .{}); + return false; + }, + .surface => |core| { + const surface = core.rt_surface.surface; + const tree = ext.getAncestor( + SplitTree, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a split tree, ignoring resize_split", .{}); + return false; + }; + + // If the tree has no splits (only one leaf), this action is not performable. + // This allows the key event to pass through to the terminal. + if (!tree.getIsSplit()) return false; + + return tree.resize( + switch (value.direction) { + .up => .up, + .down => .down, + .left => .left, + .right => .right, + }, + value.amount, + ) catch |err| switch (err) { + error.OutOfMemory => { + log.warn("unable to resize split, out of memory", .{}); + return false; + }, + }; + }, + } + } + + pub fn ringBell(target: apprt.Target) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.setBellRinging(true), + } + } + + pub fn scrollbar( + target: apprt.Target, + value: apprt.Action.Value(.scrollbar), + ) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.setScrollbar(value), + } + } + + pub fn startSearch(target: apprt.Target, value: apprt.action.StartSearch) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.setSearchActive(true, value.needle), + } + } + + pub fn endSearch(target: apprt.Target) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.setSearchActive(false, ""), + } + } + + pub fn searchTotal(target: apprt.Target, value: apprt.action.SearchTotal) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.setSearchTotal(value.total), + } + } + + pub fn searchSelected(target: apprt.Target, value: apprt.action.SearchSelected) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.setSearchSelected(value.selected), + } + } + + pub fn setTitle( + target: apprt.Target, + value: apprt.action.SetTitle, + ) void { + switch (target) { + .app => log.warn("set_title to app is unexpected", .{}), + .surface => |surface| surface.rt_surface.gobj().setTitle(value.title), + } + } + + pub fn setTabTitle( + target: apprt.Target, + value: apprt.action.SetTitle, + ) bool { + switch (target) { + .app => { + log.warn("set_tab_title to app is unexpected", .{}); + return false; + }, + .surface => |core| { + const surface = core.rt_surface.surface; + const tab = ext.getAncestor( + Tab, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a tab, ignoring set_tab_title", .{}); + return false; + }; + tab.setTitleOverride(if (value.title.len == 0) null else value.title); + return true; + }, + } + } + + pub fn showChildExited( + target: apprt.Target, + value: apprt.surface.Message.ChildExited, + ) bool { + return switch (target) { + .app => false, + .surface => |v| v.rt_surface.surface.childExited(value), + }; + } + + pub fn showGtkInspector() void { + gtk.Window.setInteractiveDebugging(@intFromBool(true)); + } + + pub fn sizeLimit( + target: apprt.Target, + value: apprt.action.SizeLimit, + ) bool { + switch (target) { + .app => return false, + .surface => |core| { + // Note: we ignore the max size currently because we have + // no mechanism to enforce it. + const surface = core.rt_surface.surface; + surface.setMinSize(.{ + .width = value.min_width, + .height = value.min_height, + }); + + return true; + }, + } + } + + pub fn toggleFullscreen(target: apprt.Target) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.toggleFullscreen(), + } + } + + pub fn toggleQuickTerminal(self: *Application) bool { + // If we already have a quick terminal window, we just toggle the + // visibility of it. + if (getQuickTerminalWindow()) |win| { + win.toggleVisibility(); + return true; + } + + // If we don't support quick terminals then we do nothing. + const priv = self.private(); + if (!priv.winproto.supportsQuickTerminal()) return false; + + // Create our new window as a quick terminal + const win = gobject.ext.newInstance(Window, .{ + .application = self, + .@"quick-terminal" = true, + }); + assert(win.isQuickTerminal()); + initAndShowWindow(self, win, null, .none); + return true; + } + + pub fn toggleSplitZoom(target: apprt.Target) bool { + switch (target) { + .app => { + log.warn("toggle_split_zoom to app is unexpected", .{}); + return false; + }, + + .surface => |core| { + // TODO: pass surface ID when we have that + const surface = core.rt_surface.surface; + const tree = ext.getAncestor( + SplitTree, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a split tree, ignoring toggle_split_zoom", .{}); + return false; + }; + + // If the tree has no splits (only one leaf), this action is not performable. + // This allows the key event to pass through to the terminal. + if (!tree.getIsSplit()) return false; + + return surface.as(gtk.Widget).activateAction("split-tree.zoom", null) != 0; + }, + } + } + + pub fn showOnScreenKeyboard(target: apprt.Target) bool { + switch (target) { + .app => { + log.warn("show_on_screen_keyboard to app is unexpected", .{}); + return false; + }, + // NOTE: Even though `activateOsk` takes a gdk.Event, it's currently + // unused by all implementations of `activateOsk` as of GTK 4.18. + // The commit that introduced the method (ce6aa73c) clarifies that + // the event *may* be used by other IM backends, but for Linux desktop + // environments this doesn't matter. + .surface => |v| return v.rt_surface.surface.showOnScreenKeyboard(null), + } + } + + fn getQuickTerminalWindow() ?*Window { + // Find a quick terminal window. + const list = gtk.Window.listToplevels(); + defer list.free(); + if (ext.listFind(gtk.Window, list, struct { + fn find(gtk_win: *gtk.Window) bool { + const win = gobject.ext.cast( + Window, + gtk_win, + ) orelse return false; + return win.isQuickTerminal(); + } + }.find)) |w| return gobject.ext.cast( + Window, + w, + ).?; + + return null; + } + + pub fn toggleMaximize(target: apprt.Target) void { + switch (target) { + .app => {}, + .surface => |v| v.rt_surface.surface.toggleMaximize(), + } + } + + pub fn toggleTabOverview(target: apprt.Target) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + const window = ext.getAncestor( + Window, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a window, ignoring new_tab", .{}); + return false; + }; + + window.toggleTabOverview(); + return true; + }, + } + } + + pub fn toggleWindowDecorations(target: apprt.Target) bool { + switch (target) { + .app => return false, + .surface => |core| { + const surface = core.rt_surface.surface; + const window = ext.getAncestor( + Window, + surface.as(gtk.Widget), + ) orelse { + log.warn("surface is not in a window, ignoring toggle_window_decorations", .{}); + return false; + }; + + window.toggleWindowDecorations(); + return true; + }, + } + } + + pub fn toggleCommandPalette(target: apprt.Target) bool { + switch (target) { + .app => return false, + .surface => |surface| { + return surface.rt_surface.gobj().toggleCommandPalette(); + }, + } + } + + pub fn controlInspector(target: apprt.Target, value: apprt.Action.Value(.inspector)) bool { + switch (target) { + .app => return false, + .surface => |surface| { + return surface.rt_surface.gobj().controlInspector(value); + }, + } + } + + pub fn commandFinished(target: apprt.Target, value: apprt.Action.Value(.command_finished)) bool { + switch (target) { + .app => return false, + .surface => |surface| { + return surface.rt_surface.gobj().commandFinished(value); + }, + } + } + + pub fn setReadonly(target: apprt.Target, value: apprt.Action.Value(.readonly)) bool { + switch (target) { + .app => return false, + .surface => |surface| { + return surface.rt_surface.gobj().setReadonly(value); + }, + } + } + + pub fn keySequence(target: apprt.Target, value: apprt.Action.Value(.key_sequence)) bool { + switch (target) { + .app => { + log.warn("key_sequence action to app is unexpected", .{}); + return false; + }, + .surface => |core| { + core.rt_surface.gobj().keySequenceAction(value) catch |err| { + log.warn("error handling key_sequence action: {}", .{err}); + }; + return true; + }, + } + } + + pub fn keyTable(target: apprt.Target, value: apprt.Action.Value(.key_table)) bool { + switch (target) { + .app => { + log.warn("key_table action to app is unexpected", .{}); + return false; + }, + .surface => |core| { + core.rt_surface.gobj().keyTableAction(value) catch |err| { + log.warn("error handling key_table action: {}", .{err}); + }; + return true; + }, + } + } +}; + +/// This sets various GTK-related environment variables as necessary +/// given the runtime environment or configuration. +/// +/// This must be called BEFORE GTK initialization. +fn setGtkEnv(config: *const CoreConfig) error{NoSpaceLeft}!void { + assert(gtk.isInitialized() == 0); + + var gdk_debug: struct { + /// output OpenGL debug information + opengl: bool = false, + /// disable GLES, Ghostty can't use GLES + @"gl-disable-gles": bool = false, + // GTK's new renderer can cause blurry font when using fractional scaling. + @"gl-no-fractional": bool = false, + /// Disabling Vulkan can improve startup times by hundreds of + /// milliseconds on some systems. We don't use Vulkan so we can just + /// disable it. + @"vulkan-disable": bool = false, + } = .{ + // `gtk-opengl-debug` dumps logs directly to stderr so both must be true + // to enable OpenGL debugging. + .opengl = state.logging.stderr and config.@"gtk-opengl-debug", + }; + + var gdk_disable: struct { + @"gles-api": bool = false, + /// current gtk implementation for color management is not good enough. + /// see: https://bugs.kde.org/show_bug.cgi?id=495647 + /// gtk issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/6864 + @"color-mgmt": bool = true, + /// Disabling Vulkan can improve startup times by hundreds of + /// milliseconds on some systems. We don't use Vulkan so we can just + /// disable it. + vulkan: bool = false, + } = .{}; + + environment: { + if (gtk_version.runtimeAtLeast(4, 18, 0)) { + gdk_disable.@"color-mgmt" = false; + } + + if (gtk_version.runtimeAtLeast(4, 16, 0)) { + // From gtk 4.16, GDK_DEBUG is split into GDK_DEBUG and GDK_DISABLE. + // For the remainder of "why" see the 4.14 comment below. + gdk_disable.@"gles-api" = true; + gdk_disable.vulkan = true; + break :environment; + } + if (gtk_version.runtimeAtLeast(4, 14, 0)) { + // We need to export GDK_DEBUG to run on Wayland after GTK 4.14. + // Older versions of GTK do not support these values so it is safe + // to always set this. Forwards versions are uncertain so we'll have + // to reassess... + // + // Upstream issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/6589 + gdk_debug.@"gl-disable-gles" = true; + gdk_debug.@"vulkan-disable" = true; + + if (gtk_version.runtimeUntil(4, 17, 5)) { + // Removed at GTK v4.17.5 + gdk_debug.@"gl-no-fractional" = true; + } + break :environment; + } + + // Versions prior to 4.14 are a bit of an unknown for Ghostty. It + // is an environment that isn't tested well and we don't have a + // good understanding of what we may need to do. + gdk_debug.@"vulkan-disable" = true; + } + + { + var buf: [1024]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + const writer = fmt.writer(); + var first: bool = true; + inline for (@typeInfo(@TypeOf(gdk_debug)).@"struct".fields) |field| { + if (@field(gdk_debug, field.name)) { + if (!first) try writer.writeAll(","); + try writer.writeAll(field.name); + first = false; + } + } + try writer.writeByte(0); + const value = fmt.getWritten(); + log.warn("setting GDK_DEBUG={s}", .{value[0 .. value.len - 1]}); + _ = internal_os.setenv("GDK_DEBUG", value[0 .. value.len - 1 :0]); + } + + { + var buf: [1024]u8 = undefined; + var fmt = std.io.fixedBufferStream(&buf); + const writer = fmt.writer(); + var first: bool = true; + inline for (@typeInfo(@TypeOf(gdk_disable)).@"struct".fields) |field| { + if (@field(gdk_disable, field.name)) { + if (!first) try writer.writeAll(","); + try writer.writeAll(field.name); + first = false; + } + } + try writer.writeByte(0); + const value = fmt.getWritten(); + log.warn("setting GDK_DISABLE={s}", .{value[0 .. value.len - 1]}); + _ = internal_os.setenv("GDK_DISABLE", value[0 .. value.len - 1 :0]); + } +} + +fn findActiveWindow(data: ?*const anyopaque, _: ?*const anyopaque) callconv(.c) c_int { + const window: *gtk.Window = @ptrCast(@alignCast(@constCast(data orelse return -1))); + + // Confusingly, `isActive` returns 1 when active, + // but we want to return 0 to indicate equality. + // Abusing integers to be enums and booleans is a terrible idea, C. + return if (window.isActive() != 0) 0 else -1; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/clipboard_confirmation_dialog.zig b/vendor/libghostty-vt/src/apprt/gtk/class/clipboard_confirmation_dialog.zig new file mode 100644 index 00000000..d44d38a3 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/clipboard_confirmation_dialog.zig @@ -0,0 +1,352 @@ +const std = @import("std"); +const adw = @import("adw"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const apprt = @import("../../../apprt.zig"); +const gresource = @import("../build/gresource.zig"); +const i18n = @import("../../../os/main.zig").i18n; +const adw_version = @import("../adw_version.zig"); +const Common = @import("../class.zig").Common; +const Dialog = @import("dialog.zig").Dialog; + +const log = std.log.scoped(.gtk_ghostty_clipboard_confirmation); + +/// Whether we're able to have the remember switch +const can_remember = adw_version.supportsSwitchRow(); + +pub const ClipboardConfirmationDialog = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = Dialog; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyClipboardConfirmationDialog", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const @"can-remember" = struct { + pub const name = "can-remember"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "can_remember", + ), + }, + ); + }; + + pub const request = struct { + pub const name = "request"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*apprt.ClipboardRequest, + .{ + .accessor = C.privateBoxedFieldAccessor("request"), + }, + ); + }; + + pub const @"clipboard-contents" = struct { + pub const name = "clipboard-contents"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*gtk.TextBuffer, + .{ + .accessor = C.privateObjFieldAccessor("clipboard_contents"), + }, + ); + }; + + pub const blur = struct { + pub const name = "blur"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "blur", + ), + }, + ); + }; + }; + + pub const signals = struct { + pub const deny = struct { + pub const name = "deny"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{bool}, + void, + ); + }; + + pub const confirm = struct { + pub const name = "confirm"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{bool}, + void, + ); + }; + }; + + const Private = struct { + /// The request that this dialog is for. + request: ?*apprt.ClipboardRequest = null, + + /// The clipboard contents being read/written. + clipboard_contents: ?*gtk.TextBuffer = null, + + /// Whether the contents should be blurred. + blur: bool = false, + + /// Whether the user can remember the choice. + can_remember: bool = false, + + // Template bindings + text_view_scroll: *gtk.ScrolledWindow, + text_view: *gtk.TextView, + reveal_button: *gtk.Button, + hide_button: *gtk.Button, + remember_choice: if (can_remember) *adw.SwitchRow else void, + + pub var offset: c_int = 0; + }; + + pub fn new() *Self { + return gobject.ext.newInstance(Self, .{}); + } + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + // Trigger initial values + self.propBlur(undefined, null); + self.propRequest(undefined, null); + } + + pub fn present(self: *Self, parent: ?*gtk.Widget) void { + self.as(Dialog).present(parent); + } + + /// Get the clipboard request without copying. + pub fn getRequest(self: *Self) ?*apprt.ClipboardRequest { + return self.private().request; + } + + /// Get the clipboard contents without copying. + pub fn getClipboardContents(self: *Self) ?*gtk.TextBuffer { + return self.private().clipboard_contents; + } + + //--------------------------------------------------------------- + // Signal Handlers + + fn propBlur( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + if (priv.blur) { + priv.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false)); + priv.text_view.as(gtk.Widget).addCssClass("blurred"); + priv.reveal_button.as(gtk.Widget).setVisible(@intFromBool(true)); + priv.hide_button.as(gtk.Widget).setVisible(@intFromBool(false)); + } else { + priv.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(true)); + priv.text_view.as(gtk.Widget).removeCssClass("blurred"); + priv.reveal_button.as(gtk.Widget).setVisible(@intFromBool(false)); + priv.hide_button.as(gtk.Widget).setVisible(@intFromBool(false)); + } + } + + fn propRequest( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + const req = priv.request orelse return; + switch (req.*) { + .osc_52_write => { + self.as(Dialog.Parent).setHeading(i18n._("Authorize Clipboard Access")); + self.as(Dialog.Parent).setBody(i18n._("An application is attempting to write to the clipboard. The current clipboard contents are shown below.")); + }, + .osc_52_read => { + self.as(Dialog.Parent).setHeading(i18n._("Authorize Clipboard Access")); + self.as(Dialog.Parent).setBody(i18n._("An application is attempting to read from the clipboard. The current clipboard contents are shown below.")); + }, + .paste => { + self.as(Dialog.Parent).setHeading(i18n._("Warning: Potentially Unsafe Paste")); + self.as(Dialog.Parent).setBody(i18n._("Pasting this text into the terminal may be dangerous as it looks like some commands may be executed.")); + }, + } + } + + fn revealButtonClicked(_: *gtk.Button, self: *Self) callconv(.c) void { + const priv = self.private(); + priv.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(true)); + priv.text_view.as(gtk.Widget).removeCssClass("blurred"); + priv.hide_button.as(gtk.Widget).setVisible(@intFromBool(true)); + priv.reveal_button.as(gtk.Widget).setVisible(@intFromBool(false)); + } + + fn hideButtonClicked(_: *gtk.Button, self: *Self) callconv(.c) void { + const priv = self.private(); + priv.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false)); + priv.text_view.as(gtk.Widget).addCssClass("blurred"); + priv.hide_button.as(gtk.Widget).setVisible(@intFromBool(false)); + priv.reveal_button.as(gtk.Widget).setVisible(@intFromBool(true)); + } + + //--------------------------------------------------------------- + // Virtual methods + + fn response( + self: *Self, + response_id: [*:0]const u8, + ) callconv(.c) void { + const remember: bool = if (comptime can_remember) remember: { + const priv = self.private(); + break :remember priv.remember_choice.getActive() != 0; + } else false; + + if (std.mem.orderZ(u8, response_id, "cancel") == .eq) { + signals.deny.impl.emit( + self, + null, + .{remember}, + null, + ); + } else if (std.mem.orderZ(u8, response_id, "ok") == .eq) { + signals.confirm.impl.emit( + self, + null, + .{remember}, + null, + ); + } + } + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.clipboard_contents) |v| { + v.unref(); + priv.clipboard_contents = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.request) |v| { + glib.ext.destroy(v); + priv.request = null; + } + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + if (comptime adw_version.atLeast(1, 4, 0)) + comptime gresource.blueprint(.{ + .major = 1, + .minor = 4, + .name = "clipboard-confirmation-dialog", + }) + else + comptime gresource.blueprint(.{ + .major = 1, + .minor = 0, + .name = "clipboard-confirmation-dialog", + }), + ); + + // Bindings + class.bindTemplateChildPrivate("text_view_scroll", .{}); + class.bindTemplateChildPrivate("text_view", .{}); + class.bindTemplateChildPrivate("hide_button", .{}); + class.bindTemplateChildPrivate("reveal_button", .{}); + if (comptime can_remember) { + class.bindTemplateChildPrivate("remember_choice", .{}); + } + + // Template Callbacks + class.bindTemplateCallback("reveal_clicked", &revealButtonClicked); + class.bindTemplateCallback("hide_clicked", &hideButtonClicked); + class.bindTemplateCallback("notify_blur", &propBlur); + class.bindTemplateCallback("notify_request", &propRequest); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.blur.impl, + properties.@"can-remember".impl, + properties.@"clipboard-contents".impl, + properties.request.impl, + }); + + // Signals + signals.confirm.impl.register(.{}); + signals.deny.impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + Dialog.virtual_methods.response.implement(class, &response); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/close_confirmation_dialog.zig b/vendor/libghostty-vt/src/apprt/gtk/class/close_confirmation_dialog.zig new file mode 100644 index 00000000..5919f9c9 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/close_confirmation_dialog.zig @@ -0,0 +1,204 @@ +const std = @import("std"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); +const i18n = @import("../../../os/main.zig").i18n; +const Common = @import("../class.zig").Common; +const Dialog = @import("dialog.zig").Dialog; + +const log = std.log.scoped(.gtk_ghostty_close_confirmation_dialog); + +pub const CloseConfirmationDialog = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = Dialog; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyCloseConfirmationDialog", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const target = struct { + pub const name = "target"; + const impl = gobject.ext.defineProperty( + name, + Self, + Target, + .{ + .default = .app, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "target", + ), + }, + ); + }; + }; + + pub const signals = struct { + pub const @"close-request" = struct { + pub const name = "close-request"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + pub const cancel = struct { + pub const name = "cancel"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + + const Private = struct { + target: Target, + pub var offset: c_int = 0; + }; + + pub fn new(target: Target) *Self { + return gobject.ext.newInstance(Self, .{ + .target = target, + }); + } + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + pub fn present(self: *Self, parent: ?*gtk.Widget) void { + // Setup our title/body text. + const priv = self.private(); + self.as(Dialog.Parent).setHeading(priv.target.title()); + self.as(Dialog.Parent).setBody(priv.target.body()); + + // Show it + self.as(Dialog).present(parent); + } + + pub fn close(self: *Self) void { + self.as(Dialog).close(); + } + + fn response( + self: *Self, + response_id: [*:0]const u8, + ) callconv(.c) void { + if (std.mem.orderZ(u8, response_id, "close") == .eq) { + signals.@"close-request".impl.emit( + self, + null, + .{}, + null, + ); + } else { + signals.cancel.impl.emit( + self, + null, + .{}, + null, + ); + } + } + + fn dispose(self: *Self) callconv(.c) void { + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const refSink = C.refSink; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(Dialog); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 2, + .name = "close-confirmation-dialog", + }), + ); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.target.impl, + }); + + // Signals + signals.@"close-request".impl.register(.{}); + signals.cancel.impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + Dialog.virtual_methods.response.implement(class, &response); + } + + pub const as = C.Class.as; + }; +}; + +/// The target of a close dialog. +/// +/// This is here so that we can consolidate all logic related to +/// prompting the user and closing windows/tabs/surfaces/etc. +/// together into one struct that is the sole source of truth. +pub const Target = enum(c_int) { + app, + tab, + window, + surface, + + pub fn title(self: Target) [*:0]const u8 { + return switch (self) { + .app => i18n._("Quit Ghostty?"), + .tab => i18n._("Close Tab?"), + .window => i18n._("Close Window?"), + .surface => i18n._("Close Split?"), + }; + } + + pub fn body(self: Target) [*:0]const u8 { + return switch (self) { + .app => i18n._("All terminal sessions will be terminated."), + .tab => i18n._("All terminal sessions in this tab will be terminated."), + .window => i18n._("All terminal sessions in this window will be terminated."), + .surface => i18n._("The currently running process in this split will be terminated."), + }; + } + + pub const getGObjectType = gobject.ext.defineEnum( + Target, + .{ .name = "GhosttyCloseConfirmationDialogTarget" }, + ); +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/command_palette.zig b/vendor/libghostty-vt/src/apprt/gtk/class/command_palette.zig new file mode 100644 index 00000000..6101e82e --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/command_palette.zig @@ -0,0 +1,789 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; + +const adw = @import("adw"); +const gio = @import("gio"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const input = @import("../../../input.zig"); +const gresource = @import("../build/gresource.zig"); +const key = @import("../key.zig"); +const WeakRef = @import("../weak_ref.zig").WeakRef; +const Common = @import("../class.zig").Common; +const Application = @import("application.zig").Application; +const Window = @import("window.zig").Window; +const Surface = @import("surface.zig").Surface; +const Tab = @import("tab.zig").Tab; +const Config = @import("config.zig").Config; + +const log = std.log.scoped(.gtk_ghostty_command_palette); + +pub const CommandPalette = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyCommandPalette", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Config, + .{ + .accessor = C.privateObjFieldAccessor("config"), + }, + ); + }; + }; + + pub const signals = struct { + /// Emitted when a command from the command palette is activated. The + /// action contains pointers to allocated data so if a receiver of this + /// signal needs to keep the action around it will need to clone the + /// action or there may be use-after-free errors. + pub const trigger = struct { + pub const name = "trigger"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{*const input.Binding.Action}, + void, + ); + }; + }; + + const Private = struct { + /// The configuration that this command palette is using. + config: ?*Config = null, + + /// The dialog object containing the palette UI. + dialog: *adw.Dialog, + + /// The search input text field. + search: *gtk.SearchEntry, + + /// The view containing each result row. + view: *gtk.ListView, + + /// The model that provides filtered data for the view to display. + model: *gtk.SingleSelection, + + /// The list that serves as the data source of the model. + /// This is where all command data is ultimately stored. + source: *gio.ListStore, + + pub var offset: c_int = 0; + }; + + /// Create a new instance of the command palette. The caller will own a + /// reference to the object. + pub fn new() *Self { + const self = gobject.ext.newInstance(Self, .{}); + + // Sink ourselves so that we aren't floating anymore. We'll unref + // ourselves when the palette is closed or an action is activated. + _ = self.refSink(); + + // Bump the ref so that the caller has a reference. + return self.ref(); + } + + //--------------------------------------------------------------- + // Virtual Methods + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + // Listen for any changes to our config. + _ = gobject.Object.signals.notify.connect( + self, + ?*anyopaque, + propConfig, + null, + .{ + .detail = "config", + }, + ); + } + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + + priv.source.removeAll(); + + if (priv.config) |config| { + config.unref(); + priv.config = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + // Signal Handlers + + fn propConfig(self: *CommandPalette, _: *gobject.ParamSpec, _: ?*anyopaque) callconv(.c) void { + const priv = self.private(); + + const config = priv.config orelse { + log.warn("command palette does not have a config!", .{}); + return; + }; + + // Clear existing binds + priv.source.removeAll(); + + const alloc = Application.default().allocator(); + var commands: std.ArrayList(*Command) = .{}; + defer { + for (commands.items) |cmd| cmd.unref(); + commands.deinit(alloc); + } + + self.collectJumpCommands(config, &commands) catch |err| { + log.warn("failed to collect jump commands: {}", .{err}); + }; + + self.collectRegularCommands(config, &commands, alloc); + + // Sort commands + std.mem.sort(*Command, commands.items, {}, struct { + fn lessThan(_: void, a: *Command, b: *Command) bool { + return compareCommands(a, b); + } + }.lessThan); + + for (commands.items) |cmd| { + const cmd_ref = cmd.as(gobject.Object); + priv.source.append(cmd_ref); + } + } + + /// Collect regular commands from configuration, filtering out unsupported actions. + fn collectRegularCommands( + self: *CommandPalette, + config: *Config, + commands: *std.ArrayList(*Command), + alloc: std.mem.Allocator, + ) void { + _ = self; + const cfg = config.get(); + + for (cfg.@"command-palette-entry".value.items) |command| { + // Filter out actions that are not implemented or don't make sense + // for GTK. + if (!isActionSupportedOnGtk(command.action)) continue; + + const cmd = Command.new(config, command) catch |err| { + log.warn("failed to create command: {}", .{err}); + continue; + }; + errdefer cmd.unref(); + + commands.append(alloc, cmd) catch |err| { + log.warn("failed to add command to list: {}", .{err}); + continue; + }; + } + } + + /// Check if an action is supported on GTK. + fn isActionSupportedOnGtk(action: input.Binding.Action) bool { + return switch (action) { + .close_all_windows, + .toggle_secure_input, + .check_for_updates, + .redo, + .undo, + .reset_window_size, + .toggle_window_float_on_top, + => false, + + else => true, + }; + } + + /// Collect jump commands for all surfaces across all windows. + fn collectJumpCommands( + self: *CommandPalette, + config: *Config, + commands: *std.ArrayList(*Command), + ) !void { + _ = self; + const app = Application.default(); + const alloc = app.allocator(); + + // Get all surfaces from the core app + const core_app = app.core(); + for (core_app.surfaces.items) |apprt_surface| { + const surface = apprt_surface.gobj(); + const cmd = Command.newJump(config, surface); + errdefer cmd.unref(); + try commands.append(alloc, cmd); + } + } + + /// Compare two commands for sorting. + /// Sorts alphabetically by title (case-insensitive), with colon normalization + /// so "Foo:" sorts before "Foo Bar:". Uses sort_key as tie-breaker. + fn compareCommands(a: *Command, b: *Command) bool { + const a_title = a.propGetTitle() orelse return false; + const b_title = b.propGetTitle() orelse return true; + + // Compare case-insensitively with colon normalization + for (0..@min(a_title.len, b_title.len)) |i| { + // Get characters, replacing ':' with '\t' + const a_char = if (a_title[i] == ':') '\t' else a_title[i]; + const b_char = if (b_title[i] == ':') '\t' else b_title[i]; + + const a_lower = std.ascii.toLower(a_char); + const b_lower = std.ascii.toLower(b_char); + + if (a_lower != b_lower) { + return a_lower < b_lower; + } + } + + // If one title is a prefix of the other, shorter one comes first + if (a_title.len != b_title.len) { + return a_title.len < b_title.len; + } + + // Titles are equal - use sort_key as tie-breaker if both are jump commands + const a_sort_key = switch (a.private().data) { + .regular => return false, + .jump => |*ja| ja.sort_key, + }; + const b_sort_key = switch (b.private().data) { + .regular => return false, + .jump => |*jb| jb.sort_key, + }; + + return a_sort_key < b_sort_key; + } + + fn close(self: *CommandPalette) void { + const priv = self.private(); + _ = priv.dialog.close(); + } + + fn dialogClosed(_: *adw.Dialog, self: *CommandPalette) callconv(.c) void { + self.unref(); + } + + fn searchStopped(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void { + // ESC was pressed - close the palette + self.close(); + } + + fn searchActivated(_: *gtk.SearchEntry, self: *CommandPalette) callconv(.c) void { + // If Enter is pressed, activate the selected entry + const priv = self.private(); + self.activated(priv.model.getSelected()); + } + + fn rowActivated(_: *gtk.ListView, pos: c_uint, self: *CommandPalette) callconv(.c) void { + self.activated(pos); + } + + //--------------------------------------------------------------- + + /// Show or hide the command palette dialog. If the dialog is shown it will + /// be modal over the given window. + pub fn toggle(self: *CommandPalette, window: *Window) void { + const priv = self.private(); + + // If the dialog has been shown, close it. + if (priv.dialog.as(gtk.Widget).getRealized() != 0) { + self.close(); + return; + } + + // Show the dialog + priv.dialog.present(window.as(gtk.Widget)); + + // Focus on the search bar when opening the dialog + _ = priv.search.as(gtk.Widget).grabFocus(); + } + + /// Helper function to send a signal containing the action that should be + /// performed. + fn activated(self: *CommandPalette, pos: c_uint) void { + const priv = self.private(); + + // Use priv.model and not priv.source here to use the list of *visible* results + const object_ = priv.model.as(gio.ListModel).getObject(pos); + defer if (object_) |object| object.unref(); + + // Close before running the action in order to avoid being replaced by + // another dialog (such as the change title dialog). If that occurs then + // the command palette dialog won't be counted as having closed properly + // and cannot receive focus when reopened. + self.close(); + + const cmd = gobject.ext.cast(Command, object_ orelse return) orelse return; + + // Handle jump commands differently + if (cmd.isJump()) { + const surface = cmd.getJumpSurface() orelse return; + defer surface.unref(); + surface.present(); + return; + } + + // Regular command - emit trigger signal + const action = cmd.getAction() orelse return; + + // Signal that an action has been selected. Signals are synchronous + // so we shouldn't need to worry about cloning the action. + signals.trigger.impl.emit( + self, + null, + .{&action}, + null, + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const refSink = C.refSink; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(Command); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "command-palette", + }), + ); + + // Bindings + class.bindTemplateChildPrivate("dialog", .{}); + class.bindTemplateChildPrivate("search", .{}); + class.bindTemplateChildPrivate("view", .{}); + class.bindTemplateChildPrivate("model", .{}); + class.bindTemplateChildPrivate("source", .{}); + + // Template Callbacks + class.bindTemplateCallback("closed", &dialogClosed); + class.bindTemplateCallback("notify_config", &propConfig); + class.bindTemplateCallback("search_stopped", &searchStopped); + class.bindTemplateCallback("search_activated", &searchActivated); + class.bindTemplateCallback("row_activated", &rowActivated); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.config.impl, + }); + + // Signals + signals.trigger.impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; + +/// Object that wraps around a command. +/// +/// As GTK list models only accept objects that are within the GObject hierarchy, +/// we have to construct a wrapper to be easily consumed by the list model. +const Command = extern struct { + pub const Self = @This(); + pub const Parent = gobject.Object; + parent: Parent, + + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyCommand", + .instanceInit = &init, + .classInit = Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + const properties = struct { + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Config, + .{ + .accessor = C.privateObjFieldAccessor("config"), + }, + ); + }; + + pub const action_key = struct { + pub const name = "action-key"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = gobject.ext.typedAccessor( + Self, + ?[:0]const u8, + .{ + .getter = propGetActionKey, + .getter_transfer = .none, + }, + ), + }, + ); + }; + + pub const action = struct { + pub const name = "action"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = gobject.ext.typedAccessor( + Self, + ?[:0]const u8, + .{ + .getter = propGetAction, + .getter_transfer = .none, + }, + ), + }, + ); + }; + + pub const title = struct { + pub const name = "title"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = gobject.ext.typedAccessor( + Self, + ?[:0]const u8, + .{ + .getter = propGetTitle, + .getter_transfer = .none, + }, + ), + }, + ); + }; + + pub const description = struct { + pub const name = "description"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = gobject.ext.typedAccessor( + Self, + ?[:0]const u8, + .{ + .getter = propGetDescription, + .getter_transfer = .none, + }, + ), + }, + ); + }; + }; + + pub const Private = struct { + config: ?*Config = null, + arena: ArenaAllocator, + data: CommandData, + + pub var offset: c_int = 0; + + pub const CommandData = union(enum) { + regular: RegularData, + jump: JumpData, + }; + + pub const RegularData = struct { + command: input.Command, + action: ?[:0]const u8 = null, + action_key: ?[:0]const u8 = null, + }; + + pub const JumpData = struct { + surface: WeakRef(Surface) = .empty, + title: ?[:0]const u8 = null, + description: ?[:0]const u8 = null, + sort_key: usize, + }; + }; + + pub fn new(config: *Config, command: input.Command) Allocator.Error!*Self { + const self = gobject.ext.newInstance(Self, .{ + .config = config, + }); + errdefer self.unref(); + + const priv = self.private(); + const cloned = try command.clone(priv.arena.allocator()); + + priv.data = .{ + .regular = .{ + .command = cloned, + }, + }; + + return self; + } + + /// Create a new jump command that focuses a specific surface. + pub fn newJump(config: *Config, surface: *Surface) *Self { + const self = gobject.ext.newInstance(Self, .{ + .config = config, + }); + + const priv = self.private(); + priv.data = .{ + .jump = .{ + // TODO: Replace with surface id whenever Ghostty adds one + .sort_key = @intFromPtr(surface), + }, + }; + priv.data.jump.surface.set(surface); + + return self; + } + + fn init(self: *Self, _: *Class) callconv(.c) void { + // NOTE: we do not watch for changes to the config here as the command + // palette will destroy and recreate this object if/when the config + // changes. + + const priv = self.private(); + priv.arena = .init(Application.default().allocator()); + } + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + + if (priv.config) |config| { + config.unref(); + priv.config = null; + } + + switch (priv.data) { + .regular => {}, + .jump => |*j| { + j.surface.set(null); + }, + } + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + + priv.arena.deinit(); + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + + fn propGetActionKey(self: *Self) ?[:0]const u8 { + const priv = self.private(); + + const regular = switch (priv.data) { + .regular => |*r| r, + .jump => return null, + }; + + if (regular.action_key) |action_key| return action_key; + + regular.action_key = std.fmt.allocPrintSentinel( + priv.arena.allocator(), + "{f}", + .{regular.command.action}, + 0, + ) catch null; + + return regular.action_key; + } + + fn propGetAction(self: *Self) ?[:0]const u8 { + const priv = self.private(); + + const regular = switch (priv.data) { + .regular => |*r| r, + .jump => return null, + }; + + if (regular.action) |action| return action; + + const cfg = if (priv.config) |config| config.get() else return null; + const keybinds = cfg.keybind.set; + + const alloc = priv.arena.allocator(); + + regular.action = action: { + var buf: [64]u8 = undefined; + const trigger = keybinds.getTrigger(regular.command.action) orelse break :action null; + const accel = (key.accelFromTrigger(&buf, trigger) catch break :action null) orelse break :action null; + break :action alloc.dupeZ(u8, accel) catch return null; + }; + + return regular.action; + } + + fn propGetTitle(self: *Self) ?[:0]const u8 { + const priv = self.private(); + + switch (priv.data) { + .regular => |*r| return r.command.title, + .jump => |*j| { + if (j.title) |title| return title; + + const surface = j.surface.get() orelse return null; + defer surface.unref(); + + const alloc = priv.arena.allocator(); + const effective_title = surface.getEffectiveTitle() orelse "Untitled"; + + j.title = std.fmt.allocPrintSentinel( + alloc, + "Focus: {s}", + .{effective_title}, + 0, + ) catch null; + + return j.title; + }, + } + } + + fn propGetDescription(self: *Self) ?[:0]const u8 { + const priv = self.private(); + + switch (priv.data) { + .regular => |*r| return r.command.description, + .jump => |*j| { + if (j.description) |desc| return desc; + + const surface = j.surface.get() orelse return null; + defer surface.unref(); + + const alloc = priv.arena.allocator(); + const title = surface.getEffectiveTitle() orelse "Untitled"; + const pwd = surface.getPwd(); + + if (pwd) |p| { + if (std.mem.indexOf(u8, title, p) == null) { + j.description = alloc.dupeZ(u8, p) catch null; + } + } + + return j.description; + }, + } + } + + //--------------------------------------------------------------- + + /// Return a copy of the action. Callers must ensure that they do not use + /// the action beyond the lifetime of this object because it has internally + /// allocated data that will be freed when this object is. + pub fn getAction(self: *Self) ?input.Binding.Action { + const priv = self.private(); + return switch (priv.data) { + .regular => |*r| r.command.action, + .jump => null, + }; + } + + /// Check if this is a jump command. + pub fn isJump(self: *Self) bool { + const priv = self.private(); + return priv.data == .jump; + } + + /// Get the jump surface. Returns a strong reference that the caller + /// must unref when done, or null if the surface has been destroyed. + pub fn getJumpSurface(self: *Self) ?*Surface { + const priv = self.private(); + return switch (priv.data) { + .regular => null, + .jump => |*j| j.surface.get(), + }; + } + + //--------------------------------------------------------------- + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.registerProperties(class, &.{ + properties.config.impl, + properties.action_key.impl, + properties.action.impl, + properties.title.impl, + properties.description.impl, + }); + + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/config.zig b/vendor/libghostty-vt/src/apprt/gtk/class/config.zig new file mode 100644 index 00000000..9a705d35 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/config.zig @@ -0,0 +1,175 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const configpkg = @import("../../../config.zig"); +const CoreConfig = configpkg.Config; + +const Common = @import("../class.zig").Common; + +const log = std.log.scoped(.gtk_ghostty_config); + +/// Wraps a `Ghostty.Config` object in a GObject so it can be reference +/// counted. When this object is freed, the underlying config is also freed. +/// +/// It is highly recommended to NOT take a reference to this object, +/// since configuration takes up a lot of memory (relatively). Instead, +/// receivers of this should usually create a `DerivedConfig` struct from +/// this, copy any memory they require, and own that structure instead. +/// +/// This can also expose helpers to access configuration in ways that +/// may be more ergonomic to GTK primitives. +pub const Config = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = gobject.Object; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyConfig", + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const @"diagnostics-buffer" = gobject.ext.defineProperty( + "diagnostics-buffer", + Self, + ?*gtk.TextBuffer, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*gtk.TextBuffer, + .{ + .getter = Self.diagnosticsBuffer, + .getter_transfer = .full, + }, + ), + }, + ); + + pub const @"has-diagnostics" = gobject.ext.defineProperty( + "has-diagnostics", + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ + .getter = Self.hasDiagnostics, + }, + ), + }, + ); + }; + + const Private = struct { + config: CoreConfig, + + pub var offset: c_int = 0; + }; + + /// Create a new GhosttyConfig from a loaded configuration. + /// + /// This clones the given configuration, so it is safe for the + /// caller to free the original configuration after this call. + pub fn new(alloc: Allocator, config: *const CoreConfig) Allocator.Error!*Self { + const self = gobject.ext.newInstance(Self, .{}); + errdefer self.unref(); + + const priv = self.private(); + priv.config = try config.clone(alloc); + + return self; + } + + /// Get the wrapped configuration. It's unsafe to store this or access + /// it in any way that may live beyond the lifetime of this object. + pub fn get(self: *Self) *const CoreConfig { + return &self.private().config; + } + + /// Get the mutable configuration. This is usually NOT recommended + /// because any changes to the config won't be propagated to anyone + /// with a reference to this object. If you know what you're doing, then + /// you can use this. + pub fn getMut(self: *Self) *CoreConfig { + return &self.private().config; + } + + /// Returns whether this configuration has any diagnostics. + pub fn hasDiagnostics(self: *Self) bool { + const config = self.get(); + return !config._diagnostics.empty(); + } + + /// Reads the diagnostics of this configuration as a TextBuffer, + /// or returns null if there are no diagnostics. + pub fn diagnosticsBuffer(self: *Self) ?*gtk.TextBuffer { + const config = self.get(); + if (config._diagnostics.empty()) return null; + + const text_buf: *gtk.TextBuffer = .new(null); + errdefer text_buf.unref(); + + var buf: [4095:0]u8 = undefined; + var writer: std.Io.Writer = .fixed(&buf); + for (config._diagnostics.items()) |diag| { + writer.end = 0; + diag.format(&writer) catch |err| { + log.warn( + "error writing diagnostic to buffer err={}", + .{err}, + ); + continue; + }; + + text_buf.insertAtCursor(&buf, @intCast(writer.end)); + text_buf.insertAtCursor("\n", 1); + } + + return text_buf; + } + + fn finalize(self: *Self) callconv(.c) void { + self.private().config.deinit(); + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + gobject.ext.registerProperties(class, &.{ + properties.@"diagnostics-buffer", + properties.@"has-diagnostics", + }); + } + }; +}; + +// This test verifies our memory management works as expected. Since +// we use the testing allocator any leaks are detected. +test "GhosttyConfig" { + const testing = std.testing; + const alloc = testing.allocator; + var config: CoreConfig = try .default(alloc); + defer config.deinit(); + const obj: *Config = try .new(alloc, &config); + obj.unref(); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/config_errors_dialog.zig b/vendor/libghostty-vt/src/apprt/gtk/class/config_errors_dialog.zig new file mode 100644 index 00000000..46d5fe62 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/config_errors_dialog.zig @@ -0,0 +1,161 @@ +const std = @import("std"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); +const Common = @import("../class.zig").Common; +const Config = @import("config.zig").Config; +const Dialog = @import("dialog.zig").Dialog; + +const log = std.log.scoped(.gtk_ghostty_config_errors_dialog); + +pub const ConfigErrorsDialog = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = Dialog; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyConfigErrorsDialog", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const config = gobject.ext.defineProperty( + "config", + Self, + ?*Config, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*Config, + .{ + .getter = Self.getConfig, + .setter = Self.setConfig, + }, + ), + }, + ); + }; + + pub const signals = struct { + pub const @"reload-config" = struct { + pub const name = "reload-config"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + + const Private = struct { + config: ?*Config, + pub var offset: c_int = 0; + }; + + pub fn new(config: *Config) *Self { + return gobject.ext.newInstance(Self, .{ + .config = config, + }); + } + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + pub fn present(self: *Self, parent: ?*gtk.Widget) void { + self.as(Dialog).present(parent); + } + + pub fn close(self: *Self) void { + self.as(Dialog).close(); + } + + fn response( + self: *Self, + response_id: [*:0]const u8, + ) callconv(.c) void { + if (std.mem.orderZ(u8, response_id, "reload") != .eq) return; + signals.@"reload-config".impl.emit( + self, + null, + .{}, + null, + ); + } + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.config) |v| { + v.unref(); + priv.config = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn getConfig(self: *Self) ?*Config { + return self.private().config; + } + + fn setConfig(self: *Self, config: ?*Config) void { + const priv = self.private(); + if (priv.config) |old| old.unref(); + + // We don't need to increase the reference count because + // the property setter handles it (uses GValue.get vs. take) + priv.config = config; + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(Dialog); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 2, + .name = "config-errors-dialog", + }), + ); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.config, + }); + + // Signals + signals.@"reload-config".impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + Dialog.virtual_methods.response.implement(class, &response); + } + + pub fn as(class: *Class, comptime T: type) *T { + return gobject.ext.as(T, class); + } + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/debug_warning.zig b/vendor/libghostty-vt/src/apprt/gtk/class/debug_warning.zig new file mode 100644 index 00000000..0ad32033 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/debug_warning.zig @@ -0,0 +1,50 @@ +const adw = @import("adw"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const adw_version = @import("../adw_version.zig"); +const gresource = @import("../build/gresource.zig"); +const Common = @import("../class.zig").Common; + +/// Debug warning banner. It will be based on adw.Banner if we're using Adwaita +/// 1.3 or newer. Otherwise it will use a gtk.Label. +pub const DebugWarning = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = if (adw_version.supportsBanner()) adw.Bin else gtk.Box; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyDebugWarning", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + }); + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + const C = Common(Self, null); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = if (adw_version.supportsBanner()) 3 else 2, + .name = "debug-warning", + }), + ); + } + + pub const as = C.Class.as; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/dialog.zig b/vendor/libghostty-vt/src/apprt/gtk/class/dialog.zig new file mode 100644 index 00000000..5bc3cdfa --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/dialog.zig @@ -0,0 +1,91 @@ +const std = @import("std"); +const adw = @import("adw"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const adw_version = @import("../adw_version.zig"); +const Common = @import("../class.zig").Common; + +const log = std.log.scoped(.gtk_ghostty_dialog); + +/// Dialog is a simple abstraction over the `adw.AlertDialog` and +/// `adw.MessageDialog` widgets, chosen at comptime based on the linked +/// Adwaita version. +/// +/// Once we drop support for Adwaita <= 1.2, this can be fully removed +/// and we can use `adw.AlertDialog` directly. +pub const Dialog = extern struct { + const Self = @This(); + parent_instance: Parent, + + pub const Parent = if (adw_version.supportsDialogs()) + adw.AlertDialog + else + adw.MessageDialog; + + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyDialog", + .classInit = &Class.init, + .parent_class = &Class.parent, + }); + + pub const virtual_methods = struct { + /// Forwarded from parent so subclasses can reference this + /// directly. This will make it easier to remove Dialog in the future. + pub const response = Parent.virtual_methods.response; + }; + + pub fn present(self: *Self, parent: ?*gtk.Widget) void { + switch (Parent) { + adw.AlertDialog => self.as(adw.Dialog).present(parent), + + adw.MessageDialog => { + // Reset the previous parent window + self.as(gtk.Window).setTransientFor(null); + + // We need to get the window for the parent in order + // to set the transient-for property on the MessageDialog. + if (parent) |widget| parent: { + const root = gtk.Widget.getRoot(widget) orelse break :parent; + const window = gobject.ext.cast( + gtk.Window, + root, + ) orelse break :parent; + self.as(gtk.Window).setTransientFor(window); + } + + self.as(gtk.Window).present(); + }, + + else => comptime unreachable, + } + } + + pub fn close(self: *Self) void { + switch (Parent) { + adw.AlertDialog => self.as(adw.Dialog).forceClose(), + adw.MessageDialog => self.as(gtk.Window).close(), + else => comptime unreachable, + } + } + + const C = Common(Self, null); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + _ = class; + } + + pub fn as(class: *Class, comptime T: type) *T { + return gobject.ext.as(T, class); + } + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/global_shortcuts.zig b/vendor/libghostty-vt/src/apprt/gtk/class/global_shortcuts.zig new file mode 100644 index 00000000..cf0f31a6 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/global_shortcuts.zig @@ -0,0 +1,633 @@ +const std = @import("std"); +const assert = @import("../../../quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); + +const Binding = @import("../../../input.zig").Binding; +const key = @import("../key.zig"); +const Common = @import("../class.zig").Common; +const Application = @import("application.zig").Application; +const Config = @import("config.zig").Config; + +const log = std.log.scoped(.gtk_ghostty_global_shortcuts); + +pub const GlobalShortcuts = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = gobject.Object; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyGlobalShortcuts", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Config, + .{ + .accessor = C.privateObjFieldAccessor("config"), + }, + ); + }; + + pub const @"dbus-connection" = struct { + pub const name = "dbus-connection"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*gio.DBusConnection, + .{ + .accessor = C.privateObjFieldAccessor("dbus_connection"), + }, + ); + }; + }; + + const Private = struct { + /// The configuration that this is using. + config: ?*Config = null, + + /// The dbus connection. + dbus_connection: ?*gio.DBusConnection = null, + + /// An arena allocator that is present for each refresh. + arena: ?std.heap.ArenaAllocator = null, + + /// A mapping from a unique ID to an action. + /// Currently the unique ID is simply the serialized representation of the + /// trigger that was used for the action as triggers are unique in the keymap, + /// but this may change in the future. + map: std.StringArrayHashMapUnmanaged(Binding.Action) = .{}, + + /// The handle of the current global shortcuts portal session, + /// as a D-Bus object path. + handle: ?[:0]const u8 = null, + + /// The D-Bus signal subscription for the response signal on requests. + /// The ID is guaranteed to be non-zero, so we can use 0 to indicate null. + response_subscription: c_uint = 0, + + /// The D-Bus signal subscription for the keybind activate signal. + /// The ID is guaranteed to be non-zero, so we can use 0 to indicate null. + activate_subscription: c_uint = 0, + + pub var offset: c_int = 0; + }; + + pub const signals = struct { + /// Emitted whenever a global shortcut is triggered. + pub const trigger = struct { + pub const name = "trigger"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{*const Binding.Action}, + void, + ); + }; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + _ = gobject.Object.signals.notify.connect( + self, + *Self, + propConfig, + self, + .{ .detail = "config" }, + ); + } + + fn close(self: *Self) void { + const priv = self.private(); + const dbus = priv.dbus_connection orelse return; + + if (priv.response_subscription != 0) { + dbus.signalUnsubscribe(priv.response_subscription); + priv.response_subscription = 0; + } + + if (priv.activate_subscription != 0) { + dbus.signalUnsubscribe(priv.activate_subscription); + priv.activate_subscription = 0; + } + + if (priv.handle) |handle| { + // Close existing session + dbus.call( + "org.freedesktop.portal.Desktop", + handle, + "org.freedesktop.portal.Session", + "Close", + null, + null, + .{}, + -1, + null, + null, + null, + ); + priv.handle = null; + } + + if (priv.arena) |*arena| { + arena.deinit(); + priv.arena = null; + priv.map = .{}; // Uses arena memory + } + } + + fn refresh(self: *Self) Allocator.Error!void { + // Always close our previous state first. + self.close(); + + const priv = self.private(); + + // We need configuration to proceed. + const config = if (priv.config) |v| v.get() else return; + + // Setup our new arena that we'll use for memory allocations. + assert(priv.arena == null); + var arena: std.heap.ArenaAllocator = .init(Application.default().allocator()); + errdefer arena.deinit(); + const alloc = arena.allocator(); + + // Our map starts out empty again. We don't need to worry about + // memory because its part of the arena we clear. + priv.map = .{}; + errdefer priv.map = .{}; + + // Update map + var trigger_buf: [1024]u8 = undefined; + var it = config.keybind.set.bindings.iterator(); + while (it.next()) |entry| { + const leaf: Binding.Set.GenericLeaf = switch (entry.value_ptr.*) { + .leader => continue, + inline .leaf, .leaf_chained => |leaf| leaf.generic(), + }; + if (!leaf.flags.global) continue; + + // We only allow global keybinds that map to exactly a single + // action for now. TODO: remove this restriction + const actions = leaf.actionsSlice(); + if (actions.len != 1) continue; + + const trigger = if (key.xdgShortcutFromTrigger( + &trigger_buf, + entry.key_ptr.*, + )) |shortcut_| + shortcut_ orelse continue + else |err| switch (err) { + // If there isn't space to translate the trigger, then our + // buffer might be too small (but 1024 is insane!). In any case + // we don't want to stop registering globals. + error.WriteFailed => { + log.warn( + "buffer too small to translate trigger, ignoring={f}", + .{entry.key_ptr.*}, + ); + continue; + }, + }; + + try priv.map.put( + alloc, + try alloc.dupeZ(u8, trigger), + actions[0], + ); + } + + // Store our arena + priv.arena = arena; + + // Create our session if we have global shortcuts. + if (priv.map.count() > 0) try self.request(.create_session); + } + + const Method = enum { + create_session, + bind_shortcuts, + + fn name(self: Method) [:0]const u8 { + return switch (self) { + .create_session => "CreateSession", + .bind_shortcuts => "BindShortcuts", + }; + } + + /// Construct the payload expected by the XDG portal call. + fn makePayload( + self: Method, + shortcuts: *GlobalShortcuts, + request_token: [:0]const u8, + ) ?*glib.Variant { + switch (self) { + // See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.GlobalShortcuts.html#org-freedesktop-portal-globalshortcuts-createsession + .create_session => { + var session_token: Token = undefined; + return glib.Variant.newParsed( + "({'handle_token': <%s>, 'session_handle_token': <%s>},)", + request_token.ptr, + generateToken(&session_token).ptr, + ); + }, + + // See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.GlobalShortcuts.html#org-freedesktop-portal-globalshortcuts-bindshortcuts + .bind_shortcuts => { + const priv = shortcuts.private(); + const handle = priv.handle orelse return null; + + const bind_type = glib.VariantType.new("a(sa{sv})"); + defer glib.free(bind_type); + + var binds: glib.VariantBuilder = undefined; + glib.VariantBuilder.init(&binds, bind_type); + + var action_buf: [256]u8 = undefined; + + var it = priv.map.iterator(); + while (it.next()) |entry| { + const trigger = entry.key_ptr.*.ptr; + const action = std.fmt.bufPrintZ( + &action_buf, + "{f}", + .{entry.value_ptr.*}, + ) catch continue; + + binds.addParsed( + "(%s, {'description': <%s>, 'preferred_trigger': <%s>})", + trigger, + action.ptr, + trigger, + ); + } + + return glib.Variant.newParsed( + "(%o, %*, '', {'handle_token': <%s>})", + handle.ptr, + binds.end(), + request_token.ptr, + ); + }, + } + } + + fn onResponse(self: Method, shortcuts: *GlobalShortcuts, vardict: *glib.Variant) void { + switch (self) { + .create_session => { + var handle: ?[*:0]u8 = null; + if (vardict.lookup("session_handle", "&s", &handle) == 0) { + log.warn( + "session handle not found in response={s}", + .{vardict.print(@intFromBool(true))}, + ); + return; + } + + const priv = shortcuts.private(); + const dbus = priv.dbus_connection.?; + const alloc = priv.arena.?.allocator(); + priv.handle = alloc.dupeZ(u8, std.mem.span(handle.?)) catch { + log.warn("out of memory: failed to clone session handle", .{}); + return; + }; + log.debug("session_handle={?s}", .{handle}); + + // Subscribe to keybind activations + assert(priv.activate_subscription == 0); + priv.activate_subscription = dbus.signalSubscribe( + null, + "org.freedesktop.portal.GlobalShortcuts", + "Activated", + "/org/freedesktop/portal/desktop", + handle, + .{ .match_arg0_path = true }, + shortcutActivated, + shortcuts, + null, + ); + + shortcuts.request(.bind_shortcuts) catch |err| { + log.warn("failed to bind shortcuts={}", .{err}); + return; + }; + }, + .bind_shortcuts => {}, + } + } + }; + + /// Submit a request to the global shortcuts portal. + fn request( + self: *Self, + comptime method: Method, + ) Allocator.Error!void { + // NOTE(pluiedev): + // XDG Portals are really, really poorly-designed pieces of hot garbage. + // How the protocol is _initially_ designed to work is as follows: + // + // 1. The client calls a method which returns the path of a Request object; + // 2. The client waits for the Response signal under said object path; + // 3. When the signal arrives, the actual return value and status code + // become available for the client for further processing. + // + // THIS DOES NOT WORK. Once the first two steps are complete, the client + // needs to immediately start listening for the third step, but an overeager + // server implementation could easily send the Response signal before the + // client is even ready, causing communications to break down over a simple + // race condition/two generals' problem that even _TCP_ had figured out + // decades ago. Worse yet, you get exactly _one_ chance to listen for the + // signal, or else your communication attempt so far has all been in vain. + // + // And they know this. Instead of fixing their freaking protocol, they just + // ask clients to manually construct the expected object path and subscribe + // to the request signal beforehand, making the whole response value of + // the original call COMPLETELY MEANINGLESS. + // + // Furthermore, this is _entirely undocumented_ aside from one tiny + // paragraph under the documentation for the Request interface, and + // anyone would be forgiven for missing it without reading the libportal + // source code. + // + // When in Rome, do as the Romans do, I guess...? + + const callbacks = struct { + fn gotResponseHandle( + source: ?*gobject.Object, + res: *gio.AsyncResult, + _: ?*anyopaque, + ) callconv(.c) void { + const dbus_ = gobject.ext.cast(gio.DBusConnection, source.?).?; + + var err: ?*glib.Error = null; + defer if (err) |err_| err_.free(); + + const params_ = dbus_.callFinish(res, &err) orelse { + if (err) |err_| log.warn("request failed={s} ({})", .{ + err_.f_message orelse "(unknown)", + err_.f_code, + }); + return; + }; + defer params_.unref(); + + // TODO: XDG recommends updating the signal subscription if the actual + // returned request path is not the same as the expected request + // path, to retain compatibility with older versions of XDG portals. + // Although it suffers from the race condition outlined above, + // we should still implement this at some point. + } + + // See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html#org-freedesktop-portal-request-response + fn responded( + dbus: *gio.DBusConnection, + _: ?[*:0]const u8, + _: [*:0]const u8, + _: [*:0]const u8, + _: [*:0]const u8, + params_: *glib.Variant, + ud: ?*anyopaque, + ) callconv(.c) void { + const self_cb: *GlobalShortcuts = @ptrCast(@alignCast(ud)); + const priv = self_cb.private(); + + // Unsubscribe from the response signal + if (priv.response_subscription != 0) { + dbus.signalUnsubscribe(priv.response_subscription); + priv.response_subscription = 0; + } + + var response: u32 = 0; + var vardict: ?*glib.Variant = null; + defer if (vardict) |v| v.unref(); + params_.get("(u@a{sv})", &response, &vardict); + + switch (response) { + 0 => { + log.debug("request successful", .{}); + method.onResponse(self_cb, vardict.?); + }, + 1 => log.debug("request was cancelled by user", .{}), + 2 => log.warn("request ended unexpectedly", .{}), + else => log.warn("unrecognized response code={}", .{response}), + } + } + }; + + var request_token_buf: Token = undefined; + const request_token = generateToken(&request_token_buf); + + const payload = method.makePayload(self, request_token) orelse return; + const request_path = try self.getRequestPath(request_token); + + const priv = self.private(); + const dbus = priv.dbus_connection.?; + + assert(priv.response_subscription == 0); + priv.response_subscription = dbus.signalSubscribe( + null, + "org.freedesktop.portal.Request", + "Response", + request_path, + null, + .{}, + callbacks.responded, + self, + null, + ); + + dbus.call( + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.GlobalShortcuts", + method.name(), + payload, + null, + .{}, + -1, + null, + callbacks.gotResponseHandle, + null, + ); + } + + /// Get the XDG portal request path for the current Ghostty instance. + /// + /// If this sounds like nonsense, see `request` for an explanation as to + /// why we need to do this. + /// + /// Precondition: dbus connection exists, arena setup + fn getRequestPath(self: *Self, token: [:0]const u8) Allocator.Error![:0]const u8 { + const priv = self.private(); + const dbus = priv.dbus_connection.?; + const alloc = priv.arena.?.allocator(); + + // See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html + // for the syntax XDG portals expect. + + // `getUniqueName` should never return null here as we're using an ordinary + // message bus connection. If it doesn't, something is very wrong + const unique_name = std.mem.span(dbus.getUniqueName().?); + + const object_path = try std.mem.joinZ( + alloc, + "/", + &.{ + "/org/freedesktop/portal/desktop/request", + unique_name[1..], // Remove leading `:` + token, + }, + ); + + // Sanitize the unique name by replacing every `.` with `_`. + // In effect, this will turn a unique name like `:1.192` into `1_192`. + // Valid D-Bus object path components never contain `.`s anyway, so we're + // free to replace all instances of `.` here and avoid extra allocation. + std.mem.replaceScalar(u8, object_path, '.', '_'); + + return object_path; + } + + //--------------------------------------------------------------- + // Property Handlers + + pub fn setDbusConnection( + self: *Self, + dbus_connection: ?*gio.DBusConnection, + ) void { + const priv = self.private(); + + // If we have a prior dbus connection we need to close our prior + // registrations first. + if (priv.dbus_connection) |v| { + self.close(); + v.unref(); + priv.dbus_connection = null; + } + + priv.dbus_connection = null; + if (dbus_connection) |v| { + v.ref(); // Weird this doesn't return self + priv.dbus_connection = v; + self.refresh() catch |err| { + log.warn("error refreshing global shortcuts: {}", .{err}); + }; + } + + self.as(gobject.Object).notifyByPspec(properties.@"dbus-connection".impl.param_spec); + } + + fn propConfig( + _: *Self, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.refresh() catch |err| { + log.warn("error refreshing global shortcuts: {}", .{err}); + }; + } + + //--------------------------------------------------------------- + // Signal Handlers + + fn shortcutActivated( + _: *gio.DBusConnection, + _: ?[*:0]const u8, + _: [*:0]const u8, + _: [*:0]const u8, + _: [*:0]const u8, + params: *glib.Variant, + ud: ?*anyopaque, + ) callconv(.c) void { + const self: *Self = @ptrCast(@alignCast(ud)); + + // 2nd value in the tuple is the activated shortcut ID + // See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.GlobalShortcuts.html#org-freedesktop-portal-globalshortcuts-activated + var shortcut_id: [*:0]const u8 = undefined; + params.getChild(1, "&s", &shortcut_id); + log.debug("activated={s}", .{shortcut_id}); + + const action = self.private().map.get(std.mem.span(shortcut_id)) orelse return; + signals.trigger.impl.emit( + self, + null, + .{&action}, + null, + ); + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + // Since we drop references here we may lose access to things like + // dbus connections, so we need to close all our connections right + // away instead of in finalize. + self.close(); + + const priv = self.private(); + if (priv.config) |v| { + v.unref(); + priv.config = null; + } + if (priv.dbus_connection) |v| { + v.unref(); + priv.dbus_connection = null; + } + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + // Properties + gobject.ext.registerProperties(class, &.{ + properties.config.impl, + properties.@"dbus-connection".impl, + }); + + // Signals + signals.trigger.impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + }; +}; + +const Token = [16]u8; + +/// Generate a random token suitable for use in requests. +fn generateToken(buf: *Token) [:0]const u8 { + // u28 takes up 7 bytes in hex, 8 bytes for "ghostty_" and 1 byte for NUL + // 7 + 8 + 1 = 16 + return std.fmt.bufPrintZ( + buf, + "ghostty_{x:0<7}", + .{std.crypto.random.int(u28)}, + ) catch unreachable; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/imgui_widget.zig b/vendor/libghostty-vt/src/apprt/gtk/class/imgui_widget.zig new file mode 100644 index 00000000..0ef753a8 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/imgui_widget.zig @@ -0,0 +1,551 @@ +const std = @import("std"); +const assert = @import("../../../quirks.zig").inlineAssert; + +const cimgui = @import("dcimgui"); +const gl = @import("opengl"); +const adw = @import("adw"); +const gdk = @import("gdk"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const input = @import("../../../input.zig"); +const gresource = @import("../build/gresource.zig"); + +const key = @import("../key.zig"); +const Common = @import("../class.zig").Common; + +const log = std.log.scoped(.gtk_ghostty_imgui_widget); + +/// A widget for embedding a Dear ImGui application. +/// +/// It'd be a lot cleaner to use inheritance here but zig-gobject doesn't +/// currently have a way to define virtual methods, so we have to use +/// composition and signals instead. +pub const ImguiWidget = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyImguiWidget", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct {}; + + pub const signals = struct {}; + + pub const virtual_methods = struct { + /// This virtual method will be called to allow the Dear ImGui + /// application to do one-time setup of the context. The correct context + /// will be current when the virtual method is called. + pub const setup = C.defineVirtualMethod("setup"); + + /// This virtual method will be called at each frame to allow the Dear + /// ImGui application to draw the application. The correct context will + /// be current when the virtual method is called. + pub const render = C.defineVirtualMethod("render"); + }; + + const Private = struct { + /// GL area where we display the Dear ImGui application. + gl_area: *gtk.GLArea, + + /// GTK input method context + im_context: *gtk.IMMulticontext, + + /// Dear ImGui context. We create a context per widget so that we can + /// have multiple active imgui views in the same application. + ig_context: ?*cimgui.c.ImGuiContext = null, + + /// Our previous instant used to calculate delta time for animations. + instant: ?std.time.Instant = null, + + /// Tick callback ID for timed updates. + tick_callback_id: c_uint = 0, + + /// Last render time for throttling to 30 FPS. + last_render_time: ?std.time.Instant = null, + + pub var offset: c_int = 0; + }; + + //--------------------------------------------------------------- + // Virtual Methods + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + fn dispose(self: *Self) callconv(.c) void { + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + // Public methods + + /// This should be called anytime the underlying data for the UI changes + /// so that the UI can be refreshed. + pub fn queueRender(self: *ImguiWidget) void { + const priv = self.private(); + priv.gl_area.queueRender(); + } + + //--------------------------------------------------------------- + // Public wrappers for virtual methods + + /// This virtual method will be called to allow the Dear ImGui application + /// to do one-time setup of the context. The correct context will be current + /// when the virtual method is called. + pub fn setup(self: *Self) callconv(.c) void { + const class = self.getClass() orelse return; + virtual_methods.setup.call(class, self, .{}); + } + + /// This virtual method will be called at each frame to allow the Dear ImGui + /// application to draw the application. The correct context will be current + /// when the virtual method is called. + pub fn render(self: *Self) callconv(.c) void { + const class = self.getClass() orelse return; + virtual_methods.render.call(class, self, .{}); + } + + //--------------------------------------------------------------- + // Private Methods + + /// Set our imgui context to be current, or return an error. This must be + /// called before any Dear ImGui API calls so that they're made against + /// the proper context. + fn setCurrentContext(self: *Self) error{ContextNotInitialized}!void { + const priv = self.private(); + const ig_context = priv.ig_context orelse { + log.warn("Dear ImGui context not initialized", .{}); + return error.ContextNotInitialized; + }; + cimgui.c.ImGui_SetCurrentContext(ig_context); + } + + /// Initialize the frame. Expects that the context is already current. + fn newFrame(self: *Self) void { + const priv = self.private(); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + + // Determine our delta time + const now = std.time.Instant.now() catch unreachable; + io.DeltaTime = if (priv.instant) |prev| delta: { + const since_ns: f64 = @floatFromInt(now.since(prev)); + const ns_per_s: f64 = @floatFromInt(std.time.ns_per_s); + const since_s: f32 = @floatCast(since_ns / ns_per_s); + break :delta @max(0.00001, since_s); + } else (1.0 / 60.0); + + priv.instant = now; + } + + /// Handle key press/release events. + fn keyEvent( + self: *ImguiWidget, + action: input.Action, + ec_key: *gtk.EventControllerKey, + keyval: c_uint, + _: c_uint, + gtk_mods: gdk.ModifierType, + ) bool { + self.queueRender(); + + self.setCurrentContext() catch return false; + + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + + const mods = key.translateMods(gtk_mods); + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftShift, mods.shift); + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftCtrl, mods.ctrl); + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftAlt, mods.alt); + cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftSuper, mods.super); + + // If our keyval has a key, then we send that key event + if (key.keyFromKeyval(keyval)) |inputkey| { + if (inputkey.imguiKey()) |imgui_key| { + cimgui.c.ImGuiIO_AddKeyEvent(io, imgui_key, action == .press); + } + } + + // Try to process the event as text + if (ec_key.as(gtk.EventController).getCurrentEvent()) |event| { + const priv = self.private(); + _ = priv.im_context.as(gtk.IMContext).filterKeypress(event); + } + + return true; + } + + /// Translate a GTK mouse button to a Dear ImGui mouse button. + fn translateMouseButton(button: c_uint) ?c_int { + return switch (button) { + 1 => cimgui.c.ImGuiMouseButton_Left, + 2 => cimgui.c.ImGuiMouseButton_Middle, + 3 => cimgui.c.ImGuiMouseButton_Right, + else => null, + }; + } + + /// Get the scale factor that the display is operating at. + fn getScaleFactor(self: *Self) f64 { + const priv = self.private(); + return @floatFromInt(priv.gl_area.as(gtk.Widget).getScaleFactor()); + } + + //--------------------------------------------------------------- + // Properties + + //--------------------------------------------------------------- + // Signal Handlers + + fn glAreaRealize(_: *gtk.GLArea, self: *Self) callconv(.c) void { + const priv = self.private(); + assert(priv.ig_context == null); + + priv.gl_area.makeCurrent(); + if (priv.gl_area.getError()) |err| { + log.warn("GLArea for Dear ImGui widget failed to realize: {s}", .{err.f_message orelse "(unknown)"}); + return; + } + + priv.ig_context = cimgui.c.ImGui_CreateContext(null) orelse { + log.warn("unable to initialize Dear ImGui context", .{}); + return; + }; + self.setCurrentContext() catch return; + + // Setup some basic config + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + io.BackendPlatformName = "ghostty_gtk"; + + // Realize means that our OpenGL context is ready, so we can now + // initialize the ImgUI OpenGL backend for our context. + _ = cimgui.ImGui_ImplOpenGL3_Init(null); + + // Call the virtual method to setup the UI. + self.setup(); + + // Add a tick callback to drive timed updates via the frame clock. + priv.tick_callback_id = self.as(gtk.Widget).addTickCallback( + tickCallback, + null, + null, + ); + } + + /// Handle a request to unrealize the GLArea + fn glAreaUnrealize(_: *gtk.GLArea, self: *ImguiWidget) callconv(.c) void { + const priv = self.private(); + assert(priv.ig_context != null); + + // Remove the tick callback if it was registered. + if (priv.tick_callback_id != 0) { + self.as(gtk.Widget).removeTickCallback(priv.tick_callback_id); + priv.tick_callback_id = 0; + } + + // Unrealize is not guaranteed to be called with a current GL context, + // so we make it current for ImGui cleanup. + priv.gl_area.makeCurrent(); + if (priv.gl_area.getError()) |err| { + log.warn("GLArea for Dear ImGui widget failed to realize: {s}", .{err.f_message orelse "(unknown)"}); + return; + } + + self.setCurrentContext() catch return; + cimgui.ImGui_ImplOpenGL3_ShutdownWithLoaderCleanup(); + cimgui.c.ImGui_DestroyContext(priv.ig_context); + priv.ig_context = null; + } + + /// Handle a request to resize the GLArea + fn glAreaResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *Self) callconv(.c) void { + self.setCurrentContext() catch return; + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + const scale_factor = area.as(gtk.Widget).getScaleFactor(); + + // Our display size is always unscaled. We'll do the scaling in the + // style instead. This creates crisper looking fonts. + io.DisplaySize = .{ .x = @floatFromInt(width), .y = @floatFromInt(height) }; + io.DisplayFramebufferScale = .{ .x = 1, .y = 1 }; + + // Setup a new style and scale it appropriately. We must use the + // ImGuiStyle constructor to get proper default values (e.g., + // CurveTessellationTol) rather than zero-initialized values. + var style: cimgui.c.ImGuiStyle = undefined; + cimgui.ext.ImGuiStyle_ImGuiStyle(&style); + cimgui.c.ImGuiStyle_ScaleAllSizes(&style, @floatFromInt(scale_factor)); + const active_style = cimgui.c.ImGui_GetStyle(); + active_style.* = style; + } + + /// Handle a request to render the contents of our GLArea + fn glAreaRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Self) callconv(.c) c_int { + self.setCurrentContext() catch return @intFromBool(false); + + // Update last render time for tick callback throttling. + const priv = self.private(); + priv.last_render_time = std.time.Instant.now() catch null; + + // Setup our frame. We render twice because some ImGui behaviors + // take multiple renders to process. I don't know how to make this + // more efficient. + for (0..2) |_| { + cimgui.ImGui_ImplOpenGL3_NewFrame(); + self.newFrame(); + cimgui.c.ImGui_NewFrame(); + + // Call the virtual method to draw the UI. + self.render(); + + // Render + cimgui.c.ImGui_Render(); + } + + // OpenGL final render + gl.clearColor(0x28 / 0xFF, 0x2C / 0xFF, 0x34 / 0xFF, 1.0); + gl.clear(gl.c.GL_COLOR_BUFFER_BIT); + cimgui.ImGui_ImplOpenGL3_RenderDrawData(cimgui.c.ImGui_GetDrawData()); + + return @intFromBool(true); + } + + fn ecFocusEnter(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void { + self.setCurrentContext() catch return; + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + cimgui.c.ImGuiIO_AddFocusEvent(io, true); + self.queueRender(); + } + + fn ecFocusLeave(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void { + self.setCurrentContext() catch return; + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + cimgui.c.ImGuiIO_AddFocusEvent(io, false); + self.queueRender(); + } + + fn ecKeyPressed( + ec_key: *gtk.EventControllerKey, + keyval: c_uint, + keycode: c_uint, + gtk_mods: gdk.ModifierType, + self: *ImguiWidget, + ) callconv(.c) c_int { + return @intFromBool(self.keyEvent( + .press, + ec_key, + keyval, + keycode, + gtk_mods, + )); + } + + fn ecKeyReleased( + ec_key: *gtk.EventControllerKey, + keyval: c_uint, + keycode: c_uint, + gtk_mods: gdk.ModifierType, + self: *ImguiWidget, + ) callconv(.c) void { + _ = self.keyEvent( + .release, + ec_key, + keyval, + keycode, + gtk_mods, + ); + } + + fn ecMousePressed( + gesture: *gtk.GestureClick, + _: c_int, + _: f64, + _: f64, + self: *ImguiWidget, + ) callconv(.c) void { + self.queueRender(); + self.setCurrentContext() catch return; + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + const gdk_button = gesture.as(gtk.GestureSingle).getCurrentButton(); + if (translateMouseButton(gdk_button)) |button| { + cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, true); + } + } + + fn ecMouseReleased( + gesture: *gtk.GestureClick, + _: c_int, + _: f64, + _: f64, + self: *ImguiWidget, + ) callconv(.c) void { + self.queueRender(); + self.setCurrentContext() catch return; + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + const gdk_button = gesture.as(gtk.GestureSingle).getCurrentButton(); + if (translateMouseButton(gdk_button)) |button| { + cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, false); + } + } + + fn ecMouseMotion( + _: *gtk.EventControllerMotion, + x: f64, + y: f64, + self: *ImguiWidget, + ) callconv(.c) void { + self.queueRender(); + self.setCurrentContext() catch return; + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + const scale_factor = self.getScaleFactor(); + cimgui.c.ImGuiIO_AddMousePosEvent( + io, + @floatCast(x * scale_factor), + @floatCast(y * scale_factor), + ); + } + + fn ecMouseScroll( + _: *gtk.EventControllerScroll, + x: f64, + y: f64, + self: *ImguiWidget, + ) callconv(.c) c_int { + self.queueRender(); + self.setCurrentContext() catch return @intFromBool(false); + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + cimgui.c.ImGuiIO_AddMouseWheelEvent( + io, + @floatCast(x), + @floatCast(-y), + ); + return @intFromBool(true); + } + + fn imCommit( + _: *gtk.IMMulticontext, + bytes: [*:0]u8, + self: *ImguiWidget, + ) callconv(.c) void { + self.queueRender(); + self.setCurrentContext() catch return; + const io: *cimgui.c.ImGuiIO = cimgui.c.ImGui_GetIO(); + cimgui.c.ImGuiIO_AddInputCharactersUTF8(io, bytes); + } + + /// Tick callback for timed updates. This drives periodic redraws. + /// Redraws are limited to 30 FPS max since our imgui widgets don't + /// usually need higher frame rates than that. + fn tickCallback( + widget: *gtk.Widget, + _: *gdk.FrameClock, + _: ?*anyopaque, + ) callconv(.c) c_int { + const self: *Self = gobject.ext.cast(Self, widget) orelse return 0; + const priv = self.private(); + + const now = std.time.Instant.now() catch { + self.queueRender(); + return 1; + }; + + // Throttle to 30 FPS (~33ms between frames) + const frame_time_ns: u64 = std.time.ns_per_s / 30; + const should_render = if (priv.last_render_time) |last| + now.since(last) >= frame_time_ns + else + true; + + if (should_render) self.queueRender(); + + return 1; // Continue the tick callback + } + + //--------------------------------------------------------------- + // Default virtual method handlers + + /// Default setup function. Does nothing but log a warning. + fn defaultSetup(_: *Self) callconv(.c) void { + log.warn("default Dear ImGui setup called, this is a bug.", .{}); + } + + /// Default render function. Does nothing but log a warning. + fn defaultRender(_: *Self) callconv(.c) void { + log.warn("default Dear ImGui render called, this is a bug.", .{}); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const refSink = C.refSink; + pub const unref = C.unref; + pub const getClass = C.getClass; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + + /// Function pointers for virtual methods. + setup: ?*const fn (*Self) callconv(.c) void, + render: ?*const fn (*Self) callconv(.c) void, + + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "imgui-widget", + }), + ); + + // Initialize our virtual methods with default functions. + class.setup = defaultSetup; + class.render = defaultRender; + + // Bindings + class.bindTemplateChildPrivate("gl_area", .{}); + class.bindTemplateChildPrivate("im_context", .{}); + + // Template Callbacks + class.bindTemplateCallback("realize", &glAreaRealize); + class.bindTemplateCallback("unrealize", &glAreaUnrealize); + class.bindTemplateCallback("resize", &glAreaResize); + class.bindTemplateCallback("render", &glAreaRender); + class.bindTemplateCallback("focus_enter", &ecFocusEnter); + class.bindTemplateCallback("focus_leave", &ecFocusLeave); + class.bindTemplateCallback("key_pressed", &ecKeyPressed); + class.bindTemplateCallback("key_released", &ecKeyReleased); + class.bindTemplateCallback("mouse_pressed", &ecMousePressed); + class.bindTemplateCallback("mouse_released", &ecMouseReleased); + class.bindTemplateCallback("mouse_motion", &ecMouseMotion); + class.bindTemplateCallback("scroll", &ecMouseScroll); + class.bindTemplateCallback("im_commit", &imCommit); + + // Signals + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/inspector_widget.zig b/vendor/libghostty-vt/src/apprt/gtk/class/inspector_widget.zig new file mode 100644 index 00000000..80ac7fc3 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/inspector_widget.zig @@ -0,0 +1,245 @@ +const std = @import("std"); + +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); +const Inspector = @import("../../../inspector/Inspector.zig"); + +const Common = @import("../class.zig").Common; +const Surface = @import("surface.zig").Surface; +const ImguiWidget = @import("imgui_widget.zig").ImguiWidget; + +const log = std.log.scoped(.gtk_ghostty_inspector_widget); + +/// Widget for displaying the Ghostty inspector. +pub const InspectorWidget = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = ImguiWidget; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyInspectorWidget", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const surface = struct { + pub const name = "surface"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface, + .{ + .accessor = .{ + .getter = getSurfaceValue, + .setter = setSurfaceValue, + }, + }, + ); + }; + }; + + pub const signals = struct {}; + + const Private = struct { + /// The surface that we are attached to. This is NOT referenced. + /// We attach a weak notify to the object. + surface: ?*Surface = null, + + pub var offset: c_int = 0; + }; + + //--------------------------------------------------------------- + // Virtual Methods + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + fn dispose(self: *Self) callconv(.c) void { + // Clear our surface so it deactivates the inspector. + self.setSurface(null); + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + /// Called to do initial setup of the UI. + fn imguiSetup( + _: *Self, + ) callconv(.c) void { + Inspector.setup(); + } + + /// Called for every frame to draw the UI. + fn imguiRender( + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + const surface = priv.surface orelse return; + const core_surface = surface.core() orelse return; + const inspector = core_surface.inspector orelse return; + inspector.render(core_surface); + } + + //--------------------------------------------------------------- + // Public methods + + /// Queue a render of the Dear ImGui widget. + pub fn queueRender(self: *Self) void { + self.as(ImguiWidget).queueRender(); + } + + //--------------------------------------------------------------- + // Properties + + fn getSurfaceValue(self: *Self, value: *gobject.Value) void { + gobject.ext.Value.set( + value, + self.private().surface, + ); + } + + fn setSurfaceValue(self: *Self, value: *const gobject.Value) void { + self.setSurface(gobject.ext.Value.get( + value, + ?*Surface, + )); + } + + pub fn getSurface(self: *Self) ?*Surface { + return self.private().surface; + } + + pub fn setSurface(self: *Self, surface_: ?*Surface) void { + const priv = self.private(); + + // Do nothing if we're not changing the value. + if (surface_ == priv.surface) return; + + // Setup our notification to happen at the end because we're + // changing values no matter what. + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + self.as(gobject.Object).notifyByPspec(properties.surface.impl.param_spec); + + // Deactivate the inspector on the old surface if it exists + // and set our value to null. + if (priv.surface) |old| old: { + priv.surface = null; + + // Remove our weak ref + old.as(gobject.Object).weakUnref( + surfaceWeakNotify, + self, + ); + + // Deactivate the inspector + const core_surface = old.core() orelse break :old; + core_surface.deactivateInspector(); + } + + // Activate the inspector on the new surface. + const surface = surface_ orelse return; + const core_surface = surface.core() orelse return; + core_surface.activateInspector() catch |err| { + log.warn("failed to activate inspector err={}", .{err}); + return; + }; + + // We use a weak reference on surface to determine if the surface + // was closed while our inspector was active. + surface.as(gobject.Object).weakRef( + surfaceWeakNotify, + self, + ); + + // Store our surface. We don't need to ref this because we setup + // the weak notify above. + priv.surface = surface; + + self.queueRender(); + } + + //--------------------------------------------------------------- + // Signal Handlers + + fn surfaceWeakNotify( + ud: ?*anyopaque, + surface: *gobject.Object, + ) callconv(.c) void { + const self: *Self = @ptrCast(@alignCast(ud orelse return)); + const priv = self.private(); + + // The weak notify docs call out that we can specifically use the + // pointer values for comparison, but the objects themselves are unsafe. + if (@intFromPtr(priv.surface) != @intFromPtr(surface)) return; + + // According to weak notify docs, "surface" is in the "dispose" state. + // Our surface doesn't clear the core surface until the "finalize" + // state so we should be able to safely access it here. We need to + // be really careful though. + const old = priv.surface orelse return; + const core_surface = old.core() orelse return; + core_surface.deactivateInspector(); + priv.surface = null; + self.as(gobject.Object).notifyByPspec(properties.surface.impl.param_spec); + + // Note: in the future we should probably show some content on our + // window to note that the surface went away in case our embedding + // widget doesn't close itself. As I type this, our window closes + // immediately when the surface goes away so you don't see this, but + // for completeness sake we should clean this up. + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const refSink = C.refSink; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(ImguiWidget); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "inspector-widget", + }), + ); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.surface.impl, + }); + + // Signals + + // Virtual methods + ImguiWidget.virtual_methods.setup.implement(class, imguiSetup); + ImguiWidget.virtual_methods.render.implement(class, imguiRender); + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/inspector_window.zig b/vendor/libghostty-vt/src/apprt/gtk/class/inspector_window.zig new file mode 100644 index 00000000..739e7569 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/inspector_window.zig @@ -0,0 +1,217 @@ +const std = @import("std"); +const build_config = @import("../../../build_config.zig"); + +const adw = @import("adw"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); + +const Common = @import("../class.zig").Common; +const Surface = @import("surface.zig").Surface; +const DebugWarning = @import("debug_warning.zig").DebugWarning; +const InspectorWidget = @import("inspector_widget.zig").InspectorWidget; +const WeakRef = @import("../weak_ref.zig").WeakRef; + +const log = std.log.scoped(.gtk_ghostty_inspector_window); + +/// Window for displaying the Ghostty inspector. +pub const InspectorWindow = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.ApplicationWindow; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyInspectorWindow", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const surface = struct { + pub const name = "surface"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface, + .{ + .accessor = .{ + .getter = getSurfaceValue, + .setter = setSurfaceValue, + }, + }, + ); + }; + + pub const debug = struct { + pub const name = "debug"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = build_config.is_debug, + .accessor = gobject.ext.typedAccessor(Self, bool, .{ + .getter = struct { + pub fn getter(_: *Self) bool { + return build_config.is_debug; + } + }.getter, + }), + }, + ); + }; + }; + + pub const signals = struct {}; + + const Private = struct { + /// The surface that we are attached to + surface: WeakRef(Surface) = .empty, + + /// The embedded inspector widget. + inspector_widget: *InspectorWidget, + + pub var offset: c_int = 0; + }; + + //--------------------------------------------------------------- + // Virtual Methods + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + // Add our dev CSS class if we're in debug mode. + if (comptime build_config.is_debug) { + self.as(gtk.Widget).addCssClass("devel"); + } + + // Set our window icon. We can't set this in the blueprint file + // because its dependent on the build config. + self.as(gtk.Window).setIconName(build_config.bundle_id); + } + + fn dispose(self: *Self) callconv(.c) void { + // You MUST clear all weak refs in dispose, otherwise it causes + // memory corruption on dispose on the TARGET (weak referenced) + // object. The only way we caught this is via Valgrind. Its not a leak, + // its an invalid memory read. In practice, I found this sometimes + // caused hanging! + self.setSurface(null); + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + // Public methods + + pub fn new(surface: *Surface) *Self { + return gobject.ext.newInstance(Self, .{ + .surface = surface, + }); + } + + /// Present the window. + pub fn present(self: *Self) void { + self.as(gtk.Window).present(); + } + + /// Queue a render of the embedded widget. + pub fn queueRender(self: *Self) void { + const priv = self.private(); + priv.inspector_widget.queueRender(); + } + + //--------------------------------------------------------------- + // Properties + + fn setSurface(self: *Self, newvalue: ?*Surface) void { + const priv = self.private(); + priv.surface.set(newvalue); + } + + fn getSurfaceValue(self: *Self, value: *gobject.Value) void { + // Important: get() refs, so we take to not increase ref twice + gobject.ext.Value.take( + value, + self.private().surface.get(), + ); + } + + fn setSurfaceValue(self: *Self, value: *const gobject.Value) void { + self.setSurface(gobject.ext.Value.get( + value, + ?*Surface, + )); + } + + //--------------------------------------------------------------- + // Signal Handlers + + fn propInspectorSurface( + inspector: *InspectorWidget, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // If the inspector's surface went away, we destroy the window. + // The inspector has a weak notify on the surface so it knows + // if it goes nil. + if (inspector.getSurface() == null) { + self.as(gtk.Window).destroy(); + } + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const refSink = C.refSink; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(DebugWarning); + gobject.ext.ensureType(InspectorWidget); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "inspector-window", + }), + ); + + // Template Bindings + class.bindTemplateChildPrivate("inspector_widget", .{}); + + // Template callbacks + class.bindTemplateCallback("notify_inspector_surface", &propInspectorSurface); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.surface.impl, + properties.debug.impl, + }); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/key_state_overlay.zig b/vendor/libghostty-vt/src/apprt/gtk/class/key_state_overlay.zig new file mode 100644 index 00000000..7aca8f01 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/key_state_overlay.zig @@ -0,0 +1,342 @@ +const std = @import("std"); +const adw = @import("adw"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const ext = @import("../ext.zig"); +const gresource = @import("../build/gresource.zig"); +const Application = @import("application.zig").Application; +const Common = @import("../class.zig").Common; + +const log = std.log.scoped(.gtk_ghostty_key_state_overlay); + +/// An overlay that displays the current key table stack and pending key sequence. +/// This helps users understand what key bindings are active and what keys they've +/// pressed in a multi-key sequence. +pub const KeyStateOverlay = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyKeyStateOverlay", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const tables = struct { + pub const name = "tables"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*ext.StringList, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*ext.StringList, + .{ + .getter = getTables, + .getter_transfer = .none, + .setter = setTables, + .setter_transfer = .full, + }, + ), + }, + ); + }; + + pub const @"has-tables" = struct { + pub const name = "has-tables"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ .getter = getHasTables }, + ), + }, + ); + }; + + pub const sequence = struct { + pub const name = "sequence"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*ext.StringList, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*ext.StringList, + .{ + .getter = getSequence, + .getter_transfer = .none, + .setter = setSequence, + .setter_transfer = .full, + }, + ), + }, + ); + }; + + pub const @"has-sequence" = struct { + pub const name = "has-sequence"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ .getter = getHasSequence }, + ), + }, + ); + }; + + pub const @"valign-target" = struct { + pub const name = "valign-target"; + const impl = gobject.ext.defineProperty( + name, + Self, + gtk.Align, + .{ + .default = .end, + .accessor = C.privateShallowFieldAccessor("valign_target"), + }, + ); + }; + }; + + const Private = struct { + /// The key table stack. + tables: ?*ext.StringList = null, + + /// The key sequence. + sequence: ?*ext.StringList = null, + + /// Target vertical alignment for the overlay. + valign_target: gtk.Align = .end, + + pub var offset: c_int = 0; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + fn getTables(self: *Self) ?*ext.StringList { + return self.private().tables; + } + + fn getSequence(self: *Self) ?*ext.StringList { + return self.private().sequence; + } + + fn setTables(self: *Self, value: ?*ext.StringList) void { + const priv = self.private(); + if (priv.tables) |old| { + old.destroy(); + priv.tables = null; + } + if (value) |v| { + priv.tables = v; + } + + self.as(gobject.Object).notifyByPspec(properties.tables.impl.param_spec); + self.as(gobject.Object).notifyByPspec(properties.@"has-tables".impl.param_spec); + } + + fn setSequence(self: *Self, value: ?*ext.StringList) void { + const priv = self.private(); + if (priv.sequence) |old| { + old.destroy(); + priv.sequence = null; + } + if (value) |v| { + priv.sequence = v; + } + + self.as(gobject.Object).notifyByPspec(properties.sequence.impl.param_spec); + self.as(gobject.Object).notifyByPspec(properties.@"has-sequence".impl.param_spec); + } + + fn getHasTables(self: *Self) bool { + const v = self.private().tables orelse return false; + return v.strings.len > 0; + } + + fn getHasSequence(self: *Self) bool { + const v = self.private().sequence orelse return false; + return v.strings.len > 0; + } + + fn closureShowChevron( + _: *Self, + has_tables: bool, + has_sequence: bool, + ) callconv(.c) c_int { + return if (has_tables and has_sequence) 1 else 0; + } + + fn closureHasState( + _: *Self, + has_tables: bool, + has_sequence: bool, + ) callconv(.c) c_int { + return if (has_tables or has_sequence) 1 else 0; + } + + fn closureTablesText( + _: *Self, + tables: ?*ext.StringList, + ) callconv(.c) ?[*:0]const u8 { + const list = tables orelse return null; + if (list.strings.len == 0) return null; + + var buf: std.Io.Writer.Allocating = .init(Application.default().allocator()); + defer buf.deinit(); + + for (list.strings, 0..) |s, i| { + if (i > 0) buf.writer.writeAll(" > ") catch return null; + buf.writer.writeAll(s) catch return null; + } + + return glib.ext.dupeZ(u8, buf.written()); + } + + fn closureSequenceText( + _: *Self, + sequence: ?*ext.StringList, + ) callconv(.c) ?[*:0]const u8 { + const list = sequence orelse return null; + if (list.strings.len == 0) return null; + + var buf: std.Io.Writer.Allocating = .init(Application.default().allocator()); + defer buf.deinit(); + + for (list.strings, 0..) |s, i| { + if (i > 0) buf.writer.writeAll(" ") catch return null; + buf.writer.writeAll(s) catch return null; + } + + return glib.ext.dupeZ(u8, buf.written()); + } + + //--------------------------------------------------------------- + // Template callbacks + + fn onDragEnd( + _: *gtk.GestureDrag, + _: f64, + offset_y: f64, + self: *Self, + ) callconv(.c) void { + // Key state overlay only moves between top-center and bottom-center. + // Horizontal alignment is always center. + const priv = self.private(); + const widget = self.as(gtk.Widget); + const parent = widget.getParent() orelse return; + + const parent_height: f64 = @floatFromInt(parent.getAllocatedHeight()); + const self_height: f64 = @floatFromInt(widget.getAllocatedHeight()); + + const self_y: f64 = if (priv.valign_target == .start) 0 else parent_height - self_height; + const new_y = self_y + offset_y + (self_height / 2); + + const new_valign: gtk.Align = if (new_y > parent_height / 2) .end else .start; + + if (new_valign != priv.valign_target) { + priv.valign_target = new_valign; + self.as(gobject.Object).notifyByPspec(properties.@"valign-target".impl.param_spec); + self.as(gtk.Widget).queueResize(); + } + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + + if (priv.tables) |v| { + v.destroy(); + } + if (priv.sequence) |v| { + v.destroy(); + } + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 2, + .name = "key-state-overlay", + }), + ); + + // Template Callbacks + class.bindTemplateCallback("on_drag_end", &onDragEnd); + class.bindTemplateCallback("show_chevron", &closureShowChevron); + class.bindTemplateCallback("has_state", &closureHasState); + class.bindTemplateCallback("tables_text", &closureTablesText); + class.bindTemplateCallback("sequence_text", &closureSequenceText); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.tables.impl, + properties.@"has-tables".impl, + properties.sequence.impl, + properties.@"has-sequence".impl, + properties.@"valign-target".impl, + }); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/resize_overlay.zig b/vendor/libghostty-vt/src/apprt/gtk/class/resize_overlay.zig new file mode 100644 index 00000000..e14f1563 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/resize_overlay.zig @@ -0,0 +1,338 @@ +const std = @import("std"); +const adw = @import("adw"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); +const Common = @import("../class.zig").Common; + +const log = std.log.scoped(.gtk_ghostty_resize_overlay); + +/// The overlay that shows the current size while a surface is resizing. +/// This can be used generically to show pretty much anything with a +/// disappearing overlay, but we have no other use at this point so it +/// is named specifically for what it does. +/// +/// General usage: +/// +/// 1. Add it to an overlay +/// 2. Set the label with `setLabel` +/// 3. Schedule to show it with `schedule` +/// +/// Set any properties to change the behavior. +pub const ResizeOverlay = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyResizeOverlay", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const duration = struct { + pub const name = "duration"; + const impl = gobject.ext.defineProperty( + name, + Self, + c_uint, + .{ + .default = 750, + .minimum = 250, + .maximum = std.math.maxInt(c_uint), + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "duration", + ), + }, + ); + }; + + pub const @"first-delay" = struct { + pub const name = "first-delay"; + const impl = gobject.ext.defineProperty( + name, + Self, + c_uint, + .{ + .default = 250, + .minimum = 250, + .maximum = std.math.maxInt(c_uint), + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "first_delay", + ), + }, + ); + }; + + pub const label = struct { + pub const name = "label"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("label_text"), + }, + ); + }; + + pub const @"overlay-halign" = struct { + pub const name = "overlay-halign"; + const impl = gobject.ext.defineProperty( + name, + Self, + gtk.Align, + .{ + .default = .center, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "halign", + ), + }, + ); + }; + + pub const @"overlay-valign" = struct { + pub const name = "overlay-valign"; + const impl = gobject.ext.defineProperty( + name, + Self, + gtk.Align, + .{ + .default = .center, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "valign", + ), + }, + ); + }; + }; + + const Private = struct { + /// The label with the text + label: *gtk.Label, + + /// The text to set on the label when scheduled. + label_text: ?[:0]const u8, + + /// The time that the overlay appears. + duration: c_uint, + + /// The first delay before any overlay is shown. Must be specified + /// during construction otherwise it has no effect. + first_delay: c_uint, + + /// The idle source that we use to update the label. + idler: ?c_uint = null, + + /// The timer for dismissing the overlay. + timer: ?c_uint = null, + + /// The first delay timer. + delay_timer: ?c_uint = null, + + /// The alignment of the label + halign: gtk.Align, + valign: gtk.Align, + + pub var offset: c_int = 0; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + const priv = self.private(); + if (priv.first_delay > 0) { + priv.delay_timer = glib.timeoutAdd( + priv.first_delay, + onDelayTimer, + self, + ); + } + } + + /// Set the label for the overlay. This will not show the + /// overlay if it is currently hidden; you must call schedule. + pub fn setLabel(self: *Self, label: ?[:0]const u8) void { + const priv = self.private(); + if (priv.label_text) |v| glib.free(@ptrCast(@constCast(v))); + priv.label_text = null; + if (label) |v| priv.label_text = glib.ext.dupeZ(u8, v); + self.as(gobject.Object).notifyByPspec(properties.label.impl.param_spec); + } + + /// Schedule the overlay to be shown. To avoid flickering during + /// resizes we schedule the overlay to be shown on the next idle tick. + pub fn schedule(self: *Self) void { + const priv = self.private(); + + // If we have a delay timer then we're not showing anything + // yet so do nothing. + if (priv.delay_timer != null) return; + + // When updating a widget, wait until GTK is "idle", i.e. not in the middle + // of doing any other updates. Since we are called in the middle of resizing + // GTK is doing a lot of work rearranging all of the widgets. Not doing this + // results in a lot of warnings from GTK and _horrible_ flickering of the + // resize overlay. + if (priv.idler != null) return; + priv.idler = glib.idleAdd(onIdle, self); + } + + fn onIdle(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + const priv = self.private(); + + // No matter what our idler is complete with this callback + priv.idler = null; + + // Cancel our previous show timer no matter what. + if (priv.timer) |timer| { + if (glib.Source.remove(timer) == 0) { + log.warn("unable to remove size overlay timer", .{}); + } + priv.timer = null; + } + + // If we have a label to show, show ourselves. If we don't have + // label text, then hide our label. + const text = priv.label_text orelse { + self.as(gtk.Widget).setVisible(0); + return 0; + }; + + // Set our label and show it. + priv.label.setLabel(text); + self.as(gtk.Widget).setVisible(1); + + // Setup the new timer to hide ourselves after the delay. + priv.timer = glib.timeoutAdd( + priv.duration, + onTimer, + self, + ); + + return 0; + } + + fn onTimer(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + const priv = self.private(); + priv.timer = null; + self.as(gtk.Widget).setVisible(0); + return 0; + } + + fn onDelayTimer(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + const priv = self.private(); + priv.delay_timer = null; + return 0; + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.idler) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove resize overlay idler", .{}); + } + priv.idler = null; + } + if (priv.timer) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove resize overlay timer", .{}); + } + priv.timer = null; + } + if (priv.delay_timer) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove resize overlay delay timer", .{}); + } + priv.delay_timer = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.label_text) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.label_text = null; + } + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 2, + .name = "resize-overlay", + }), + ); + + // Bindings + class.bindTemplateChildPrivate("label", .{}); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.duration.impl, + properties.label.impl, + properties.@"first-delay".impl, + properties.@"overlay-halign".impl, + properties.@"overlay-valign".impl, + }); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/search_overlay.zig b/vendor/libghostty-vt/src/apprt/gtk/class/search_overlay.zig new file mode 100644 index 00000000..a8111546 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/search_overlay.zig @@ -0,0 +1,493 @@ +const std = @import("std"); +const adw = @import("adw"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gdk = @import("gdk"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); +const Common = @import("../class.zig").Common; + +const log = std.log.scoped(.gtk_ghostty_search_overlay); + +/// The overlay that shows the current size while a surface is resizing. +/// This can be used generically to show pretty much anything with a +/// disappearing overlay, but we have no other use at this point so it +/// is named specifically for what it does. +/// +/// General usage: +/// +/// 1. Add it to an overlay +/// 2. Set the label with `setLabel` +/// 3. Schedule to show it with `schedule` +/// +/// Set any properties to change the behavior. +pub const SearchOverlay = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttySearchOverlay", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const active = struct { + pub const name = "active"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ + .getter = getSearchActive, + .setter = setSearchActive, + }, + ), + }, + ); + }; + + pub const @"search-total" = struct { + pub const name = "search-total"; + const impl = gobject.ext.defineProperty( + name, + Self, + u64, + .{ + .default = 0, + .minimum = 0, + .maximum = std.math.maxInt(u64), + .accessor = gobject.ext.typedAccessor( + Self, + u64, + .{ .getter = getSearchTotal }, + ), + }, + ); + }; + + pub const @"has-search-total" = struct { + pub const name = "has-search-total"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ .getter = getHasSearchTotal }, + ), + }, + ); + }; + + pub const @"search-selected" = struct { + pub const name = "search-selected"; + const impl = gobject.ext.defineProperty( + name, + Self, + u64, + .{ + .default = 0, + .minimum = 0, + .maximum = std.math.maxInt(u64), + .accessor = gobject.ext.typedAccessor( + Self, + u64, + .{ .getter = getSearchSelected }, + ), + }, + ); + }; + + pub const @"has-search-selected" = struct { + pub const name = "has-search-selected"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ .getter = getHasSearchSelected }, + ), + }, + ); + }; + + pub const @"halign-target" = struct { + pub const name = "halign-target"; + const impl = gobject.ext.defineProperty( + name, + Self, + gtk.Align, + .{ + .default = .end, + .accessor = C.privateShallowFieldAccessor("halign_target"), + }, + ); + }; + + pub const @"valign-target" = struct { + pub const name = "valign-target"; + const impl = gobject.ext.defineProperty( + name, + Self, + gtk.Align, + .{ + .default = .start, + .accessor = C.privateShallowFieldAccessor("valign_target"), + }, + ); + }; + }; + + pub const signals = struct { + /// Emitted when the search is stopped (e.g., Escape pressed). + pub const @"stop-search" = struct { + pub const name = "stop-search"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted when the search text changes (debounced). + pub const @"search-changed" = struct { + pub const name = "search-changed"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{?[*:0]const u8}, + void, + ); + }; + + /// Emitted when navigating to the next match. + pub const @"next-match" = struct { + pub const name = "next-match"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted when navigating to the previous match. + pub const @"previous-match" = struct { + pub const name = "previous-match"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + + const Private = struct { + /// The search entry widget. + search_entry: *gtk.SearchEntry, + + /// True when a search is active, meaning we should show the overlay. + active: bool = false, + + /// Total number of search matches (null means unknown/none). + search_total: ?usize = null, + + /// Currently selected match index (null means none selected). + search_selected: ?usize = null, + + /// Target horizontal alignment for the overlay. + halign_target: gtk.Align = .end, + + /// Target vertical alignment for the overlay. + valign_target: gtk.Align = .start, + + pub var offset: c_int = 0; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + /// Grab focus on the search entry and select all text. + pub fn grabFocus(self: *Self) void { + const priv = self.private(); + _ = priv.search_entry.as(gtk.Widget).grabFocus(); + + // Select all text in the search entry field. -1 is distance from + // the end, causing the entire text to be selected. + priv.search_entry.as(gtk.Editable).selectRegion(0, -1); + } + + // Set active status, and update search on activation + fn setSearchActive(self: *Self, active: bool) void { + const priv = self.private(); + if (!priv.active and active) { + const text = priv.search_entry.as(gtk.Editable).getText(); + signals.@"search-changed".impl.emit(self, null, .{text}, null); + } + priv.active = active; + } + + // Set contents of search + pub fn setSearchContents(self: *Self, content: [:0]const u8) void { + const priv = self.private(); + priv.search_entry.as(gtk.Editable).setText(content); + signals.@"search-changed".impl.emit(self, null, .{content}, null); + } + + /// Set the total number of search matches. + pub fn setSearchTotal(self: *Self, total: ?usize) void { + const priv = self.private(); + const had_total = priv.search_total != null; + if (priv.search_total == total) return; + priv.search_total = total; + self.as(gobject.Object).notifyByPspec(properties.@"search-total".impl.param_spec); + if (had_total != (total != null)) { + self.as(gobject.Object).notifyByPspec(properties.@"has-search-total".impl.param_spec); + } + } + + /// Set the currently selected match index. + pub fn setSearchSelected(self: *Self, selected: ?usize) void { + const priv = self.private(); + const had_selected = priv.search_selected != null; + if (priv.search_selected == selected) return; + priv.search_selected = selected; + self.as(gobject.Object).notifyByPspec(properties.@"search-selected".impl.param_spec); + if (had_selected != (selected != null)) { + self.as(gobject.Object).notifyByPspec(properties.@"has-search-selected".impl.param_spec); + } + } + + fn getSearchActive(self: *Self) bool { + return self.private().active; + } + + fn getSearchTotal(self: *Self) u64 { + return self.private().search_total orelse 0; + } + + fn getHasSearchTotal(self: *Self) bool { + return self.private().search_total != null; + } + + fn getSearchSelected(self: *Self) u64 { + return self.private().search_selected orelse 0; + } + + fn getHasSearchSelected(self: *Self) bool { + return self.private().search_selected != null; + } + + fn closureMatchLabel( + _: *Self, + has_selected: bool, + selected: u64, + has_total: bool, + total: u64, + ) callconv(.c) ?[*:0]const u8 { + if (!has_total or total == 0) return glib.ext.dupeZ(u8, "0/0"); + var buf: [32]u8 = undefined; + const label = std.fmt.bufPrintZ(&buf, "{}/{}", .{ + if (has_selected) selected + 1 else 0, + total, + }) catch return null; + return glib.ext.dupeZ(u8, label); + } + + //--------------------------------------------------------------- + // Template callbacks + + fn searchChanged(entry: *gtk.SearchEntry, self: *Self) callconv(.c) void { + const text = entry.as(gtk.Editable).getText(); + signals.@"search-changed".impl.emit(self, null, .{text}, null); + } + + // NOTE: The callbacks below use anyopaque for the first parameter + // because they're shared with multiple widgets in the template. + + fn stopSearch(_: *anyopaque, self: *Self) callconv(.c) void { + signals.@"stop-search".impl.emit(self, null, .{}, null); + } + + fn nextMatch(_: *anyopaque, self: *Self) callconv(.c) void { + signals.@"next-match".impl.emit(self, null, .{}, null); + } + + fn previousMatch(_: *anyopaque, self: *Self) callconv(.c) void { + signals.@"previous-match".impl.emit(self, null, .{}, null); + } + + fn searchEntryKeyPressed( + _: *gtk.EventControllerKey, + keyval: c_uint, + _: c_uint, + gtk_mods: gdk.ModifierType, + self: *Self, + ) callconv(.c) c_int { + if (keyval == gdk.KEY_Return or keyval == gdk.KEY_KP_Enter) { + if (gtk_mods.shift_mask) { + signals.@"previous-match".impl.emit(self, null, .{}, null); + } else { + signals.@"next-match".impl.emit(self, null, .{}, null); + } + + return 1; + } + + return 0; + } + + fn onDragEnd( + _: *gtk.GestureDrag, + offset_x: f64, + offset_y: f64, + self: *Self, + ) callconv(.c) void { + // On drag end, we want to move our halign/valign if we crossed + // the midpoint on either axis. This lets the search overlay be + // moved to different corners of the parent container. + + const priv = self.private(); + const widget = self.as(gtk.Widget); + const parent = widget.getParent() orelse return; + + const parent_width: f64 = @floatFromInt(parent.getAllocatedWidth()); + const parent_height: f64 = @floatFromInt(parent.getAllocatedHeight()); + const self_width: f64 = @floatFromInt(widget.getAllocatedWidth()); + const self_height: f64 = @floatFromInt(widget.getAllocatedHeight()); + + const self_x: f64 = if (priv.halign_target == .start) 0 else parent_width - self_width; + const self_y: f64 = if (priv.valign_target == .start) 0 else parent_height - self_height; + + const new_x = self_x + offset_x + (self_width / 2); + const new_y = self_y + offset_y + (self_height / 2); + + const new_halign: gtk.Align = if (new_x > parent_width / 2) .end else .start; + const new_valign: gtk.Align = if (new_y > parent_height / 2) .end else .start; + + var changed = false; + if (new_halign != priv.halign_target) { + priv.halign_target = new_halign; + self.as(gobject.Object).notifyByPspec(properties.@"halign-target".impl.param_spec); + changed = true; + } + if (new_valign != priv.valign_target) { + priv.valign_target = new_valign; + self.as(gobject.Object).notifyByPspec(properties.@"valign-target".impl.param_spec); + changed = true; + } + + if (changed) self.as(gtk.Widget).queueResize(); + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + _ = priv; + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + _ = priv; + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 2, + .name = "search-overlay", + }), + ); + + // Bindings + class.bindTemplateChildPrivate("search_entry", .{}); + + // Template Callbacks + class.bindTemplateCallback("stop_search", &stopSearch); + class.bindTemplateCallback("search_changed", &searchChanged); + class.bindTemplateCallback("match_label_closure", &closureMatchLabel); + class.bindTemplateCallback("next_match", &nextMatch); + class.bindTemplateCallback("previous_match", &previousMatch); + class.bindTemplateCallback("search_entry_key_pressed", &searchEntryKeyPressed); + class.bindTemplateCallback("on_drag_end", &onDragEnd); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.active.impl, + properties.@"search-total".impl, + properties.@"has-search-total".impl, + properties.@"search-selected".impl, + properties.@"has-search-selected".impl, + properties.@"halign-target".impl, + properties.@"valign-target".impl, + }); + + // Signals + signals.@"stop-search".impl.register(.{}); + signals.@"search-changed".impl.register(.{}); + signals.@"next-match".impl.register(.{}); + signals.@"previous-match".impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/split_tree.zig b/vendor/libghostty-vt/src/apprt/gtk/class/split_tree.zig new file mode 100644 index 00000000..311fbd8a --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/split_tree.zig @@ -0,0 +1,1290 @@ +const std = @import("std"); +const assert = @import("../../../quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const adw = @import("adw"); +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const configpkg = @import("../../../config.zig"); +const apprt = @import("../../../apprt.zig"); +const ext = @import("../ext.zig"); +const gresource = @import("../build/gresource.zig"); +const Common = @import("../class.zig").Common; +const WeakRef = @import("../weak_ref.zig").WeakRef; +const Application = @import("application.zig").Application; +const CloseConfirmationDialog = @import("close_confirmation_dialog.zig").CloseConfirmationDialog; +const Surface = @import("surface.zig").Surface; +const SurfaceScrolledWindow = @import("surface_scrolled_window.zig").SurfaceScrolledWindow; + +const log = std.log.scoped(.gtk_ghostty_split_tree); + +pub const SplitTree = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = gtk.Box; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttySplitTree", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + /// The active surface is the surface that should be receiving all + /// surface-targeted actions. This is usually the focused surface, + /// but may also not be focused if the user has selected a non-surface + /// widget. + pub const @"active-surface" = struct { + pub const name = "active-surface"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*Surface, + .{ + .getter = getActiveSurface, + }, + ), + }, + ); + }; + + pub const @"has-surfaces" = struct { + pub const name = "has-surfaces"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ + .getter = getHasSurfaces, + }, + ), + }, + ); + }; + + pub const @"is-zoomed" = struct { + pub const name = "is-zoomed"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ + .getter = getIsZoomed, + }, + ), + }, + ); + }; + + pub const tree = struct { + pub const name = "tree"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface.Tree, + .{ + .accessor = .{ + .getter = getTreeValue, + .setter = setTreeValue, + }, + }, + ); + }; + + pub const @"is-split" = struct { + pub const name = "is-split"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ + .getter = getIsSplit, + }, + ), + }, + ); + }; + }; + + pub const signals = struct { + /// Emitted whenever the tree property has changed, with access + /// to the previous and new values. + pub const changed = struct { + pub const name = "changed"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{ ?*const Surface.Tree, ?*const Surface.Tree }, + void, + ); + }; + }; + + const Private = struct { + /// The tree datastructure containing all of our surface views. + tree: ?*Surface.Tree, + + // Template bindings + tree_bin: *adw.Bin, + + /// Last focused surface in the tree. We need this to handle various + /// tree change states. + last_focused: WeakRef(Surface) = .empty, + + /// The source that we use to rebuild the tree. This is also + /// used to debounce updates. + rebuild_source: ?c_uint = null, + + /// Used to store state about a pending surface close for the + /// close dialog. + pending_close: ?Surface.Tree.Node.Handle, + + pub var offset: c_int = 0; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + // Initialize our actions + self.initActionMap(); + + // Initialize some basic state + const priv = self.private(); + priv.pending_close = null; + } + + fn initActionMap(self: *Self) void { + const s_variant_type = glib.ext.VariantType.newFor([:0]const u8); + defer s_variant_type.free(); + + const actions = [_]ext.actions.Action(Self){ + // All of these will eventually take a target surface parameter. + // For now all our targets originate from the focused surface. + .init("new-split", actionNewSplit, s_variant_type), + .init("equalize", actionEqualize, null), + .init("zoom", actionZoom, null), + }; + + _ = ext.actions.addAsGroup(Self, self, "split-tree", &actions); + } + + /// Create a new split in the given direction from the currently + /// active surface. + /// + /// If the tree is empty this will create a new tree with a new surface + /// and ignore the direction. + /// + /// The parent will be used as the parent of the surface regardless of + /// if that parent is in this split tree or not. This allows inheriting + /// surface properties from anywhere. + pub fn newSplit( + self: *Self, + direction: Surface.Tree.Split.Direction, + parent_: ?*Surface, + overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }, + ) Allocator.Error!void { + const alloc = Application.default().allocator(); + + // Create our new surface. + const surface: *Surface = .new(.{ + .command = overrides.command, + .working_directory = overrides.working_directory, + .title = overrides.title, + }); + defer surface.unref(); + _ = surface.refSink(); + + // Inherit properly if we were asked to. + if (parent_) |p| { + if (p.core()) |core| { + surface.setParent(core, .split); + } + } + + // Bind is-split property for new surface + _ = self.as(gobject.Object).bindProperty( + "is-split", + surface.as(gobject.Object), + "is-split", + .{ .sync_create = true }, + ); + + // Create our tree + var single_tree = try Surface.Tree.init(alloc, surface); + defer single_tree.deinit(); + + // We want to move our focus to the new surface no matter what. + // But we need to be careful to restore state if we fail. + const old_last_focused = self.private().last_focused.get(); + defer if (old_last_focused) |v| v.unref(); // unref strong ref from get + self.private().last_focused.set(surface); + errdefer self.private().last_focused.set(old_last_focused); + + // If we have no tree yet, then this becomes our tree and we're done. + const old_tree = self.getTree() orelse { + self.setTree(&single_tree); + return; + }; + + // The handle we create the split relative to. Today this is the active + // surface but this might be the handle of the given parent if we want. + const handle = self.getActiveSurfaceHandle() orelse .root; + + // Create our split! + var new_tree = try old_tree.split( + alloc, + handle, + direction, + 0.5, // Always split equally for new splits + &single_tree, + ); + defer new_tree.deinit(); + log.debug( + "new split at={} direction={} old_tree={f} new_tree={f}", + .{ handle, direction, old_tree, &new_tree }, + ); + + // Replace our tree + self.setTree(&new_tree); + } + + pub fn resize( + self: *Self, + direction: Surface.Tree.Split.Direction, + amount: u16, + ) Allocator.Error!bool { + // Avoid useless work + if (amount == 0) return false; + + const old_tree = self.getTree() orelse return false; + const active = self.getActiveSurfaceHandle() orelse return false; + + // Get all our dimensions we're going to need to turn our + // amount into a percentage. + const priv = self.private(); + const width = priv.tree_bin.as(gtk.Widget).getWidth(); + const height = priv.tree_bin.as(gtk.Widget).getHeight(); + if (width == 0 or height == 0) return false; + const width_f64: f64 = @floatFromInt(width); + const height_f64: f64 = @floatFromInt(height); + const amount_f64: f64 = @floatFromInt(amount); + + // Get our ratio and use positive/neg for directions. + const ratio: f64 = switch (direction) { + .right => amount_f64 / width_f64, + .left => -(amount_f64 / width_f64), + .down => amount_f64 / height_f64, + .up => -(amount_f64 / height_f64), + }; + + const layout: Surface.Tree.Split.Layout = switch (direction) { + .left, .right => .horizontal, + .up, .down => .vertical, + }; + + var new_tree = try old_tree.resize( + Application.default().allocator(), + active, + layout, + @floatCast(ratio), + ); + defer new_tree.deinit(); + self.setTree(&new_tree); + return true; + } + + /// Move focus from the currently focused surface to the given + /// direction. Returns true if focus switched to a new surface. + pub fn goto(self: *Self, to: Surface.Tree.Goto) bool { + const tree = self.getTree() orelse return false; + const active = self.getActiveSurfaceHandle() orelse return false; + const target = if (tree.goto( + Application.default().allocator(), + active, + to, + )) |handle_| + handle_ orelse return false + else |err| switch (err) { + // Nothing we can do in this scenario. This is highly unlikely + // since split trees don't use that much memory. The application + // is probably about to crash in other ways. + error.OutOfMemory => return false, + }; + + // If we aren't changing targets then we did nothing. + if (active == target) return false; + + // Get the surface at the target location and grab focus. + const surface = tree.nodes[target.idx()].leaf; + surface.grabFocus(); + + // We also need to setup our last_focused to this because if we + // trigger a tree change like below, the grab focus above never + // actually triggers in time to set this and this ensures we + // grab focus to the right thing. + const old_last_focused = self.private().last_focused.get(); + defer if (old_last_focused) |v| v.unref(); // unref strong ref from get + self.private().last_focused.set(surface); + errdefer self.private().last_focused.set(old_last_focused); + + if (tree.zoomed != null) { + const app = Application.default(); + const config_obj = app.getConfig(); + defer config_obj.unref(); + const config = config_obj.get(); + + if (!config.@"split-preserve-zoom".navigation) { + tree.zoomed = null; + } else { + tree.zoom(target); + } + + // When the zoom state changes our tree state changes and + // we need to send the proper notifications to trigger + // relayout. + const object = self.as(gobject.Object); + object.notifyByPspec(properties.tree.impl.param_spec); + object.notifyByPspec(properties.@"is-zoomed".impl.param_spec); + } + + return true; + } + + fn disconnectSurfaceHandlers(self: *Self) void { + const tree = self.getTree() orelse return; + var it = tree.iterator(); + while (it.next()) |entry| { + const surface = entry.view; + _ = gobject.signalHandlersDisconnectMatched( + surface.as(gobject.Object), + .{ .data = true }, + 0, + 0, + null, + null, + self, + ); + } + } + + fn connectSurfaceHandlers(self: *Self) void { + const tree = self.getTree() orelse return; + var it = tree.iterator(); + while (it.next()) |entry| { + const surface = entry.view; + _ = Surface.signals.@"close-request".connect( + surface, + *Self, + surfaceCloseRequest, + self, + .{}, + ); + _ = gobject.Object.signals.notify.connect( + surface, + *Self, + propSurfaceFocused, + self, + .{ .detail = "focused" }, + ); + } + } + + //--------------------------------------------------------------- + // Properties + + /// Returns true if this split tree needs confirmation before quitting based + /// on the various Ghostty configurations. + pub fn getNeedsConfirmQuit(self: *Self) bool { + const tree = self.getTree() orelse return false; + var it = tree.iterator(); + while (it.next()) |entry| { + if (entry.view.core()) |core| { + if (core.needsConfirmQuit()) { + return true; + } + } + } + + return false; + } + + /// Get the currently active surface. See the "active-surface" property. + /// This does not ref the value. + pub fn getActiveSurface(self: *Self) ?*Surface { + const tree = self.getTree() orelse return null; + const handle = self.getActiveSurfaceHandle() orelse return null; + return tree.nodes[handle.idx()].leaf; + } + + fn getActiveSurfaceHandle(self: *Self) ?Surface.Tree.Node.Handle { + const tree = self.getTree() orelse return null; + var it = tree.iterator(); + while (it.next()) |entry| { + if (entry.view.getFocused()) return entry.handle; + } + + // If none are currently focused, the most previously focused + // surface (if it exists) is our active surface. This lets things + // like apprt actions and bell ringing continue to work in the + // background. + if (self.private().last_focused.get()) |v| { + defer v.unref(); + + // We need to find the handle of the last focused surface. + it = tree.iterator(); + while (it.next()) |entry| { + if (entry.view == v) return entry.handle; + } + } + + return null; + } + + /// Returns the last focused surface in the tree. + pub fn getLastFocusedSurface(self: *Self) ?*Surface { + const surface = self.private().last_focused.get() orelse return null; + // We unref because get() refs the surface. We don't use the weakref + // in a multi-threaded context so this is safe. + surface.unref(); + return surface; + } + + pub fn getHasSurfaces(self: *Self) bool { + const tree: *const Surface.Tree = self.private().tree orelse &.empty; + return !tree.isEmpty(); + } + + pub fn getIsZoomed(self: *Self) bool { + const tree: *const Surface.Tree = self.private().tree orelse &.empty; + return tree.zoomed != null; + } + + /// Get the tree data model that we're showing in this widget. This + /// does not clone the tree. + pub fn getTree(self: *Self) ?*Surface.Tree { + return self.private().tree; + } + + /// Set the tree data model that we're showing in this widget. This + /// will clone the given tree. + pub fn setTree(self: *Self, tree_: ?*const Surface.Tree) void { + const priv = self.private(); + + // We always normalize our tree parameter so that empty trees + // become null so that we don't have to deal with callers being + // confused about that. + const tree: ?*const Surface.Tree = tree: { + const tree = tree_ orelse break :tree null; + if (tree.isEmpty()) break :tree null; + break :tree tree; + }; + + // Emit the signal so that handlers can witness both the before and + // after values of the tree. + signals.changed.impl.emit( + self, + null, + .{ priv.tree, tree }, + null, + ); + + if (priv.tree) |old_tree| { + self.disconnectSurfaceHandlers(); + ext.boxedFree(Surface.Tree, old_tree); + priv.tree = null; + } + + if (tree) |new_tree| { + assert(priv.tree == null); + assert(!new_tree.isEmpty()); + priv.tree = ext.boxedCopy(Surface.Tree, new_tree); + self.connectSurfaceHandlers(); + } + + self.as(gobject.Object).notifyByPspec(properties.tree.impl.param_spec); + } + + fn getTreeValue(self: *Self, value: *gobject.Value) void { + gobject.ext.Value.set( + value, + self.private().tree, + ); + } + + fn setTreeValue(self: *Self, value: *const gobject.Value) void { + self.setTree(gobject.ext.Value.get( + value, + ?*Surface.Tree, + )); + } + + pub fn getIsSplit(self: *Self) bool { + const tree: *const Surface.Tree = self.private().tree orelse &.empty; + if (tree.isEmpty()) return false; + + const root_handle: Surface.Tree.Node.Handle = .root; + const root = tree.nodes[root_handle.idx()]; + return switch (root) { + .leaf => false, + .split => true, + }; + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + priv.last_focused.set(null); + if (priv.rebuild_source) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove rebuild source", .{}); + } + priv.rebuild_source = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.tree) |tree| { + ext.boxedFree(Surface.Tree, tree); + priv.tree = null; + } + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + // Signal handlers + + pub fn actionNewSplit( + _: *gio.SimpleAction, + args_: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const args = args_ orelse { + log.warn("split-tree.new-split called without a parameter", .{}); + return; + }; + + var dir: ?[*:0]const u8 = null; + args.get("&s", &dir); + + const direction = std.meta.stringToEnum( + Surface.Tree.Split.Direction, + std.mem.span(dir) orelse return, + ) orelse { + // Need to be defensive here since actions can be triggered externally. + log.warn("invalid split direction for split-tree.new-split: {s}", .{dir.?}); + return; + }; + + self.newSplit( + direction, + self.getActiveSurface(), + .none, + ) catch |err| { + log.warn("new split failed error={}", .{err}); + }; + } + + pub fn actionEqualize( + _: *gio.SimpleAction, + parameter_: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + _ = parameter_; + + const old_tree = self.getTree() orelse return; + var new_tree = old_tree.equalize(Application.default().allocator()) catch |err| { + log.warn("unable to equalize tree: {}", .{err}); + return; + }; + defer new_tree.deinit(); + self.setTree(&new_tree); + } + + pub fn actionZoom( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const tree = self.getTree() orelse return; + if (tree.zoomed != null) { + tree.zoomed = null; + } else { + const active = self.getActiveSurfaceHandle() orelse return; + if (tree.zoomed == active) return; + tree.zoom(active); + } + + self.as(gobject.Object).notifyByPspec(properties.tree.impl.param_spec); + } + + fn surfaceCloseRequest( + surface: *Surface, + self: *Self, + ) callconv(.c) void { + const core = surface.core() orelse return; + + // Reset our pending close state + const priv = self.private(); + priv.pending_close = null; + + // Find the surface in the tree to verify this is valid and + // set our pending close handle. + priv.pending_close = handle: { + const tree = self.getTree() orelse return; + var it = tree.iterator(); + while (it.next()) |entry| { + if (entry.view == surface) { + break :handle entry.handle; + } + } + + return; + }; + + // If we don't need to confirm then just close immediately. + if (!core.needsConfirmQuit()) { + closeConfirmationClose( + null, + self, + ); + return; + } + + // Show a confirmation dialog + const dialog: *CloseConfirmationDialog = .new(.surface); + _ = CloseConfirmationDialog.signals.@"close-request".connect( + dialog, + *Self, + closeConfirmationClose, + self, + .{}, + ); + dialog.present(self.as(gtk.Widget)); + } + + fn closeConfirmationClose( + _: ?*CloseConfirmationDialog, + self: *Self, + ) callconv(.c) void { + // Get the handle we're closing + const priv = self.private(); + const handle = priv.pending_close orelse return; + priv.pending_close = null; + + // Figure out our next focus target. The next focus target is + // always the "previous" surface unless we're the leftmost then + // its the next. + const old_tree = self.getTree() orelse return; + const next_focus: ?*Surface = next_focus: { + const alloc = Application.default().allocator(); + const next_handle: Surface.Tree.Node.Handle = + (old_tree.goto(alloc, handle, .previous) catch null) orelse + (old_tree.goto(alloc, handle, .next) catch null) orelse + break :next_focus null; + if (next_handle == handle) break :next_focus null; + + // Note: we don't need to ref this or anything because its + // guaranteed to remain in the new tree since its not part + // of the handle we're removing. + break :next_focus old_tree.nodes[next_handle.idx()].leaf; + }; + + // Remove it from the tree. + var new_tree = old_tree.remove( + Application.default().allocator(), + handle, + ) catch |err| { + log.warn("unable to remove surface from tree: {}", .{err}); + return; + }; + defer new_tree.deinit(); + self.setTree(&new_tree); + + // Grab focus. We have to set this on the "last focused" because our + // focus will be set when the tree is redrawn. + if (next_focus) |v| priv.last_focused.set(v); + } + + fn propSurfaceFocused( + surface: *Surface, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // We never CLEAR our last_focused because the property is specifically + // the last focused surface. We let the weakref clear itself when + // the surface is destroyed. + if (!surface.getFocused()) return; + self.private().last_focused.set(surface); + + // Our active surface probably changed + self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec); + } + + fn propTree( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + + // No matter what we notify + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + self.as(gobject.Object).notifyByPspec(properties.@"has-surfaces".impl.param_spec); + self.as(gobject.Object).notifyByPspec(properties.@"is-zoomed".impl.param_spec); + + // If we were planning a rebuild, always remove that so we can + // start from a clean slate. + if (priv.rebuild_source) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove rebuild source", .{}); + } + priv.rebuild_source = null; + } + + // If we transitioned to an empty tree, clear immediately instead of + // waiting for an idle callback. Delaying teardown can keep the last + // surface alive during shutdown if the main loop exits first. + if (priv.tree == null) { + priv.tree_bin.setChild(null); + return; + } + + // Build on an idle callback so rapid tree changes are debounced. + // We keep the existing tree attached until the rebuild runs, + // which avoids transient empty frames. + assert(priv.rebuild_source == null); + priv.rebuild_source = glib.idleAdd( + onRebuild, + self, + ); + } + + fn onRebuild(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + + // Always mark our rebuild source as null since we're done. + const priv = self.private(); + priv.rebuild_source = null; + + // Rebuild our tree + const tree: *const Surface.Tree = self.private().tree orelse &.empty; + if (tree.isEmpty()) { + priv.tree_bin.setChild(null); + } else { + const built = self.buildTree( + tree, + tree.zoomed orelse .root, + ); + defer built.deinit(); + priv.tree_bin.setChild(built.widget); + } + + // Replacing our tree widget hierarchy can reset focus state. + // If we have a last-focused surface, restore focus to it. + if (priv.last_focused.get()) |v| { + defer v.unref(); + v.grabFocus(); + } + + // Our split status may have changed + self.as(gobject.Object).notifyByPspec(properties.@"is-split".impl.param_spec); + + // Our active surface may have changed + self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec); + + return 0; + } + + /// Builds the widget tree associated with a surface split tree. + /// + /// Returned widgets are expected to be attached to a parent by the caller. + /// + /// If `release_ref` is true then `widget` has an extra temporary + /// reference that must be released once it is parented in the rebuilt + /// tree. + const BuildTreeResult = struct { + widget: *gtk.Widget, + release_ref: bool, + + pub fn initNew(widget: *gtk.Widget) BuildTreeResult { + return .{ .widget = widget, .release_ref = false }; + } + + pub fn initReused(widget: *gtk.Widget) BuildTreeResult { + // We add a temporary ref to the widget to ensure it doesn't + // get destroyed while we're rebuilding the tree and detaching + // it from its old parent. The caller is expected to release + // this ref once the widget is attached to its new parent. + _ = widget.as(gobject.Object).ref(); + + // Detach after we ref it so that this doesn't mark the + // widget for destruction. + detachWidget(widget); + + return .{ .widget = widget, .release_ref = true }; + } + + pub fn deinit(self: BuildTreeResult) void { + // If we have to release a ref, do it. + if (self.release_ref) self.widget.as(gobject.Object).unref(); + } + }; + + fn buildTree( + self: *Self, + tree: *const Surface.Tree, + current: Surface.Tree.Node.Handle, + ) BuildTreeResult { + return switch (tree.nodes[current.idx()]) { + .leaf => |v| leaf: { + const window = ext.getAncestor( + SurfaceScrolledWindow, + v.as(gtk.Widget), + ) orelse { + // The surface isn't in a window already so we don't + // have to worry about reuse. + break :leaf .initNew(gobject.ext.newInstance( + SurfaceScrolledWindow, + .{ .surface = v }, + ).as(gtk.Widget)); + }; + + // Keep this widget alive while we detach it from the + // old tree and adopt it into the new one. + break :leaf .initReused(window.as(gtk.Widget)); + }, + .split => |s| split: { + const left = self.buildTree(tree, s.left); + defer left.deinit(); + const right = self.buildTree(tree, s.right); + defer right.deinit(); + + break :split .initNew(SplitTreeSplit.new( + current, + &s, + left.widget, + right.widget, + ).as(gtk.Widget)); + }, + }; + } + + /// Detach a split widget from its current parent. + /// + /// We intentionally use parent-specific child APIs when possible + /// (`GtkPaned.setStartChild/setEndChild`, `AdwBin.setChild`) instead of + /// calling `gtk.Widget.unparent` directly. Container implementations track + /// child pointers/properties internally, and those setters are the path + /// that keeps container state and notifications in sync. + fn detachWidget(widget: *gtk.Widget) void { + const parent = widget.getParent() orelse return; + + // Surface will be in a paned when it is split. + if (gobject.ext.cast(gtk.Paned, parent)) |paned| { + if (paned.getStartChild()) |child| { + if (child == widget) { + paned.setStartChild(null); + return; + } + } + + if (paned.getEndChild()) |child| { + if (child == widget) { + paned.setEndChild(null); + return; + } + } + } + + // Surface will be in a bin when it is not split. + if (gobject.ext.cast(adw.Bin, parent)) |bin| { + if (bin.getChild()) |child| { + if (child == widget) { + bin.setChild(null); + return; + } + } + } + + // Fallback for unexpected parents where we don't have a typed + // container API available. + widget.unparent(); + } + + //--------------------------------------------------------------- + // Class + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(Surface); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "split-tree", + }), + ); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.@"active-surface".impl, + properties.@"has-surfaces".impl, + properties.@"is-zoomed".impl, + properties.tree.impl, + properties.@"is-split".impl, + }); + + // Bindings + class.bindTemplateChildPrivate("tree_bin", .{}); + + // Template Callbacks + class.bindTemplateCallback("notify_tree", &propTree); + + // Signals + signals.changed.impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; + +/// This is an internal-only widget that represents a split in the +/// split tree. This is a wrapper around gtk.Paned that allows us to handle +/// ratio (0 to 1) based positioning of the split, and also allows us to +/// write back the updated ratio to the split tree when the user manually +/// adjusts the split position. +/// +/// Since this is internal, it expects to be nested within a SplitTree and +/// will use `getAncestor` to find the SplitTree it belongs to. +/// +/// This is an _immutable_ widget. It isn't meant to be updated after +/// creation. As such, there are no properties or APIs to change the split, +/// access the paned, etc. +const SplitTreeSplit = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttySplitTreeSplit", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + const Private = struct { + /// The handle of the node in the tree that this split represents. + /// Assumed to be correct. + handle: Surface.Tree.Node.Handle, + + /// Source to handle repositioning the split when properties change. + idle: ?c_uint = null, + + // Template bindings + paned: *gtk.Paned, + + pub var offset: c_int = 0; + }; + + /// Create a new split. + /// + /// The reason we don't use GObject properties here is because this is + /// an immutable widget and we don't want to deal with the overhead of + /// all the boilerplate for properties, signals, bindings, etc. + pub fn new( + handle: Surface.Tree.Node.Handle, + split: *const Surface.Tree.Split, + start_child: *gtk.Widget, + end_child: *gtk.Widget, + ) *Self { + const self = gobject.ext.newInstance(Self, .{}); + const priv = self.private(); + priv.handle = handle; + + // Setup our paned fields + const paned = priv.paned; + paned.setStartChild(start_child); + paned.setEndChild(end_child); + paned.as(gtk.Orientable).setOrientation(switch (split.layout) { + .horizontal => .horizontal, + .vertical => .vertical, + }); + + // Signals and so on are setup in the template. + + return self; + } + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + fn refresh(self: *Self) void { + const priv = self.private(); + if (priv.idle == null) priv.idle = glib.idleAdd( + onIdle, + self, + ); + } + + fn onIdle(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + const priv = self.private(); + const paned = priv.paned; + + // Our idle source is always over + priv.idle = null; + + // Get our split. This is the most dangerous part of this entire + // widget. We assume that this widget is always a child of a + // SplitTree, we assume that our handle is valid, and we assume + // the handle is always a split node. + const split_tree = ext.getAncestor( + SplitTree, + self.as(gtk.Widget), + ) orelse return 0; + const tree = split_tree.getTree() orelse return 0; + const split: *const Surface.Tree.Split = &tree.nodes[priv.handle.idx()].split; + + // Current, min, and max positions as pixels. + const pos = paned.getPosition(); + const min = min: { + var val = gobject.ext.Value.new(c_int); + defer val.unset(); + gobject.Object.getProperty( + paned.as(gobject.Object), + "min-position", + &val, + ); + break :min gobject.ext.Value.get(&val, c_int); + }; + const max = max: { + var val = gobject.ext.Value.new(c_int); + defer val.unset(); + gobject.Object.getProperty( + paned.as(gobject.Object), + "max-position", + &val, + ); + break :max gobject.ext.Value.get(&val, c_int); + }; + const pos_set: bool = max: { + var val = gobject.ext.Value.new(c_int); + defer val.unset(); + gobject.Object.getProperty( + paned.as(gobject.Object), + "position-set", + &val, + ); + break :max gobject.ext.Value.get(&val, c_int) != 0; + }; + + // We don't actually use min, but we don't expect this to ever + // be non-zero, so let's add an assert to ensure that. + assert(min == 0); + + // If our max is zero then we can't do any math. I don't know + // if this is possible but I suspect it can be if you make a nested + // split completely minimized. + if (max == 0) return 0; + + // Determine our current ratio. + const current_ratio: f64 = ratio: { + const pos_f64: f64 = @floatFromInt(pos); + const max_f64: f64 = @floatFromInt(max); + break :ratio pos_f64 / max_f64; + }; + const desired_ratio: f64 = @floatCast(split.ratio); + + // If our ratio is close enough to our desired ratio, then + // we ignore the update. This is to avoid constant split updates + // for lossy floating point math. + if (std.math.approxEqAbs( + f64, + current_ratio, + desired_ratio, + 0.001, + )) { + return 0; + } + + // If we're out of bounds, then we need to either set the position + // to what we expect OR update our expected ratio. + + // If we've never set the position, then we set it to the desired. + if (!pos_set) { + const desired_pos: c_int = desired_pos: { + const max_f64: f64 = @floatFromInt(max); + break :desired_pos @intFromFloat(@round(max_f64 * desired_ratio)); + }; + paned.setPosition(desired_pos); + return 0; + } + + // If we've set the position, then this is a manual human update + // and we need to write our update back to the tree. + tree.resizeInPlace(priv.handle, @floatCast(current_ratio)); + + return 0; + } + + //--------------------------------------------------------------- + // Signal handlers + + fn propPosition( + _: *gtk.Paned, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.refresh(); + } + + fn propMaxPosition( + _: *gtk.Paned, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.refresh(); + } + + fn propMinPosition( + _: *gtk.Paned, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.refresh(); + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.idle) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove idle source", .{}); + } + priv.idle = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "split-tree-split", + }), + ); + + // Bindings + class.bindTemplateChildPrivate("paned", .{}); + + // Template Callbacks + class.bindTemplateCallback("notify_max_position", &propMaxPosition); + class.bindTemplateCallback("notify_min_position", &propMinPosition); + class.bindTemplateCallback("notify_position", &propPosition); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/surface.zig b/vendor/libghostty-vt/src/apprt/gtk/class/surface.zig new file mode 100644 index 00000000..179c779d --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/surface.zig @@ -0,0 +1,4049 @@ +const std = @import("std"); +const assert = @import("../../../quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const adw = @import("adw"); +const gdk = @import("gdk"); +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const apprt = @import("../../../apprt.zig"); +const build_config = @import("../../../build_config.zig"); +const configpkg = @import("../../../config.zig"); +const datastruct = @import("../../../datastruct/main.zig"); +const font = @import("../../../font/main.zig"); +const input = @import("../../../input.zig"); +const internal_os = @import("../../../os/main.zig"); +const renderer = @import("../../../renderer.zig"); +const terminal = @import("../../../terminal/main.zig"); +const CoreSurface = @import("../../../Surface.zig"); +const gresource = @import("../build/gresource.zig"); +const ext = @import("../ext.zig"); +const gsettings = @import("../gsettings.zig"); +const gtk_key = @import("../key.zig"); +const ApprtSurface = @import("../Surface.zig"); +const Common = @import("../class.zig").Common; +const Application = @import("application.zig").Application; +const Config = @import("config.zig").Config; +const ResizeOverlay = @import("resize_overlay.zig").ResizeOverlay; +const SearchOverlay = @import("search_overlay.zig").SearchOverlay; +const KeyStateOverlay = @import("key_state_overlay.zig").KeyStateOverlay; +const ChildExited = @import("surface_child_exited.zig").SurfaceChildExited; +const ClipboardConfirmationDialog = @import("clipboard_confirmation_dialog.zig").ClipboardConfirmationDialog; +const TitleDialog = @import("title_dialog.zig").TitleDialog; +const Window = @import("window.zig").Window; +const InspectorWindow = @import("inspector_window.zig").InspectorWindow; +const i18n = @import("../../../os/i18n.zig"); +const media = @import("../media.zig"); + +const log = std.log.scoped(.gtk_ghostty_surface); + +pub const Surface = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const Implements = [_]type{gtk.Scrollable}; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttySurface", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + .implements = &.{ + gobject.ext.implement(gtk.Scrollable, .{}), + }, + }); + + /// A SplitTree implementation that stores surfaces. + pub const Tree = datastruct.SplitTree(Self); + + pub const properties = struct { + /// This property is set to true when the bell is ringing. Note that + /// this property will only emit a changed signal when there is a + /// full state change. If a bell is ringing and another bell event + /// comes through, the change notification will NOT be emitted. + /// + /// If you need to know every scenario the bell is triggered, + /// listen to the `bell` signal instead. + pub const @"bell-ringing" = struct { + pub const name = "bell-ringing"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = C.privateShallowFieldAccessor("bell_ringing"), + }, + ); + }; + + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Config, + .{ + .accessor = C.privateObjFieldAccessor("config"), + }, + ); + }; + + pub const @"child-exited" = struct { + pub const name = "child-exited"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "child_exited", + ), + }, + ); + }; + + pub const @"default-size" = struct { + pub const name = "default-size"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Size, + .{ + .accessor = C.privateBoxedFieldAccessor("default_size"), + }, + ); + }; + + pub const @"error" = struct { + pub const name = "error"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "error", + ), + }, + ); + }; + + pub const @"font-size-request" = struct { + pub const name = "font-size-request"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*font.face.DesiredSize, + .{ + .accessor = C.privateBoxedFieldAccessor("font_size_request"), + }, + ); + }; + + pub const focused = struct { + pub const name = "focused"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "focused", + ), + }, + ); + }; + + pub const @"min-size" = struct { + pub const name = "min-size"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Size, + .{ + .accessor = C.privateBoxedFieldAccessor("min_size"), + }, + ); + }; + + pub const @"mouse-hidden" = struct { + pub const name = "mouse-hidden"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ + .getter = getMouseHidden, + .setter = setMouseHidden, + }, + ), + }, + ); + }; + + pub const @"mouse-shape" = struct { + pub const name = "mouse-shape"; + const impl = gobject.ext.defineProperty( + name, + Self, + terminal.MouseShape, + .{ + .default = .text, + .accessor = gobject.ext.typedAccessor( + Self, + terminal.MouseShape, + .{ + .getter = getMouseShape, + .setter = setMouseShape, + }, + ), + }, + ); + }; + + pub const @"mouse-hover-url" = struct { + pub const name = "mouse-hover-url"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("mouse_hover_url"), + }, + ); + }; + + pub const pwd = struct { + pub const name = "pwd"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("pwd"), + }, + ); + }; + + pub const title = struct { + pub const name = "title"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("title"), + }, + ); + }; + + pub const @"title-override" = struct { + pub const name = "title-override"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("title_override"), + }, + ); + }; + + pub const zoom = struct { + pub const name = "zoom"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "zoom", + ), + }, + ); + }; + + pub const @"is-split" = struct { + pub const name = "is-split"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "is_split", + ), + }, + ); + }; + + pub const hadjustment = struct { + pub const name = "hadjustment"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*gtk.Adjustment, + .{ + .accessor = .{ + .getter = getHAdjustmentValue, + .setter = setHAdjustmentValue, + }, + }, + ); + }; + + pub const vadjustment = struct { + pub const name = "vadjustment"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*gtk.Adjustment, + .{ + .accessor = .{ + .getter = getVAdjustmentValue, + .setter = setVAdjustmentValue, + }, + }, + ); + }; + + pub const @"hscroll-policy" = struct { + pub const name = "hscroll-policy"; + const impl = gobject.ext.defineProperty( + name, + Self, + gtk.ScrollablePolicy, + .{ + .default = .natural, + .accessor = C.privateShallowFieldAccessor("hscroll_policy"), + }, + ); + }; + + pub const @"vscroll-policy" = struct { + pub const name = "vscroll-policy"; + const impl = gobject.ext.defineProperty( + name, + Self, + gtk.ScrollablePolicy, + .{ + .default = .natural, + .accessor = C.privateShallowFieldAccessor("vscroll_policy"), + }, + ); + }; + + pub const @"key-sequence" = struct { + pub const name = "key-sequence"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*ext.StringList, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*ext.StringList, + .{ + .getter = getKeySequence, + .getter_transfer = .full, + }, + ), + }, + ); + }; + + pub const @"key-table" = struct { + pub const name = "key-table"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*ext.StringList, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*ext.StringList, + .{ + .getter = getKeyTable, + .getter_transfer = .full, + }, + ), + }, + ); + }; + + pub const readonly = struct { + pub const name = "readonly"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = false, + .accessor = gobject.ext.typedAccessor( + Self, + bool, + .{ + .getter = getReadonly, + }, + ), + }, + ); + }; + }; + + pub const signals = struct { + /// Emitted whenever the bell event is received. Unlike the + /// `bell-ringing` property, this is emitted every time the event + /// is received and not just on state changes. + pub const bell = struct { + pub const name = "bell"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + /// Emitted whenever the surface would like to be closed for any + /// reason. + /// + /// The surface view does NOT handle its own close confirmation. + /// If there is a process alive then the boolean parameter will + /// specify it and the parent widget should handle this request. + /// + /// This signal lets the containing widget decide how closure works. + /// This lets this Surface widget be used as a split, tab, etc. + /// without it having to be aware of its own semantics. + pub const @"close-request" = struct { + pub const name = "close-request"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted whenever the clipboard has been written. + pub const @"clipboard-write" = struct { + pub const name = "clipboard-write"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{ + apprt.Clipboard, + [*:0]const u8, + }, + void, + ); + }; + + /// Emitted whenever the surface reads the clipboard. + pub const @"clipboard-read" = struct { + pub const name = "clipboard-read"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted after the surface is initialized. + pub const init = struct { + pub const name = "init"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted just prior to the context menu appearing. + pub const menu = struct { + pub const name = "menu"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted when the focus wants to be brought to the top and + /// focused. + pub const @"present-request" = struct { + pub const name = "present-request"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted when this surface requests its container to toggle its + /// fullscreen state. + pub const @"toggle-fullscreen" = struct { + pub const name = "toggle-fullscreen"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + + /// Emitted when this surface requests its container to toggle its + /// maximized state. + pub const @"toggle-maximize" = struct { + pub const name = "toggle-maximize"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + + const Private = struct { + /// The configuration that this surface is using. + config: ?*Config = null, + + /// The default size for a window that embeds this surface. + default_size: ?*Size = null, + + /// The minimum size for this surface. Embedders enforce this, + /// not the surface itself. + min_size: ?*Size = null, + + /// The requested font size. This only applies to initialization + /// and has no effect later. + font_size_request: ?*font.face.DesiredSize = null, + + /// The mouse shape to show for the surface. + mouse_shape: terminal.MouseShape = .default, + + /// Whether the mouse should be hidden or not as requested externally. + mouse_hidden: bool = false, + + /// The URL that the mouse is currently hovering over. + mouse_hover_url: ?[:0]const u8 = null, + + /// The current working directory. This has to be reported externally, + /// usually by shell integration which then talks to libghostty + /// which triggers this property. + /// + /// If this is set prior to initialization then the surface will + /// start in this pwd. If it is set after, it has no impact on the + /// core surface. + pwd: ?[:0]const u8 = null, + + /// The title of this surface, if any has been set. + title: ?[:0]const u8 = null, + + /// The manually overridden title of this surface from `promptTitle`. + title_override: ?[:0]const u8 = null, + + /// The current focus state of the terminal based on the + /// focus events. + focused: bool = true, + + /// Whether this surface is "zoomed" or not. A zoomed surface + /// shows up taking the full bounds of a split view. + zoom: bool = false, + + /// The GLAarea that renders the actual surface. This is a binding + /// to the template so it doesn't have to be unrefed manually. + gl_area: *gtk.GLArea, + + /// The labels for the left/right sides of the URL hover tooltip. + url_left: *gtk.Label, + url_right: *gtk.Label, + + /// The resize overlay + resize_overlay: *ResizeOverlay, + + /// The search overlay + search_overlay: *SearchOverlay, + + /// The key state overlay + key_state_overlay: *KeyStateOverlay, + + /// The apprt Surface. + rt_surface: ApprtSurface = undefined, + + /// The core surface backing this GTK surface. This starts out + /// null because it can't be initialized until there is an available + /// GLArea that is realized. + // + // NOTE(mitchellh): This is a limitation we should definitely remove + // at some point by modifying our OpenGL renderer for GTK to + // start in an unrealized state. There are other benefits to being + // able to initialize the surface early so we should aim for that, + // eventually. + core_surface: ?*CoreSurface = null, + + /// Cached metrics for libghostty callbacks + size: apprt.SurfaceSize, + cursor_pos: apprt.CursorPos, + + /// Various input method state. All related to key input. + in_keyevent: IMKeyEvent = .false, + im_context: *gtk.IMMulticontext, + im_composing: bool = false, + im_buf: [128]u8 = undefined, + im_len: u7 = 0, + + /// True when we have a precision scroll in progress + precision_scroll: bool = false, + + /// True when the child has exited. + child_exited: bool = false, + + // Progress bar + progress_bar_timer: ?c_uint = null, + + // True while the bell is ringing. This will be set to false (after + // true) under various scenarios, but can also manually be set to + // false by a parent widget. + bell_ringing: bool = false, + + /// True if this surface is in an error state. This is currently + /// a simple boolean with no additional information on WHAT the + /// error state is, because we don't yet need it or use it. For now, + /// if this is true, then it means the terminal is non-functional. + @"error": bool = false, + + /// The source that handles setting our child property. + idle_rechild: ?c_uint = null, + + /// A weak reference to an inspector window. + inspector: ?*InspectorWindow = null, + + // True if the current surface is a split, this is used to apply + // unfocused-split-* options + is_split: bool = false, + + action_group: ?*gio.SimpleActionGroup = null, + + // Gtk.Scrollable interface adjustments + hadj: ?*gtk.Adjustment = null, + vadj: ?*gtk.Adjustment = null, + hscroll_policy: gtk.ScrollablePolicy = .natural, + vscroll_policy: gtk.ScrollablePolicy = .natural, + vadj_signal_group: ?*gobject.SignalGroup = null, + + // Key state tracking for key sequences and tables + key_sequence: std.ArrayListUnmanaged([:0]const u8) = .empty, + key_tables: std.ArrayListUnmanaged([:0]const u8) = .empty, + + // Template binds + child_exited_overlay: *ChildExited, + context_menu: *gtk.PopoverMenu, + drop_target: *gtk.DropTarget, + progress_bar_overlay: *gtk.ProgressBar, + error_page: *adw.StatusPage, + terminal_page: *gtk.Overlay, + + /// The context for this surface (window, tab, or split) + context: apprt.surface.NewSurfaceContext = .window, + + /// Whether primary paste (middle-click paste) is enabled. + gtk_enable_primary_paste: bool = true, + + /// True when a left mouse down was consumed purely for a focus change, + /// and the matching left mouse release should also be suppressed. + suppress_left_mouse_release: bool = false, + + /// How much pending horizontal scroll do we have? + pending_horizontal_scroll: f64 = 0.0, + + /// Timer to reset the amount of horizontal scroll if the user + /// stops scrolling. + pending_horizontal_scroll_reset: ?c_uint = null, + + overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + } = .none, + + pub var offset: c_int = 0; + }; + + pub fn new(overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }) *Self { + const self = gobject.ext.newInstance(Self, .{ + .@"title-override" = overrides.title, + }); + const alloc = Application.default().allocator(); + const priv: *Private = self.private(); + priv.overrides = .{ + .command = if (overrides.command) |c| c.clone(alloc) catch null else null, + .working_directory = if (overrides.working_directory) |wd| alloc.dupeZ(u8, wd) catch null else null, + }; + return self; + } + + pub fn core(self: *Self) ?*CoreSurface { + const priv = self.private(); + return priv.core_surface; + } + + pub fn rt(self: *Self) *ApprtSurface { + const priv = self.private(); + return &priv.rt_surface; + } + + /// Set the parent of this surface. This will extract the information + /// required to initialize this surface with the proper values but doesn't + /// retain any memory. + /// + /// If the surface is already realized this does nothing. + pub fn setParent( + self: *Self, + parent: *CoreSurface, + context: apprt.surface.NewSurfaceContext, + ) void { + const priv = self.private(); + + // This is a mistake! We can only set a parent before surface + // realization. We log this because this is probably a logic error. + if (priv.core_surface != null) { + log.warn("setParent called after surface is already realized", .{}); + return; + } + + // Store the context so initSurface can use it + priv.context = context; + + // Setup our font size + const font_size_ptr = glib.ext.create(font.face.DesiredSize); + errdefer glib.ext.destroy(font_size_ptr); + font_size_ptr.* = parent.font_size; + priv.font_size_request = font_size_ptr; + self.as(gobject.Object).notifyByPspec(properties.@"font-size-request".impl.param_spec); + + // Remainder needs a config. If there is no config we just assume + // we aren't inheriting any of these values. + if (priv.config) |config_obj| { + // Setup our cwd if configured to inherit + if (apprt.surface.shouldInheritWorkingDirectory(context, config_obj.get())) { + if (parent.rt_surface.surface.getPwd()) |pwd| { + priv.pwd = glib.ext.dupeZ(u8, pwd); + self.as(gobject.Object).notifyByPspec(properties.pwd.impl.param_spec); + } + } + } + } + + /// Force the surface to redraw itself. Ghostty often will only redraw + /// the terminal in reaction to internal changes. If there are external + /// events that invalidate the surface, such as the widget moving parents, + /// then we should force a redraw. + pub fn redraw(self: *Self) void { + const priv = self.private(); + priv.gl_area.queueRender(); + } + + /// Callback used to determine whether border should be shown around the + /// surface. + fn closureShouldBorderBeShown( + _: *Self, + config_: ?*Config, + bell_ringing_: c_int, + ) callconv(.c) c_int { + const bell_ringing = bell_ringing_ != 0; + + // If the bell isn't ringing exit early because when the surface is + // first created there's a race between this code being run and the + // config being set on the surface. That way we don't overwhelm people + // with the warning that we issue if the config isn't set and overwhelm + // ourselves with large numbers of bug reports. + if (!bell_ringing) return @intFromBool(false); + + const config = if (config_) |v| v.get() else { + log.warn("config unavailable for computing whether border should be shown, likely bug", .{}); + return @intFromBool(false); + }; + + return @intFromBool(config.@"bell-features".border); + } + + /// Callback used to determine whether unfocused-split-fill / unfocused-split-opacity + /// should be applied to the surface + fn closureShouldUnfocusedSplitBeShown( + _: *Self, + search_active: c_int, + focused: c_int, + is_split: c_int, + ) callconv(.c) c_int { + return @intFromBool(search_active == 0 and focused == 0 and is_split != 0); + } + + pub fn toggleFullscreen(self: *Self) void { + signals.@"toggle-fullscreen".impl.emit( + self, + null, + .{}, + null, + ); + } + + pub fn toggleMaximize(self: *Self) void { + signals.@"toggle-maximize".impl.emit( + self, + null, + .{}, + null, + ); + } + + pub fn toggleCommandPalette(self: *Self) bool { + // TODO: pass the surface with the action + return self.as(gtk.Widget).activateAction("win.toggle-command-palette", null) != 0; + } + + pub fn controlInspector( + self: *Self, + value: apprt.Action.Value(.inspector), + ) bool { + // Let's see if we have an inspector already. + const priv = self.private(); + if (priv.inspector) |inspector| switch (value) { + .show => {}, + // Our weak ref will set our private value to null + .toggle, .hide => inspector.as(gtk.Window).destroy(), + } else switch (value) { + .toggle, .show => { + const inspector = InspectorWindow.new(self); + inspector.present(); + inspector.as(gobject.Object).weakRef(inspectorWeakNotify, self); + priv.inspector = inspector; + }, + + .hide => {}, + } + + return true; + } + + /// Redraw our inspector, if there is one associated with this surface. + pub fn redrawInspector(self: *Self) void { + const priv = self.private(); + if (priv.inspector) |v| v.queueRender(); + } + + /// Handle a key sequence action from the apprt. + pub fn keySequenceAction( + self: *Self, + value: apprt.action.KeySequence, + ) Allocator.Error!void { + const priv = self.private(); + const alloc = Application.default().allocator(); + + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + self.as(gobject.Object).notifyByPspec(properties.@"key-sequence".impl.param_spec); + + switch (value) { + .trigger => |trigger| { + // Convert the trigger to a human-readable label + var buf: std.Io.Writer.Allocating = .init(alloc); + defer buf.deinit(); + if (gtk_key.labelFromTrigger(&buf.writer, trigger)) |success| { + if (!success) return; + } else |_| return error.OutOfMemory; + + // Make space + try priv.key_sequence.ensureUnusedCapacity(alloc, 1); + + // Copy and append + const duped = try buf.toOwnedSliceSentinel(0); + errdefer alloc.free(duped); + priv.key_sequence.appendAssumeCapacity(duped); + }, + .end => { + // Free all the stored strings and clear + for (priv.key_sequence.items) |s| alloc.free(s); + priv.key_sequence.clearAndFree(alloc); + }, + } + } + + /// Handle a key table action from the apprt. + pub fn keyTableAction( + self: *Self, + value: apprt.action.KeyTable, + ) Allocator.Error!void { + const priv = self.private(); + const alloc = Application.default().allocator(); + + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + self.as(gobject.Object).notifyByPspec(properties.@"key-table".impl.param_spec); + + switch (value) { + .activate => |name| { + // Duplicate the name string and push onto stack + const duped = try alloc.dupeZ(u8, name); + errdefer alloc.free(duped); + try priv.key_tables.append(alloc, duped); + }, + .deactivate => { + // Pop and free the top table + if (priv.key_tables.pop()) |s| alloc.free(s); + }, + .deactivate_all => { + // Free all tables and clear + for (priv.key_tables.items) |s| alloc.free(s); + priv.key_tables.clearAndFree(alloc); + }, + } + } + + pub fn showOnScreenKeyboard(self: *Self, event: ?*gdk.Event) bool { + const priv = self.private(); + return priv.im_context.as(gtk.IMContext).activateOsk(event) != 0; + } + + /// Set the scrollbar state for this surface. This will setup the + /// properties for our Gtk.Scrollable interface properly. + pub fn setScrollbar(self: *Self, scrollbar: terminal.Scrollbar) void { + // Update existing adjustment in-place. If we don't have an + // adjustment then we do nothing because we're not part of a + // scrolled window. + const vadj = self.getVAdjustment() orelse return; + + // Check if values match existing adjustment and skip update if so + const value: f64 = @floatFromInt(scrollbar.offset); + const upper: f64 = @floatFromInt(scrollbar.total); + const page_size: f64 = @floatFromInt(scrollbar.len); + + if (std.math.approxEqAbs(f64, vadj.getValue(), value, 0.001) and + std.math.approxEqAbs(f64, vadj.getUpper(), upper, 0.001) and + std.math.approxEqAbs(f64, vadj.getPageSize(), page_size, 0.001)) + { + return; + } + + // If we have a vadjustment we MUST have the signal group since + // it is setup in the prop handler. + const priv = self.private(); + const group = priv.vadj_signal_group.?; + + // During manual scrollbar changes from Ghostty core we don't + // want to emit value-changed signals so we block them. This would + // cause a waste of resources at best and infinite loops at worst. + group.block(); + defer group.unblock(); + + vadj.configure( + value, // value: current scroll position + 0, // lower: minimum value + upper, // upper: maximum value (total scrollable area) + 1, // step_increment: amount to scroll on arrow click + page_size, // page_increment: amount to scroll on page up/down + page_size, // page_size: size of visible area + ); + } + + /// Set the current progress report state. + pub fn setProgressReport( + self: *Self, + value: terminal.osc.Command.ProgressReport, + ) void { + const priv = self.private(); + + // No matter what, we stop the timer because if we're removing + // then we're done and otherwise we restart it. + if (priv.progress_bar_timer) |timer| { + if (glib.Source.remove(timer) == 0) { + log.warn("unable to remove progress bar timer", .{}); + } + priv.progress_bar_timer = null; + } + + if (priv.config) |config| { + if (!config.get().@"progress-style") { + log.debug("progress_report action blocked by config", .{}); + priv.progress_bar_overlay.as(gtk.Widget).setVisible(@intFromBool(false)); + return; + } + } + + const progress_bar = priv.progress_bar_overlay; + switch (value.state) { + // Remove the progress bar + .remove => { + progress_bar.as(gtk.Widget).setVisible(@intFromBool(false)); + return; + }, + + // Set the progress bar to a fixed value if one was provided, otherwise pulse. + // Remove the `error` CSS class so that the progress bar shows as normal. + .set => { + progress_bar.as(gtk.Widget).removeCssClass("error"); + if (value.progress) |progress| { + progress_bar.setFraction(computeFraction(progress)); + } else { + progress_bar.pulse(); + } + }, + + // Set the progress bar to a fixed value if one was provided, otherwise pulse. + // Set the `error` CSS class so that the progress bar shows as an error color. + .@"error" => { + progress_bar.as(gtk.Widget).addCssClass("error"); + if (value.progress) |progress| { + progress_bar.setFraction(computeFraction(progress)); + } else { + progress_bar.pulse(); + } + }, + + // The state of progress is unknown, so pulse the progress bar to + // indicate that things are still happening. + .indeterminate => { + progress_bar.pulse(); + }, + + // If a progress value was provided, set the progress bar to that value. + // Don't pulse the progress bar as that would indicate that things were + // happening. Otherwise this is mainly used to keep the progress bar on + // screen instead of timing out. + .pause => { + if (value.progress) |progress| { + progress_bar.setFraction(computeFraction(progress)); + } + }, + } + + // Assume all states lead to visibility + assert(value.state != .remove); + progress_bar.as(gtk.Widget).setVisible(@intFromBool(true)); + + // Start our timer to remove bad actor programs that stall + // the progress bar. + const progress_bar_timeout_seconds = 15; + assert(priv.progress_bar_timer == null); + priv.progress_bar_timer = glib.timeoutAdd( + progress_bar_timeout_seconds * std.time.ms_per_s, + progressBarTimer, + self, + ); + } + + /// The progress bar hasn't been updated by the TUI recently, remove it. + fn progressBarTimer(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud.?)); + const priv = self.private(); + priv.progress_bar_timer = null; + self.setProgressReport(.{ .state = .remove }); + return @intFromBool(glib.SOURCE_REMOVE); + } + + /// Request that this terminal come to the front and become focused. + /// It is up to the embedding widget to react to this. + pub fn present(self: *Self) void { + signals.@"present-request".impl.emit( + self, + null, + .{}, + null, + ); + } + + pub fn commandFinished(self: *Self, value: apprt.Action.Value(.command_finished)) bool { + const app = Application.default(); + const alloc = app.allocator(); + const priv: *Private = self.private(); + + const notify_next_command_finish = notify: { + const simple_action_group = priv.action_group orelse break :notify false; + const action_group = simple_action_group.as(gio.ActionGroup); + const state = action_group.getActionState("notify-on-next-command-finish") orelse break :notify false; + const bool_variant_type = glib.ext.VariantType.newFor(bool); + defer bool_variant_type.free(); + if (state.isOfType(bool_variant_type) == 0) break :notify false; + const notify = state.getBoolean() != 0; + action_group.changeActionState("notify-on-next-command-finish", glib.Variant.newBoolean(@intFromBool(false))); + break :notify notify; + }; + + const config = priv.config orelse return false; + + const cfg = config.get(); + + if (!notify_next_command_finish) { + if (cfg.@"notify-on-command-finish" == .never) return true; + if (cfg.@"notify-on-command-finish" == .unfocused and self.getFocused()) return true; + } + + if (value.duration.lte(cfg.@"notify-on-command-finish-after")) return true; + + const action = cfg.@"notify-on-command-finish-action"; + + if (action.bell) self.setBellRinging(true); + + if (action.notify) notify: { + const title_ = title: { + const exit_code = value.exit_code orelse break :title i18n._("Command Finished"); + if (exit_code == 0) break :title i18n._("Command Succeeded"); + break :title i18n._("Command Failed"); + }; + const title = std.mem.span(title_); + const body = body: { + const exit_code = value.exit_code orelse break :body std.fmt.allocPrintSentinel( + alloc, + "Command took {f}.", + .{value.duration.round(std.time.ns_per_ms)}, + 0, + ) catch break :notify; + break :body std.fmt.allocPrintSentinel( + alloc, + "Command took {f} and exited with code {d}.", + .{ value.duration.round(std.time.ns_per_ms), exit_code }, + 0, + ) catch break :notify; + }; + defer alloc.free(body); + + self.sendDesktopNotification(title, body); + } + + return true; + } + + /// Get the readonly state from the core surface. + pub fn getReadonly(self: *Self) bool { + const priv: *Private = self.private(); + const surface = priv.core_surface orelse return false; + return surface.readonly; + } + + /// Notify anyone interested that the readonly status has changed. + pub fn setReadonly(self: *Self, _: apprt.Action.Value(.readonly)) bool { + self.as(gobject.Object).notifyByPspec(properties.readonly.impl.param_spec); + + return true; + } + + /// Key press event (press or release). + /// + /// At a high level, we want to construct an `input.KeyEvent` and + /// pass that to `keyCallback`. At a low level, this is more complicated + /// than it appears because we need to construct all of this information + /// and its not given to us. + /// + /// For all events, we run the GdkEvent through the input method context. + /// This allows the input method to capture the event and trigger + /// callbacks such as preedit, commit, etc. + /// + /// There are a couple important aspects to the prior paragraph: we must + /// send ALL events through the input method context. This is because + /// input methods use both key press and key release events to determine + /// the state of the input method. For example, fcitx uses key release + /// events on modifiers (i.e. ctrl+shift) to switch the input method. + /// + /// We set some state to note we're in a key event (self.in_keyevent) + /// because some of the input method callbacks change behavior based on + /// this state. For example, we don't want to send character events + /// like "a" via the input "commit" event if we're actively processing + /// a keypress because we'd lose access to the keycode information. + /// However, a "commit" event may still happen outside of a keypress + /// event from e.g. a tablet or on-screen keyboard. + /// + /// Finally, we take all of the information in order to determine if we have + /// a unicode character or if we have to map the keyval to a code to + /// get the underlying logical key, etc. + /// + /// Then we can emit the keyCallback. + pub fn keyEvent( + self: *Surface, + action: input.Action, + ec_key: *gtk.EventControllerKey, + keyval: c_uint, + keycode: c_uint, + gtk_mods: gdk.ModifierType, + ) bool { + //log.warn("keyEvent action={}", .{action}); + const event = ec_key.as(gtk.EventController).getCurrentEvent() orelse return false; + const key_event = gobject.ext.cast(gdk.KeyEvent, event) orelse return false; + const priv = self.private(); + + // The block below is all related to input method handling. See the function + // comment for some high level details and then the comments within + // the block for more specifics. + { + // This can trigger an input method so we need to notify the im context + // where the cursor is so it can render the dropdowns in the correct + // place. + if (priv.core_surface) |surface| { + const ime_point = surface.imePoint(); + priv.im_context.as(gtk.IMContext).setCursorLocation(&.{ + .f_x = @intFromFloat(ime_point.x), + .f_y = @intFromFloat(ime_point.y), + .f_width = 1, + .f_height = 1, + }); + } + + // We note that we're in a keypress because we want some logic to + // depend on this. For example, we don't want to send character events + // like "a" via the input "commit" event if we're actively processing + // a keypress because we'd lose access to the keycode information. + // + // We have to maintain some additional state here of whether we + // were composing because different input methods call the callbacks + // in different orders. For example, ibus calls commit THEN preedit + // end but simple calls preedit end THEN commit. + priv.in_keyevent = if (priv.im_composing) .composing else .not_composing; + defer priv.in_keyevent = .false; + + // Pass the event through the input method which returns true if handled. + // Confusingly, not all events handled by the input method result + // in this returning true so we have to maintain some additional + // state about whether we were composing or not to determine if + // we should proceed with key encoding. + // + // Cases where the input method does not mark the event as handled: + // + // - If we change the input method via keypress while we have preedit + // text, the input method will commit the pending text but will not + // mark it as handled. We use the `.composing` state to detect + // this case. + // + // - If we switch input methods (i.e. via ctrl+shift with fcitx), + // the input method will handle the key release event but will not + // mark it as handled. I don't know any way to detect this case so + // it will result in a key event being sent to the key callback. + // For Kitty text encoding, this will result in modifiers being + // triggered despite being technically consumed. At the time of + // writing, both Kitty and Alacritty have the same behavior. I + // know of no way to fix this. + const im_handled = priv.im_context.as(gtk.IMContext).filterKeypress(event) != 0; + // log.warn("GTKIM: im_handled={} im_len={} im_composing={}", .{ + // im_handled, + // self.im_len, + // self.im_composing, + // }); + + // If the input method handled the event, you would think we would + // never proceed with key encoding for Ghostty but that is not the + // case. Input methods will handle basic character encoding like + // typing "a" and we want to associate that with the key event. + // So we have to check additional state to determine if we exit. + if (im_handled) { + // If we are composing then we're in a preedit state and do + // not want to encode any keys. For example: type a deadkey + // such as single quote on a US international keyboard layout. + if (priv.im_composing) return true; + + // If we were composing and now we're not, it means that we committed + // the text. We also don't want to encode a key event for this. + // Example: enable Japanese input method, press "konn" and then + // press enter. The final enter should not be encoded and "konn" + // (in hiragana) should be written as "ใ“ใ‚“". + if (priv.in_keyevent == .composing) return true; + + // Not composing and our input method buffer is empty. This could + // mean that the input method reacted to this event by activating + // an onscreen keyboard or something equivalent. We don't know. + // But the input method handled it and didn't give us text so + // we will just assume we should not encode this. This handles a + // real scenario when ibus starts the emoji input method + // (super+.). + if (priv.im_len == 0) return true; + } + + // At this point, for the sake of explanation of internal state: + // it is possible that im_len > 0 and im_composing == false. This + // means that we received a commit event from the input method that + // we want associated with the key event. This is common: its how + // basic character translation for simple inputs like "a" work. + } + + // We always reset the length of the im buffer. There's only one scenario + // we reach this point with im_len > 0 and that's if we received a commit + // event from the input method. We don't want to keep that state around + // since we've handled it here. + defer priv.im_len = 0; + + // Get the keyvals for this event. + const keyval_unicode = gdk.keyvalToUnicode(keyval); + const keyval_unicode_unshifted: u21 = gtk_key.keyvalUnicodeUnshifted( + priv.gl_area.as(gtk.Widget), + key_event, + keycode, + ); + + // We want to get the physical unmapped key to process physical keybinds. + // (These are keybinds explicitly marked as requesting physical mapping). + const physical_key = keycode: { + const w3c_key: input.Key = w3c: for (input.keycodes.entries) |entry| { + if (entry.native == keycode) break :w3c entry.key; + } else .unidentified; + + // Consult the pre-remapped XKB keyval/keysym to get the (possibly) + // remapped key. If the W3C key or the remapped key + // is eligible for remapping, we use it. + // + // See the docs for `shouldBeRemappable` for why we even have to + // do this in the first place. + if (gtk_key.keyFromKeyval(keyval)) |remapped| { + if (w3c_key.shouldBeRemappable() or remapped.shouldBeRemappable()) + break :keycode remapped; + } + + // Return the original physical key + break :keycode w3c_key; + }; + + // Get our modifier for the event + const mods: input.Mods = gtk_key.eventMods( + event, + physical_key, + gtk_mods, + action, + Application.default().winproto(), + ); + + // Get our consumed modifiers + const consumed_mods: input.Mods = consumed: { + const T = @typeInfo(gdk.ModifierType); + std.debug.assert(T.@"struct".layout == .@"packed"); + const I = T.@"struct".backing_integer.?; + + const masked = @as(I, @bitCast(key_event.getConsumedModifiers())) & @as(I, gdk.MODIFIER_MASK); + break :consumed gtk_key.translateMods(@bitCast(masked)); + }; + + // log.debug("key pressed key={} keyval={x} physical_key={} composing={} text_len={} mods={}", .{ + // key, + // keyval, + // physical_key, + // priv.im_composing, + // priv.im_len, + // mods, + // }); + + // If we have no UTF-8 text, we try to convert our keyval to + // a text value. We have to do this because GTK will not process + // "Ctrl+Shift+1" (on US keyboards) as "Ctrl+!" but instead as "". + // But the keyval is set correctly so we can at least extract that. + if (priv.im_len == 0 and keyval_unicode > 0) im: { + if (std.math.cast(u21, keyval_unicode)) |cp| { + // We don't want to send control characters as IM + // text. Control characters are handled already by + // the encoder directly. + if (cp < 0x20) break :im; + + if (std.unicode.utf8Encode(cp, &priv.im_buf)) |len| { + priv.im_len = len; + } else |_| {} + } + } + + // Invoke the core Ghostty logic to handle this input. + const surface = priv.core_surface orelse return false; + const effect = surface.keyCallback(.{ + .action = action, + .key = physical_key, + .mods = mods, + .consumed_mods = consumed_mods, + .composing = priv.im_composing, + .utf8 = priv.im_buf[0..priv.im_len], + .unshifted_codepoint = keyval_unicode_unshifted, + }) catch |err| { + log.err("error in key callback err={}", .{err}); + return false; + }; + + switch (effect) { + .closed => return true, + .ignored => {}, + .consumed => if (action == .press or action == .repeat) { + // If we were in the composing state then we reset our context. + // We do NOT want to reset if we're not in the composing state + // because there is other IME state that we want to preserve, + // such as quotation mark ordering for Chinese input. + if (priv.im_composing) { + priv.im_context.as(gtk.IMContext).reset(); + surface.preeditCallback(null) catch {}; + } + + // Bell stops ringing when any key is pressed that is used by + // the core in any way. + self.setBellRinging(false); + + return true; + }, + } + + return false; + } + + /// Prompt for a manual title change for the surface. + pub fn promptTitle(self: *Self) void { + const priv = self.private(); + const dialog = TitleDialog.new(.surface, priv.title_override orelse priv.title); + _ = TitleDialog.signals.set.connect( + dialog, + *Self, + titleDialogSet, + self, + .{}, + ); + + dialog.present(self.as(gtk.Widget)); + } + + /// Scale x/y by the GDK device scale. + fn scaledCoordinates( + self: *Self, + x: f64, + y: f64, + ) struct { x: f64, y: f64 } { + const gl_area = self.private().gl_area; + const scale_factor: f64 = @floatFromInt( + gl_area.as(gtk.Widget).getScaleFactor(), + ); + + return .{ + .x = x * scale_factor, + .y = y * scale_factor, + }; + } + + //--------------------------------------------------------------- + // Libghostty Callbacks + + pub fn close(self: *Self) void { + signals.@"close-request".impl.emit( + self, + null, + .{}, + null, + ); + } + + pub fn childExited( + self: *Self, + data: apprt.surface.Message.ChildExited, + ) bool { + // Even if we don't support the overlay, we still keep our property + // up to date for anyone listening. + const priv = self.private(); + priv.child_exited = true; + self.as(gobject.Object).notifyByPspec( + properties.@"child-exited".impl.param_spec, + ); + + // If we have the noop child exited overlay then we don't do anything + // for child exited. The false return will force libghostty to show + // the normal text-based message. + if (comptime @hasDecl(ChildExited, "noop")) { + return false; + } + + priv.child_exited_overlay.setData(&data); + return true; + } + + pub fn getContentScale(self: *Self) apprt.ContentScale { + const priv = self.private(); + const gl_area = priv.gl_area; + + const gtk_scale: f32 = scale: { + const widget = gl_area.as(gtk.Widget); + // Future: detect GTK version 4.12+ and use gdk_surface_get_scale so we + // can support fractional scaling. + const scale = widget.getScaleFactor(); + if (scale <= 0) { + log.warn("gtk_widget_get_scale_factor returned a non-positive number: {}", .{scale}); + break :scale 1.0; + } + break :scale @floatFromInt(scale); + }; + + // Also scale using font-specific DPI, which is often exposed to the user + // via DE accessibility settings (see https://docs.gtk.org/gtk4/class.Settings.html). + const xft_dpi_scale = xft_scale: { + // gtk-xft-dpi is font DPI multiplied by 1024. See + // https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html + const gtk_xft_dpi = gsettings.get(.@"gtk-xft-dpi") orelse { + log.warn("gtk-xft-dpi was not set, using default value", .{}); + break :xft_scale 1.0; + }; + + // Use a value of 1.0 for the XFT DPI scale if the setting is <= 0 + // See: + // 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}); + break :xft_scale 1.0; + } + + // As noted above gtk-xft-dpi is multiplied by 1024, so we divide by + // 1024, then divide by the default value (96) to derive a scale. Note + // gtk-xft-dpi can be fractional, so we use floating point math here. + const xft_dpi: f32 = @as(f32, @floatFromInt(gtk_xft_dpi)) / 1024.0; + break :xft_scale xft_dpi / 96.0; + }; + + const scale = gtk_scale * xft_dpi_scale; + return .{ .x = scale, .y = scale }; + } + + pub fn getSize(self: *Self) apprt.SurfaceSize { + const priv = self.private(); + // By the time this is called, we should be in a widget tree. + // This should not be called before that. We ensure this by initializing + // the surface in `glareaResize`. This is VERY important because it + // avoids the pty having an incorrect initial size. + assert(priv.size.width >= 0 and priv.size.height >= 0); + return priv.size; + } + + pub fn getCursorPos(self: *Self) apprt.CursorPos { + return self.private().cursor_pos; + } + + pub fn defaultTermioEnv(self: *Self) !std.process.EnvMap { + const app = Application.default(); + const alloc = app.allocator(); + var env = try internal_os.getEnvMap(alloc); + errdefer env.deinit(); + + if (app.savedLanguage()) |language| { + try env.put("LANG", language); + } else { + env.remove("LANG"); + } + + // Don't leak these GTK environment variables to child processes. + env.remove("GDK_DEBUG"); + env.remove("GDK_DISABLE"); + env.remove("GSK_RENDERER"); + + // Remove some environment variables that are set when Ghostty is launched + // from a `.desktop` file, by D-Bus activation, or systemd. + env.remove("GIO_LAUNCHED_DESKTOP_FILE"); + env.remove("GIO_LAUNCHED_DESKTOP_FILE_PID"); + env.remove("DBUS_STARTER_ADDRESS"); + env.remove("DBUS_STARTER_BUS_TYPE"); + env.remove("INVOCATION_ID"); + env.remove("JOURNAL_STREAM"); + env.remove("NOTIFY_SOCKET"); + + // Unset environment varies set by snaps if we're running in a snap. + // This allows Ghostty to further launch additional snaps. + if (comptime build_config.snap) { + if (env.get("SNAP") != null) try filterSnapPaths( + alloc, + &env, + ); + } + + // This is a hack because it ties ourselves (optionally) to the + // Window class. The right solution we should do is emit a signal + // here where the handler can modify our EnvMap, but boxing the + // EnvMap is a bit annoying so I'm punting it. + if (ext.getAncestor(Window, self.as(gtk.Widget))) |window| { + try window.winproto().addSubprocessEnv(&env); + + if (window.isQuickTerminal()) { + try env.put("GHOSTTY_QUICK_TERMINAL", "1"); + } + } + + return env; + } + + /// Filter out environment variables that start with forbidden prefixes. + fn filterSnapPaths(gpa: std.mem.Allocator, env_map: *std.process.EnvMap) !void { + comptime assert(build_config.snap); + + const snap_vars = [_][]const u8{ + "SNAP", + "SNAP_USER_COMMON", + "SNAP_USER_DATA", + "SNAP_DATA", + "SNAP_COMMON", + }; + + // Use an arena because everything in this function is temporary. + var arena = std.heap.ArenaAllocator.init(gpa); + defer arena.deinit(); + const alloc = arena.allocator(); + + var env_to_remove: std.ArrayList([]const u8) = .empty; + var env_to_update: std.ArrayList(struct { + key: []const u8, + value: []const u8, + }) = .empty; + + var it = env_map.iterator(); + while (it.next()) |entry| { + const key = entry.key_ptr.*; + const value = entry.value_ptr.*; + + // Ignore fields we set ourself + if (std.mem.eql(u8, key, "TERMINFO")) continue; + if (std.mem.startsWith(u8, key, "GHOSTTY")) continue; + + // Any env var starting with SNAP must be removed + if (std.mem.startsWith(u8, key, "SNAP_")) { + try env_to_remove.append(alloc, key); + continue; + } + + var filtered_paths: std.ArrayList([]const u8) = .empty; + var modified = false; + var paths = std.mem.splitAny(u8, value, ":"); + while (paths.next()) |path| { + var include = true; + for (snap_vars) |k| if (env_map.get(k)) |snap_path| { + if (snap_path.len == 0) continue; + if (std.mem.startsWith(u8, path, snap_path)) { + include = false; + modified = true; + break; + } + }; + if (include) try filtered_paths.append(alloc, path); + } + + if (modified) { + if (filtered_paths.items.len > 0) { + const new_value = try std.mem.join(alloc, ":", filtered_paths.items); + try env_to_update.append(alloc, .{ .key = key, .value = new_value }); + } else { + try env_to_remove.append(alloc, key); + } + } + } + + for (env_to_update.items) |item| try env_map.put( + item.key, + item.value, + ); + for (env_to_remove.items) |key| _ = env_map.remove(key); + } + + pub fn clipboardRequest( + self: *Self, + clipboard_type: apprt.Clipboard, + state: apprt.ClipboardRequest, + ) !bool { + return try Clipboard.request( + self, + clipboard_type, + state, + ); + } + + pub fn setClipboard( + self: *Self, + clipboard_type: apprt.Clipboard, + contents: []const apprt.ClipboardContent, + confirm: bool, + ) void { + Clipboard.set( + self, + clipboard_type, + contents, + confirm, + ); + } + + /// Focus this surface. This properly focuses the input part of + /// our surface. + pub fn grabFocus(self: *Self) void { + const priv = self.private(); + _ = priv.gl_area.as(gtk.Widget).grabFocus(); + } + + pub fn sendDesktopNotification(self: *Self, title: [:0]const u8, body: [:0]const u8) void { + const app = Application.default(); + const priv: *Private = self.private(); + + const core_surface = priv.core_surface orelse { + log.warn("can't send notification because there is no core surface", .{}); + return; + }; + + const t = switch (title.len) { + 0 => "Ghostty", + else => title, + }; + + const notification = gio.Notification.new(t); + defer notification.unref(); + notification.setBody(body); + + const icon = gio.ThemedIcon.new("com.mitchellh.ghostty"); + defer icon.unref(); + notification.setIcon(icon.as(gio.Icon)); + + const pointer = glib.Variant.newUint64(core_surface.id); + notification.setDefaultActionAndTargetValue( + "app.present-surface", + pointer, + ); + + // We set the notification ID to the body content. If the content is the + // same, this notification may replace a previous notification + const gio_app = app.as(gio.Application); + gio_app.sendNotification(body, notification); + } + + //--------------------------------------------------------------- + // Virtual Methods + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + // Initialize our actions + self.initActionMap(); + + const priv = self.private(); + + // Initialize some private fields so they aren't undefined + priv.rt_surface = .{ .surface = self }; + priv.precision_scroll = false; + priv.cursor_pos = .{ .x = 0, .y = 0 }; + priv.mouse_shape = .text; + priv.mouse_hidden = false; + priv.focused = true; + priv.size = .{ .width = 0, .height = 0 }; + priv.vadj_signal_group = null; + + // If our configuration is null then we get the configuration + // from the application. + if (priv.config == null) { + const app = Application.default(); + priv.config = app.getConfig(); + } + + // Setup our input method state + priv.in_keyevent = .false; + priv.im_composing = false; + priv.im_len = 0; + + // Read GTK primary paste setting + priv.gtk_enable_primary_paste = gsettings.get(.@"gtk-enable-primary-paste") orelse true; + + // Set up to handle items being dropped on our surface. Files can be dropped + // from Nautilus and strings can be dropped from many programs. The order + // of these types matter. + var drop_target_types = [_]gobject.Type{ + gdk.FileList.getGObjectType(), + gio.File.getGObjectType(), + gobject.ext.types.string, + }; + priv.drop_target.setGtypes(&drop_target_types, drop_target_types.len); + + // Setup properties we can't set from our Blueprint file. + self.as(gtk.Widget).setCursorFromName("text"); + + // Initialize our config + self.propConfig(undefined, null); + } + + fn initActionMap(self: *Self) void { + const priv: *Private = self.private(); + + const actions = [_]ext.actions.Action(Self){ + .init( + "prompt-title", + actionPromptTitle, + null, + ), + .initStateful( + "notify-on-next-command-finish", + actionNotifyOnNextCommandFinish, + null, + glib.Variant.newBoolean(@intFromBool(false)), + ), + }; + + priv.action_group = ext.actions.addAsGroup(Self, self, "surface", &actions); + } + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + + if (priv.config) |v| { + v.unref(); + priv.config = null; + } + + if (priv.vadj_signal_group) |group| { + group.setTarget(null); + group.as(gobject.Object).unref(); + priv.vadj_signal_group = null; + } + + if (priv.hadj) |v| { + v.as(gobject.Object).unref(); + priv.hadj = null; + } + + if (priv.vadj) |v| { + v.as(gobject.Object).unref(); + priv.vadj = null; + } + + if (priv.progress_bar_timer) |timer| { + if (glib.Source.remove(timer) == 0) { + log.warn("unable to remove progress bar timer", .{}); + } + priv.progress_bar_timer = null; + } + + if (priv.idle_rechild) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove idle source", .{}); + } + priv.idle_rechild = null; + } + + if (priv.pending_horizontal_scroll_reset) |v| { + if (glib.Source.remove(v) == 0) { + log.warn("unable to remove pending horizontal scroll reset source", .{}); + } + priv.pending_horizontal_scroll_reset = null; + } + + // This works around a GTK double-free bug where if you bind + // to a top-level template child, it frees twice if the widget is + // also the root child of the template. By unsetting the child here, + // we avoid the double-free. + self.as(adw.Bin).setChild(null); + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const alloc = Application.default().allocator(); + const priv = self.private(); + if (priv.core_surface) |v| { + // Remove ourselves from the list of known surfaces in the app. + // We do this before deinit in case a callback triggers + // searching for this surface. + Application.default().core().deleteSurface(self.rt()); + + // NOTE: We must deinit the surface in the finalize call and NOT + // the dispose call because the inspector widget relies on this + // behavior with a weakRef to properly deactivate. + + // Deinit the surface + v.deinit(); + alloc.destroy(v); + + priv.core_surface = null; + } + if (priv.mouse_hover_url) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.mouse_hover_url = null; + } + if (priv.default_size) |v| { + ext.boxedFree(Size, v); + priv.default_size = null; + } + if (priv.font_size_request) |v| { + glib.ext.destroy(v); + priv.font_size_request = null; + } + if (priv.min_size) |v| { + ext.boxedFree(Size, v); + priv.min_size = null; + } + if (priv.pwd) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.pwd = null; + } + if (priv.title) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.title = null; + } + if (priv.title_override) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.title_override = null; + } + if (priv.overrides.command) |c| { + c.deinit(alloc); + priv.overrides.command = null; + } + if (priv.overrides.working_directory) |wd| { + alloc.free(wd); + priv.overrides.working_directory = null; + } + + // Clean up key sequence and key table state + for (priv.key_sequence.items) |s| alloc.free(s); + priv.key_sequence.deinit(alloc); + for (priv.key_tables.items) |s| alloc.free(s); + priv.key_tables.deinit(alloc); + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + // Properties + + /// Returns the title property without a copy. + pub fn getTitle(self: *Self) ?[:0]const u8 { + return self.private().title; + } + + /// Returns the effective title: the user-overridden title if set, + /// otherwise the terminal-set title. + pub fn getEffectiveTitle(self: *Self) ?[:0]const u8 { + const priv = self.private(); + return priv.title_override orelse priv.title; + } + + /// Copies the effective title to the clipboard. + pub fn copyTitleToClipboard(self: *Self) bool { + const title = self.getEffectiveTitle() orelse return false; + if (title.len == 0) return false; + self.setClipboard(.standard, &.{.{ + .mime = "text/plain", + .data = title, + }}, false); + return true; + } + + /// Set the title for this surface, copies the value. This should always + /// be the title as set by the terminal program, not any manually set + /// title. For manually set titles see `setTitleOverride`. + pub fn setTitle(self: *Self, title: ?[:0]const u8) void { + const priv = self.private(); + if (priv.title) |v| glib.free(@ptrCast(@constCast(v))); + priv.title = null; + if (title) |v| priv.title = glib.ext.dupeZ(u8, v); + self.as(gobject.Object).notifyByPspec(properties.title.impl.param_spec); + } + + /// Overridden title. This will be generally be shown over the title + /// unless this is unset (null). + pub fn setTitleOverride(self: *Self, title: ?[:0]const u8) void { + const priv = self.private(); + if (priv.title_override) |v| glib.free(@ptrCast(@constCast(v))); + priv.title_override = null; + if (title) |v| priv.title_override = glib.ext.dupeZ(u8, v); + self.as(gobject.Object).notifyByPspec(properties.@"title-override".impl.param_spec); + } + + /// Returns the pwd property without a copy. + pub fn getPwd(self: *Self) ?[:0]const u8 { + return self.private().pwd; + } + + /// Set the pwd for this surface, copies the value. + pub fn setPwd(self: *Self, pwd: ?[:0]const u8) void { + const priv = self.private(); + if (priv.pwd) |v| glib.free(@ptrCast(@constCast(v))); + priv.pwd = null; + if (pwd) |v| priv.pwd = glib.ext.dupeZ(u8, v); + self.as(gobject.Object).notifyByPspec(properties.pwd.impl.param_spec); + } + + /// Returns the focus state of this surface. + pub fn getFocused(self: *Self) bool { + return self.private().focused; + } + + /// Change the configuration for this surface. + pub fn setConfig(self: *Self, config: *Config) void { + const priv = self.private(); + if (priv.config) |c| c.unref(); + priv.config = config.ref(); + self.as(gobject.Object).notifyByPspec(properties.config.impl.param_spec); + } + + /// Return the default size, if set. + pub fn getDefaultSize(self: *Self) ?*Size { + const priv = self.private(); + return priv.default_size; + } + + /// Set the default size for a window that contains this surface. + /// This is up to the embedding widget to respect this. Generally, only + /// the first surface in a window respects this. + pub fn setDefaultSize(self: *Self, size: Size) void { + const priv = self.private(); + if (priv.default_size) |v| ext.boxedFree( + Size, + v, + ); + priv.default_size = ext.boxedCopy( + Size, + &size, + ); + self.as(gobject.Object).notifyByPspec(properties.@"default-size".impl.param_spec); + } + + /// Estimate and set the initial window size from config and font metrics. + /// This can be called before the core surface exists to set up the window + /// size before presenting. This is an estimate because it does not take + /// into account any padding that may need to be added to the window. + pub fn estimateInitialSize(self: *Self) void { + const priv: *Private = self.private(); + const config_obj = priv.config orelse return; + const config = config_obj.get(); + + // Both dimensions must be configured + if (config.@"window-height" <= 0 or config.@"window-width" <= 0) return; + + const app = Application.default(); + const alloc = app.allocator(); + + // Get content scale and compute DPI + const content_scale = self.getContentScale(); + const x_dpi = content_scale.x * font.face.default_dpi; + const y_dpi = content_scale.y * font.face.default_dpi; + + const font_size: font.face.DesiredSize = .{ + .points = config.@"font-size", + .xdpi = @intFromFloat(x_dpi), + .ydpi = @intFromFloat(y_dpi), + }; + + // Get font grid for cell metrics + var derived_config = font.SharedGridSet.DerivedConfig.init(alloc, config) catch return; + defer derived_config.deinit(); + + const font_grid_key, const font_grid = app.core().font_grid_set.ref( + &derived_config, + font_size, + ) catch return; + defer app.core().font_grid_set.deref(font_grid_key); + + const cell = font_grid.cellSize(); + + const width = @max(CoreSurface.min_window_width_cells, config.@"window-width") * cell.width; + const height = @max(CoreSurface.min_window_height_cells, config.@"window-height") * cell.height; + const width_f32: f32 = @floatFromInt(width); + const height_f32: f32 = @floatFromInt(height); + + const final_width: u32 = @intFromFloat(@ceil(width_f32 / content_scale.x)); + const final_height: u32 = @intFromFloat(@ceil(height_f32 / content_scale.y)); + + self.setDefaultSize(.{ .width = final_width, .height = final_height }); + } + + /// Get the key sequence list. Full transfer. + fn getKeySequence(self: *Self) ?*ext.StringList { + const priv = self.private(); + const alloc = Application.default().allocator(); + return ext.StringList.create(alloc, priv.key_sequence.items) catch null; + } + + /// Get the key table list. Full transfer. + fn getKeyTable(self: *Self) ?*ext.StringList { + const priv = self.private(); + const alloc = Application.default().allocator(); + return ext.StringList.create(alloc, priv.key_tables.items) catch null; + } + + /// Return the min size, if set. + pub fn getMinSize(self: *Self) ?*Size { + const priv = self.private(); + return priv.min_size; + } + + /// Set the min size for a window that contains this surface. + /// This is up to the embedding widget to respect this. Generally, only + /// the first surface in a window respects this. + pub fn setMinSize(self: *Self, size: Size) void { + const priv = self.private(); + if (priv.min_size) |v| ext.boxedFree( + Size, + v, + ); + priv.min_size = ext.boxedCopy( + Size, + &size, + ); + self.as(gobject.Object).notifyByPspec(properties.@"min-size".impl.param_spec); + } + + pub fn getMouseShape(self: *Self) terminal.MouseShape { + return self.private().mouse_shape; + } + + pub fn setMouseShape(self: *Self, shape: terminal.MouseShape) void { + const priv = self.private(); + priv.mouse_shape = shape; + self.as(gobject.Object).notifyByPspec(properties.@"mouse-shape".impl.param_spec); + } + + pub fn getMouseHidden(self: *Self) bool { + return self.private().mouse_hidden; + } + + pub fn setMouseHidden(self: *Self, hidden: bool) void { + const priv = self.private(); + priv.mouse_hidden = hidden; + self.as(gobject.Object).notifyByPspec(properties.@"mouse-hidden".impl.param_spec); + } + + pub fn setMouseHoverUrl(self: *Self, url: ?[:0]const u8) void { + const priv = self.private(); + if (priv.mouse_hover_url) |v| glib.free(@ptrCast(@constCast(v))); + priv.mouse_hover_url = null; + if (url) |v| priv.mouse_hover_url = glib.ext.dupeZ(u8, v); + self.as(gobject.Object).notifyByPspec(properties.@"mouse-hover-url".impl.param_spec); + } + + pub fn getBellRinging(self: *Self) bool { + return self.private().bell_ringing; + } + + pub fn setBellRinging(self: *Self, ringing: bool) void { + // Prevent duplicate change notifications if the signals we emit + // in this function cause this state to change again. + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + + // Logic around bell reaction happens on every event even if we're + // already in the ringing state. + if (ringing) self.ringBell(); + + // Property change only happens on actual state change + const priv = self.private(); + if (priv.bell_ringing == ringing) return; + priv.bell_ringing = ringing; + self.as(gobject.Object).notifyByPspec(properties.@"bell-ringing".impl.param_spec); + } + + pub fn setError(self: *Self, v: bool) void { + const priv = self.private(); + priv.@"error" = v; + self.as(gobject.Object).notifyByPspec(properties.@"error".impl.param_spec); + } + + pub fn setSearchActive(self: *Self, active: bool, needle: [:0]const u8) void { + const priv = self.private(); + var value = gobject.ext.Value.newFrom(active); + defer value.unset(); + gobject.Object.setProperty( + priv.search_overlay.as(gobject.Object), + SearchOverlay.properties.active.name, + &value, + ); + + if (!std.mem.eql(u8, needle, "")) { + priv.search_overlay.setSearchContents(needle); + } + + if (active) { + priv.search_overlay.grabFocus(); + } + } + + pub fn setSearchTotal(self: *Self, total: ?usize) void { + self.private().search_overlay.setSearchTotal(total); + } + + pub fn setSearchSelected(self: *Self, selected: ?usize) void { + self.private().search_overlay.setSearchSelected(selected); + } + + fn propConfig( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + const config = if (priv.config) |c| c.get() else return; + + // resize-overlay-duration + { + const ms = config.@"resize-overlay-duration".asMilliseconds(); + var value = gobject.ext.Value.newFrom(ms); + defer value.unset(); + gobject.Object.setProperty( + priv.resize_overlay.as(gobject.Object), + "duration", + &value, + ); + } + + // resize-overlay-position + { + const hv: struct { + gtk.Align, // halign + gtk.Align, // valign + } = switch (config.@"resize-overlay-position") { + .center => .{ .center, .center }, + .@"top-left" => .{ .start, .start }, + .@"top-right" => .{ .end, .start }, + .@"top-center" => .{ .center, .start }, + .@"bottom-left" => .{ .start, .end }, + .@"bottom-right" => .{ .end, .end }, + .@"bottom-center" => .{ .center, .end }, + }; + + var halign = gobject.ext.Value.newFrom(hv[0]); + defer halign.unset(); + var valign = gobject.ext.Value.newFrom(hv[1]); + defer valign.unset(); + gobject.Object.setProperty( + priv.resize_overlay.as(gobject.Object), + "overlay-halign", + &halign, + ); + gobject.Object.setProperty( + priv.resize_overlay.as(gobject.Object), + "overlay-valign", + &valign, + ); + } + } + + fn propError( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + if (priv.@"error") { + // Ensure we have an opaque background. The window will NOT set + // this if we have transparency set and we need an opaque + // background for the error message to be readable. + self.as(gtk.Widget).addCssClass("background"); + } else { + // Regardless of transparency setting, we remove the background + // CSS class from this widget. Parent widgets will set it + // appropriately (see window.zig for example). + self.as(gtk.Widget).removeCssClass("background"); + } + + // We need to set our child property on an idle tick, because the + // error property can be triggered by signals that are in the middle + // of widget mapping and changing our child during that time + // results in a hard gtk crash. + if (priv.idle_rechild == null) priv.idle_rechild = glib.idleAdd( + onIdleRechild, + self, + ); + } + + fn onIdleRechild(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + const priv = self.private(); + priv.idle_rechild = null; + if (priv.@"error") { + self.as(adw.Bin).setChild(priv.error_page.as(gtk.Widget)); + } else { + self.as(adw.Bin).setChild(priv.terminal_page.as(gtk.Widget)); + } + return 0; + } + + fn propMouseHoverUrl( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + const visible = if (priv.mouse_hover_url) |v| v.len > 0 else false; + priv.url_left.as(gtk.Widget).setVisible(if (visible) 1 else 0); + } + + fn propMouseHidden( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + + // If we're hidden we set it to "none" + if (priv.mouse_hidden) { + self.as(gtk.Widget).setCursorFromName("none"); + return; + } + + // If we're not hidden we just trigger the mouse shape + // prop notification to handle setting the proper mouse shape. + self.propMouseShape(undefined, null); + } + + fn propMouseShape( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + + // If our mouse should be hidden currently then we don't + // do anything. + if (priv.mouse_hidden) return; + + const name: [:0]const u8 = switch (priv.mouse_shape) { + .default => "default", + .help => "help", + .pointer => "pointer", + .context_menu => "context-menu", + .progress => "progress", + .wait => "wait", + .cell => "cell", + .crosshair => "crosshair", + .text => "text", + .vertical_text => "vertical-text", + .alias => "alias", + .copy => "copy", + .no_drop => "no-drop", + .move => "move", + .not_allowed => "not-allowed", + .grab => "grab", + .grabbing => "grabbing", + .all_scroll => "all-scroll", + .col_resize => "col-resize", + .row_resize => "row-resize", + .n_resize => "n-resize", + .e_resize => "e-resize", + .s_resize => "s-resize", + .w_resize => "w-resize", + .ne_resize => "ne-resize", + .nw_resize => "nw-resize", + .se_resize => "se-resize", + .sw_resize => "sw-resize", + .ew_resize => "ew-resize", + .ns_resize => "ns-resize", + .nesw_resize => "nesw-resize", + .nwse_resize => "nwse-resize", + .zoom_in => "zoom-in", + .zoom_out => "zoom-out", + }; + + // Set our new cursor. + self.as(gtk.Widget).setCursorFromName(name.ptr); + } + + fn vadjValueChanged(adj: *gtk.Adjustment, self: *Self) callconv(.c) void { + // This will trigger for every single pixel change in the adjustment, + // but our core surface handles the noise from this so that identical + // rows are cheap. + const core_surface = self.core() orelse return; + const row: usize = @intFromFloat(@round(adj.getValue())); + _ = core_surface.performBindingAction(.{ .scroll_to_row = row }) catch |err| { + log.err("error performing scroll_to_row action err={}", .{err}); + }; + } + + fn propVAdjustment( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + + // When vadjustment is first set, we setup the signal group lazily. + // This makes it so that if we don't use scrollbars, we never + // pay the memory cost of this. + const group: *gobject.SignalGroup = priv.vadj_signal_group orelse group: { + const group = gobject.SignalGroup.new(gtk.Adjustment.getGObjectType()); + group.connect( + "value-changed", + @ptrCast(&vadjValueChanged), + self, + ); + + priv.vadj_signal_group = group; + break :group group; + }; + + // Setup our signal group target + group.setTarget(if (priv.vadj) |v| v.as(gobject.Object) else null); + } + + /// Handle bell features that need to happen every time a BEL is received + /// Currently this is audio and system but this could change in the future. + fn ringBell(self: *Self) void { + const priv = self.private(); + + // Emit the signal + signals.bell.impl.emit( + self, + null, + .{}, + null, + ); + + // Activate actions if they exist + _ = self.as(gtk.Widget).activateAction("tab.ring-bell", null); + _ = self.as(gtk.Widget).activateAction("win.ring-bell", null); + + const config = if (priv.config) |c| c.get() else return; + + // Do our sound + if (config.@"bell-features".audio) audio: { + const config_path = config.@"bell-audio-path" orelse break :audio; + const path, const required = switch (config_path) { + .optional => |path| .{ path, false }, + .required => |path| .{ path, true }, + }; + + const volume = std.math.clamp( + config.@"bell-audio-volume", + 0.0, + 1.0, + ); + + const media_file = media.fromFilename(path) orelse break :audio; + media.playMediaFile(media_file, volume, required); + } + } + + //--------------------------------------------------------------- + // Gtk.Scrollable interface implementation + + pub fn getHAdjustment(self: *Self) ?*gtk.Adjustment { + return self.private().hadj; + } + + pub fn setHAdjustment(self: *Self, adj_: ?*gtk.Adjustment) void { + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + self.as(gobject.Object).notifyByPspec(properties.hadjustment.impl.param_spec); + + const priv = self.private(); + if (priv.hadj) |old| { + old.as(gobject.Object).unref(); + priv.hadj = null; + } + + const adj = adj_ orelse return; + _ = adj.as(gobject.Object).ref(); + priv.hadj = adj; + } + + fn getHAdjustmentValue(self: *Self, value: *gobject.Value) void { + gobject.ext.Value.set(value, self.getHAdjustment()); + } + + fn setHAdjustmentValue(self: *Self, value: *const gobject.Value) void { + self.setHAdjustment(gobject.ext.Value.get(value, ?*gtk.Adjustment)); + } + + pub fn getVAdjustment(self: *Self) ?*gtk.Adjustment { + return self.private().vadj; + } + + pub fn setVAdjustment(self: *Self, adj_: ?*gtk.Adjustment) void { + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + self.as(gobject.Object).notifyByPspec(properties.vadjustment.impl.param_spec); + + const priv = self.private(); + + if (priv.vadj) |old| { + old.as(gobject.Object).unref(); + priv.vadj = null; + } + + const adj = adj_ orelse return; + _ = adj.as(gobject.Object).ref(); + priv.vadj = adj; + } + + fn getVAdjustmentValue(self: *Self, value: *gobject.Value) void { + gobject.ext.Value.set(value, self.getVAdjustment()); + } + + fn setVAdjustmentValue(self: *Self, value: *const gobject.Value) void { + self.setVAdjustment(gobject.ext.Value.get(value, ?*gtk.Adjustment)); + } + + //--------------------------------------------------------------- + // Signal Handlers + + pub fn actionPromptTitle( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const surface = self.core() orelse return; + _ = surface.performBindingAction(.prompt_surface_title) catch |err| { + log.warn("unable to perform prompt title action err={}", .{err}); + }; + } + + pub fn actionNotifyOnNextCommandFinish( + action: *gio.SimpleAction, + _: ?*glib.Variant, + _: *Self, + ) callconv(.c) void { + const state = action.as(gio.Action).getState() orelse glib.Variant.newBoolean(@intFromBool(false)); + defer state.unref(); + const bool_variant_type = glib.ext.VariantType.newFor(bool); + defer bool_variant_type.free(); + if (state.isOfType(bool_variant_type) == 0) return; + const value = state.getBoolean() != 0; + action.setState(glib.Variant.newBoolean(@intFromBool(!value))); + } + + fn childExitedClose( + _: *ChildExited, + self: *Self, + ) callconv(.c) void { + // This closes the surface with no confirmation. + self.close(); + } + + fn contextMenuClosed( + _: *gtk.PopoverMenu, + self: *Self, + ) callconv(.c) void { + // When the context menu closes, it moves focus back to the tab + // bar if there are tabs. That's not correct. We need to grab it + // on the surface. + self.grabFocus(); + } + + fn inspectorWeakNotify( + ud: ?*anyopaque, + _: *gobject.Object, + ) callconv(.c) void { + const self: *Self = @ptrCast(@alignCast(ud orelse return)); + const priv = self.private(); + priv.inspector = null; + } + + fn dtDrop( + _: *gtk.DropTarget, + value: *gobject.Value, + _: f64, + _: f64, + self: *Self, + ) callconv(.c) c_int { + const alloc = Application.default().allocator(); + + if (ext.gValueHolds(value, gdk.FileList.getGObjectType())) { + var stream: std.Io.Writer.Allocating = .init(alloc); + defer stream.deinit(); + + var shell_escape_writer: internal_os.ShellEscapeWriter = .init(&stream.writer); + const writer = &shell_escape_writer.writer; + + const list: ?*glib.SList = list: { + const unboxed = value.getBoxed() orelse return 0; + const fl: *gdk.FileList = @ptrCast(@alignCast(unboxed)); + break :list fl.getFiles(); + }; + defer if (list) |v| v.free(); + + { + var current: ?*glib.SList = list; + while (current) |item| : (current = item.f_next) { + const file: *gio.File = @ptrCast(@alignCast(item.f_data orelse continue)); + const path = file.getPath() orelse continue; + const slice = std.mem.span(path); + defer glib.free(path); + + writer.writeAll(slice) catch |err| { + log.err("unable to write path to buffer: {}", .{err}); + continue; + }; + writer.writeAll("\n") catch |err| { + log.err("unable to write to buffer: {}", .{err}); + continue; + }; + } + } + + const string = stream.toOwnedSliceSentinel(0) catch |err| { + log.err("unable to convert to a slice: {}", .{err}); + return 0; + }; + defer alloc.free(string); + Clipboard.paste(self, string); + return 1; + } + + if (ext.gValueHolds(value, gio.File.getGObjectType())) { + const object = value.getObject() orelse return 0; + const file = gobject.ext.cast(gio.File, object) orelse return 0; + const path = file.getPath() orelse return 0; + var stream: std.Io.Writer.Allocating = .init(alloc); + defer stream.deinit(); + + var shell_escape_writer: internal_os.ShellEscapeWriter = .init(&stream.writer); + const writer = &shell_escape_writer.writer; + writer.writeAll(std.mem.span(path)) catch |err| { + log.err("unable to write path to buffer: {}", .{err}); + return 0; + }; + writer.writeAll("\n") catch |err| { + log.err("unable to write to buffer: {}", .{err}); + return 0; + }; + + const string = stream.toOwnedSliceSentinel(0) catch |err| { + log.err("unable to convert to a slice: {}", .{err}); + return 0; + }; + defer alloc.free(string); + return 1; + } + + if (ext.gValueHolds(value, gobject.ext.types.string)) { + if (value.getString()) |string| { + Clipboard.paste(self, std.mem.span(string)); + } + return 1; + } + + return 1; + } + + fn ecKeyPressed( + ec_key: *gtk.EventControllerKey, + keyval: c_uint, + keycode: c_uint, + gtk_mods: gdk.ModifierType, + self: *Self, + ) callconv(.c) c_int { + return @intFromBool(self.keyEvent( + .press, + ec_key, + keyval, + keycode, + gtk_mods, + )); + } + + fn ecKeyReleased( + ec_key: *gtk.EventControllerKey, + keyval: c_uint, + keycode: c_uint, + state: gdk.ModifierType, + self: *Self, + ) callconv(.c) void { + _ = self.keyEvent( + .release, + ec_key, + keyval, + keycode, + state, + ); + } + + fn ecFocusEnter(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void { + self.updateFocus(true); + } + + fn ecFocusLeave(_: *gtk.EventControllerFocus, self: *Self) callconv(.c) void { + self.updateFocus(false); + } + + fn updateFocus(self: *Self, focused: bool) void { + const priv = self.private(); + priv.focused = focused; + + const ctx = priv.im_context.as(gtk.IMContext); + if (focused) ctx.focusIn() else ctx.focusOut(); + + _ = glib.idleAddOnce(idleFocus, self.ref()); + self.as(gobject.Object).notifyByPspec(properties.focused.impl.param_spec); + + // Bell stops ringing as soon as we gain focus + if (focused) self.setBellRinging(false); + } + + /// The focus callback must be triggered on an idle loop source because + /// there are actions within libghostty callbacks (such as showing close + /// confirmation dialogs) that can trigger focus loss and cause a deadlock + /// because the lock may be held during the callback. + /// + /// Userdata should be a `*Surface`. This will unref once. + fn idleFocus(ud: ?*anyopaque) callconv(.c) void { + const self: *Self = @ptrCast(@alignCast(ud orelse return)); + defer self.unref(); + + const priv = self.private(); + const surface = priv.core_surface orelse return; + surface.focusCallback(priv.focused) catch |err| { + log.warn("error in focus callback err={}", .{err}); + }; + } + + fn gcMouseDown( + gesture: *gtk.GestureClick, + _: c_int, + x: f64, + y: f64, + self: *Self, + ) callconv(.c) void { + const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return; + + // Bell stops ringing if any mouse button is pressed. + self.setBellRinging(false); + + // Get our surface. If we don't have one, ignore this. + const priv = self.private(); + const core_surface = priv.core_surface orelse return; + + // If we don't have focus, grab it. + const gl_area_widget = priv.gl_area.as(gtk.Widget); + const had_focus = gl_area_widget.hasFocus() != 0; + if (!had_focus) { + _ = gl_area_widget.grabFocus(); + } + + // Report the event + const button = translateMouseButton(gesture.as(gtk.GestureSingle).getCurrentButton()); + + // If this click is only transitioning split focus, suppress it so + // it doesn't get forwarded to the terminal as a mouse event. + if (!had_focus and button == .left) { + priv.suppress_left_mouse_release = true; + return; + } + + if (button == .middle and !priv.gtk_enable_primary_paste) { + return; + } + + const consumed = consumed: { + const gtk_mods = event.getModifierState(); + const mods = gtk_key.translateMods(gtk_mods); + break :consumed core_surface.mouseButtonCallback( + .press, + button, + mods, + ) catch |err| err: { + log.warn("error in key callback err={}", .{err}); + break :err false; + }; + }; + + // If a right click isn't consumed, mouseButtonCallback selects the hovered + // word and returns false. We can use this to handle the context menu + // opening under normal scenarios. + if (!consumed and button == .right) { + signals.menu.impl.emit( + self, + null, + .{}, + null, + ); + + const rect: gdk.Rectangle = .{ + .f_x = @intFromFloat(x), + .f_y = @intFromFloat(y), + .f_width = 1, + .f_height = 1, + }; + + const popover = priv.context_menu.as(gtk.Popover); + popover.setPointingTo(&rect); + popover.popup(); + } + } + + fn gcMouseUp( + gesture: *gtk.GestureClick, + _: c_int, + _: f64, + _: f64, + self: *Self, + ) callconv(.c) void { + const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return; + + const priv = self.private(); + const surface = priv.core_surface orelse return; + const gtk_mods = event.getModifierState(); + const button = translateMouseButton(gesture.as(gtk.GestureSingle).getCurrentButton()); + + if (button == .left and priv.suppress_left_mouse_release) { + priv.suppress_left_mouse_release = false; + return; + } + + if (button == .middle and !priv.gtk_enable_primary_paste) { + return; + } + + const mods = gtk_key.translateMods(gtk_mods); + const consumed = surface.mouseButtonCallback( + .release, + button, + mods, + ) catch |err| { + log.warn("error in key callback err={}", .{err}); + return; + }; + + // Trigger the on-screen keyboard if we have no selection, + // and that the mouse event hasn't been intercepted by the callback. + // + // It's better to do this here rather than within the core callback + // since we have direct access to the underlying gdk.Event here. + if (!consumed and button == .left and !surface.hasSelection()) { + if (!self.showOnScreenKeyboard(event)) { + log.warn("failed to activate the on-screen keyboard", .{}); + } + } + } + + fn ecMouseMotion( + ec: *gtk.EventControllerMotion, + x: f64, + y: f64, + self: *Self, + ) callconv(.c) void { + const event = ec.as(gtk.EventController).getCurrentEvent() orelse return; + const priv = self.private(); + + const scaled = self.scaledCoordinates(x, y); + const pos: apprt.CursorPos = .{ + .x = @floatCast(scaled.x), + .y = @floatCast(scaled.y), + }; + + // There seem to be at least two cases where GTK issues a mouse motion + // event without the cursor actually moving: + // 1. GLArea is resized under the mouse. This has the unfortunate + // side effect of causing focus to potentially change when + // `focus-follows-mouse` is enabled. + // 2. The window title is updated. This can cause the mouse to unhide + // incorrectly when hide-mouse-when-typing is enabled. + // To prevent incorrect behavior, we'll only grab focus and + // continue with callback logic if the cursor has actually moved. + const is_cursor_still = @abs(priv.cursor_pos.x - pos.x) < 1 and + @abs(priv.cursor_pos.y - pos.y) < 1; + if (is_cursor_still) return; + + // If we don't have focus, and we want it, grab it. + if (priv.config) |config| { + const gl_area_widget = priv.gl_area.as(gtk.Widget); + if (gl_area_widget.hasFocus() == 0 and + config.get().@"focus-follows-mouse") + { + _ = gl_area_widget.grabFocus(); + } + } + + // Our pos changed, update + priv.cursor_pos = pos; + + // Notify the callback + if (priv.core_surface) |surface| { + const gtk_mods = event.getModifierState(); + const mods = gtk_key.translateMods(gtk_mods); + surface.cursorPosCallback(priv.cursor_pos, mods) catch |err| { + log.warn("error in cursor pos callback err={}", .{err}); + }; + } + } + + fn ecMouseLeave( + ec_motion: *gtk.EventControllerMotion, + self: *Self, + ) callconv(.c) void { + const event = ec_motion.as(gtk.EventController).getCurrentEvent() orelse return; + + // Get our modifiers + const priv = self.private(); + if (priv.core_surface) |surface| { + // If we have a core surface then we can send the cursor pos + // callback with an invalid position to indicate the mouse left. + const gtk_mods = event.getModifierState(); + const mods = gtk_key.translateMods(gtk_mods); + surface.cursorPosCallback( + .{ .x = -1, .y = -1 }, + mods, + ) catch |err| { + log.warn("error in cursor pos callback err={}", .{err}); + return; + }; + } + } + + fn ecMouseScrollVerticalPrecisionBegin( + _: *gtk.EventControllerScroll, + self: *Self, + ) callconv(.c) void { + self.private().precision_scroll = true; + } + + fn ecMouseScrollVerticalPrecisionEnd( + _: *gtk.EventControllerScroll, + self: *Self, + ) callconv(.c) void { + self.private().precision_scroll = false; + } + + fn ecMouseScrollVertical( + _: *gtk.EventControllerScroll, + x: f64, + y: f64, + self: *Self, + ) callconv(.c) c_int { + const priv: *Private = self.private(); + const surface = priv.core_surface orelse return 0; + + // Multiply precision scrolls by 10 to get a better response from + // touchpad scrolling + const multiplier: f64 = if (priv.precision_scroll) 10.0 else 1.0; + const scroll_mods: input.ScrollMods = .{ + .precision = priv.precision_scroll, + }; + + const scaled = self.scaledCoordinates(x, y); + surface.scrollCallback( + // We invert because we apply natural scrolling to the values. + // This behavior has existed for years without Linux users complaining + // but I suspect we'll have to make this configurable in the future + // or read a system setting. + scaled.x * -1 * multiplier, + scaled.y * -1 * multiplier, + scroll_mods, + ) catch |err| { + log.warn("error in scroll callback err={}", .{err}); + return 0; + }; + + return 1; + } + + fn ecMouseScrollHorizontal( + ec: *gtk.EventControllerScroll, + x: f64, + _: f64, + self: *Self, + ) callconv(.c) c_int { + const priv: *Private = self.private(); + + switch (ec.getUnit()) { + .surface => {}, + .wheel => return @intFromBool(false), + else => return @intFromBool(false), + } + + priv.pending_horizontal_scroll += x; + + if (@abs(priv.pending_horizontal_scroll) < 120) { + if (priv.pending_horizontal_scroll_reset) |v| { + _ = glib.Source.remove(v); + priv.pending_horizontal_scroll_reset = null; + } + priv.pending_horizontal_scroll_reset = glib.timeoutAdd(500, ecMouseScrollHorizontalReset, self); + return @intFromBool(true); + } + + _ = self.as(gtk.Widget).activateAction( + if (priv.pending_horizontal_scroll < 0.0) + "tab.next-page" + else + "tab.previous-page", + null, + ); + + if (priv.pending_horizontal_scroll_reset) |v| { + _ = glib.Source.remove(v); + priv.pending_horizontal_scroll_reset = null; + } + + priv.pending_horizontal_scroll = 0.0; + + return @intFromBool(true); + } + + fn ecMouseScrollHorizontalReset(ud: ?*anyopaque) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return @intFromBool(glib.SOURCE_REMOVE))); + const priv: *Private = self.private(); + priv.pending_horizontal_scroll = 0.0; + priv.pending_horizontal_scroll_reset = null; + return @intFromBool(glib.SOURCE_REMOVE); + } + + fn imPreeditStart( + _: *gtk.IMMulticontext, + self: *Self, + ) callconv(.c) void { + // log.warn("GTKIM: preedit start", .{}); + + // Start our composing state for the input method and reset our + // input buffer to empty. + const priv = self.private(); + priv.im_composing = true; + priv.im_len = 0; + } + + fn imPreeditChanged( + ctx: *gtk.IMMulticontext, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + + // Any preedit change should mark that we're composing. Its possible this + // is false using fcitx5-hangul and typing "dkssud" ("์•ˆ๋…•"). The + // second "s" results in a "commit" for "์•ˆ" which sets composing to false, + // but then immediately sends a preedit change for the next symbol. With + // composing set to false we won't commit this text. Therefore, we must + // ensure it is set here. + priv.im_composing = true; + + // We can't set our preedit on our surface unless we're realized. + // We do this now because we want to still keep our input method + // state coherent. + const surface = priv.core_surface orelse return; + + // Get our pre-edit string that we'll use to show the user. + var buf: [*:0]u8 = undefined; + ctx.as(gtk.IMContext).getPreeditString( + &buf, + null, + null, + ); + defer glib.free(buf); + const str = std.mem.sliceTo(buf, 0); + + // Update our preedit state in Ghostty core + // log.warn("GTKIM: preedit change str={s}", .{str}); + surface.preeditCallback(str) catch |err| { + log.warn( + "error in preedit callback err={}", + .{err}, + ); + }; + } + + fn imPreeditEnd( + _: *gtk.IMMulticontext, + self: *Self, + ) callconv(.c) void { + // log.warn("GTKIM: preedit end", .{}); + + // End our composing state for GTK, allowing us to commit the text. + const priv = self.private(); + priv.im_composing = false; + + // End our preedit state in Ghostty core + const surface = priv.core_surface orelse return; + surface.preeditCallback(null) catch |err| { + log.warn("error in preedit callback err={}", .{err}); + }; + } + + fn imCommit( + _: *gtk.IMMulticontext, + bytes: [*:0]u8, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + const str = std.mem.sliceTo(bytes, 0); + + // log.debug("GTKIM: input commit composing={} keyevent={} str={s}", .{ + // self.im_composing, + // self.in_keyevent, + // str, + // }); + + // We need to handle commit specially if we're in a key event. + // Specifically, GTK will send us a commit event for basic key + // encodings like "a" (on a US layout keyboard). We don't want + // to treat this as IME committed text because we want to associate + // it with a key event (i.e. "a" key press). + switch (priv.in_keyevent) { + // If we're not in a key event then this commit is from + // some other source (i.e. on-screen keyboard, tablet, etc.) + // and we want to commit the text to the core surface. + .false => {}, + + // If we're in a composing state and in a key event then this + // key event is resulting in a commit of multiple keypresses + // and we don't want to encode it alongside the keypress. + .composing => {}, + + // If we're not composing then this commit is just a normal + // key encoding and we want our key event to handle it so + // that Ghostty can be aware of the key event alongside + // the text. + .not_composing => { + if (str.len > priv.im_buf.len) { + log.warn("not enough buffer space for input method commit", .{}); + return; + } + + // Copy our committed text to the buffer + @memcpy(priv.im_buf[0..str.len], str); + priv.im_len = @intCast(str.len); + + // log.debug("input commit len={}", .{priv.im_len}); + return; + }, + } + + // If we reach this point from above it means we're composing OR + // not in a keypress. In either case, we want to commit the text + // given to us because that's what GTK is asking us to do. If we're + // not in a keypress it means that this commit came via a non-keyboard + // event (i.e. on-screen keyboard, tablet of some kind, etc.). + + // Committing ends composing state + priv.im_composing = false; + + // We can't set our preedit on our surface unless we're realized. + // We do this now because we want to still keep our input method + // state coherent. + if (priv.core_surface) |surface| { + // End our preedit state. Well-behaved input methods do this for us + // by triggering a preedit-end event but some do not (ibus 1.5.29). + surface.preeditCallback(null) catch |err| { + log.warn("error in preedit callback err={}", .{err}); + }; + + // Send the text to the core surface, associated with no key (an + // invalid key, which should produce no PTY encoding). + _ = surface.keyCallback(.{ + .action = .press, + .key = .unidentified, + .mods = .{}, + .consumed_mods = .{}, + .composing = false, + .utf8 = str, + }) catch |err| { + log.warn("error in key callback err={}", .{err}); + }; + } + } + + fn glareaRealize( + _: *gtk.GLArea, + self: *Self, + ) callconv(.c) void { + log.debug("realize", .{}); + + // Make the GL area current so we can detect any OpenGL errors. If + // we have errors here we can't render and we switch to the error + // state. + const priv = self.private(); + priv.gl_area.makeCurrent(); + if (priv.gl_area.getError()) |err| { + log.warn("failed to make GL context current: {s}", .{err.f_message orelse "(no message)"}); + log.warn("this error is almost always due to a library, driver, or GTK issue", .{}); + log.warn("this is a common cause of this issue: https://ghostty.org/docs/help/gtk-opengl-context", .{}); + self.setError(true); + return; + } + + // If we already have an initialized surface then we notify it. + // If we don't, we'll initialize it on the first resize so we have + // our proper initial dimensions. + if (priv.core_surface) |v| realize: { + v.renderer.displayRealized() catch |err| { + log.warn("core displayRealized failed err={}", .{err}); + break :realize; + }; + + self.redraw(); + } + + // Setup our input method. We do this here because this will + // create a strong reference back to ourself and we want to be + // able to release that in unrealize. + priv.im_context.as(gtk.IMContext).setClientWidget(self.as(gtk.Widget)); + } + + fn glareaUnrealize( + gl_area: *gtk.GLArea, + self: *Self, + ) callconv(.c) void { + log.debug("unrealize", .{}); + + // Notify our core surface + const priv = self.private(); + if (priv.core_surface) |surface| { + // There is no guarantee that our GLArea context is current + // when unrealize is emitted, so we need to make it current. + gl_area.makeCurrent(); + if (gl_area.getError()) |err| { + // I don't know a scenario this can happen, but it means + // we probably leaked memory because displayUnrealized + // below frees resources that aren't specifically OpenGL + // related. I didn't make the OpenGL renderer handle this + // scenario because I don't know if its even possible + // under valid circumstances, so let's log. + log.warn( + "gl_area_make_current failed in unrealize msg={s}", + .{err.f_message orelse "(no message)"}, + ); + log.warn("OpenGL resources and memory likely leaked", .{}); + return; + } + + surface.renderer.displayUnrealized(); + } + + // Unset our input method + priv.im_context.as(gtk.IMContext).setClientWidget(null); + } + + fn glareaRender( + _: *gtk.GLArea, + _: *gdk.GLContext, + self: *Self, + ) callconv(.c) c_int { + // If we don't have a surface then we failed to initialize for + // some reason and there's nothing to draw to the GLArea. + const priv = self.private(); + const surface = priv.core_surface orelse return 1; + + surface.renderer.drawFrame(true) catch |err| { + log.warn("failed to draw frame err={}", .{err}); + return 0; + }; + + return 1; + } + + fn glareaResize( + gl_area: *gtk.GLArea, + width: c_int, + height: c_int, + self: *Self, + ) callconv(.c) void { + // Some debug output to help understand what GTK is telling us. + { + const widget = gl_area.as(gtk.Widget); + const scale_factor = widget.getScaleFactor(); + const window_scale_factor = scale: { + const root = widget.getRoot() orelse break :scale 0; + const gtk_native = root.as(gtk.Native); + const gdk_surface = gtk_native.getSurface() orelse break :scale 0; + break :scale gdk_surface.getScaleFactor(); + }; + + log.debug("gl resize width={} height={} scale={} window_scale={}", .{ + width, + height, + scale_factor, + window_scale_factor, + }); + } + + // Store our cached size + const priv = self.private(); + + const new_size: apprt.SurfaceSize = .{ + .width = @intCast(width), + .height = @intCast(height), + }; + const changed = !priv.size.eql(&new_size); + priv.size = new_size; + + // If our surface is realize, we send callbacks. + if (priv.core_surface) |surface| { + // We also update the content scale because there is no signal for + // content scale change and it seems to trigger a resize event. + surface.contentScaleCallback(self.getContentScale()) catch |err| { + log.warn("error in content scale callback err={}", .{err}); + }; + + if (changed) { + surface.sizeCallback(new_size) catch |err| { + log.warn("error in size callback err={}", .{err}); + }; + // Setup our resize overlay if configured + self.resizeOverlaySchedule(); + } + + return; + } + + // If we don't have a surface, then we initialize it. + self.initSurface() catch |err| { + log.warn("surface failed to initialize err={}", .{err}); + }; + } + + const InitError = Allocator.Error || error{ + GLAreaError, + SurfaceError, + }; + + fn initSurface(self: *Self) InitError!void { + const priv: *Private = self.private(); + assert(priv.core_surface == null); + const gl_area = priv.gl_area; + + // We need to make the context current so we can call GL functions. + // This is required for all surface operations. + gl_area.makeCurrent(); + if (gl_area.getError()) |err| { + log.warn("failed to make GL context current: {s}", .{err.f_message orelse "(no message)"}); + log.warn("this error is usually due to a driver or gtk bug", .{}); + log.warn("this is a common cause of this issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/4950", .{}); + return error.GLAreaError; + } + + const app = Application.default(); + const alloc = app.allocator(); + + // Make our pointer to store our surface + const surface = try alloc.create(CoreSurface); + errdefer alloc.destroy(surface); + + // Add ourselves to the list of surfaces on the app. + try app.core().addSurface(self.rt()); + errdefer app.core().deleteSurface(self.rt()); + + // Initialize our surface configuration. + var config = try apprt.surface.newConfig( + app.core(), + priv.config.?.get(), + priv.context, + ); + defer config.deinit(); + + if (priv.overrides.command) |c| { + config.command = try c.clone(config._arena.?.allocator()); + } + if (priv.overrides.working_directory) |wd| { + const config_alloc = config.arenaAlloc(); + var wd_val: configpkg.WorkingDirectory = .{ .path = try config_alloc.dupe(u8, wd) }; + try wd_val.finalize(config_alloc); + config.@"working-directory" = wd_val; + } + + // Properties that can impact surface init + if (priv.font_size_request) |size| config.@"font-size" = size.points; + if (priv.pwd) |pwd| { + const config_alloc = config.arenaAlloc(); + var wd_val: configpkg.WorkingDirectory = .{ .path = try config_alloc.dupe(u8, pwd) }; + try wd_val.finalize(config_alloc); + config.@"working-directory" = wd_val; + } + + // Initialize the surface + surface.init( + alloc, + &config, + app.core(), + app.rt(), + &priv.rt_surface, + ) catch |err| { + log.warn("failed to initialize surface err={}", .{err}); + return error.SurfaceError; + }; + errdefer surface.deinit(); + + // Store it! + priv.core_surface = surface; + + // Emit the signal that we initialized the surface. + Surface.signals.init.impl.emit( + self, + null, + .{}, + null, + ); + + self.updateFocus(priv.focused); + } + + fn resizeOverlaySchedule(self: *Self) void { + const priv = self.private(); + const surface = priv.core_surface orelse return; + + // Only show the resize overlay if its enabled + const config = if (priv.config) |c| c.get() else return; + switch (config.@"resize-overlay") { + .always, .@"after-first" => {}, + .never => return, + } + + // If we have resize overlays enabled, setup an idler + // to show that. We do this in an idle tick because doing it + // during the resize results in flickering. + var buf: [32]u8 = undefined; + priv.resize_overlay.setLabel(text: { + const grid_size = surface.size.grid(); + break :text std.fmt.bufPrintZ( + &buf, + "{d} x {d}", + .{ + grid_size.columns, + grid_size.rows, + }, + ) catch |err| err: { + log.warn("unable to format text: {}", .{err}); + break :err ""; + }; + }); + priv.resize_overlay.schedule(); + } + + fn ecUrlMouseEnter( + _: *gtk.EventControllerMotion, + _: f64, + _: f64, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + const right = priv.url_right.as(gtk.Widget); + right.setVisible(1); + } + + fn ecUrlMouseLeave( + _: *gtk.EventControllerMotion, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + const right = priv.url_right.as(gtk.Widget); + right.setVisible(0); + } + + fn titleDialogSet( + _: *TitleDialog, + title_ptr: [*:0]const u8, + self: *Self, + ) callconv(.c) void { + const title = std.mem.span(title_ptr); + self.setTitleOverride(if (title.len == 0) null else title); + } + + fn searchStop(_: *SearchOverlay, self: *Self) callconv(.c) void { + const surface = self.core() orelse return; + _ = surface.performBindingAction(.end_search) catch |err| { + log.warn("unable to perform end_search action err={}", .{err}); + }; + _ = self.private().gl_area.as(gtk.Widget).grabFocus(); + } + + fn searchChanged(_: *SearchOverlay, needle: ?[*:0]const u8, self: *Self) callconv(.c) void { + const surface = self.core() orelse return; + _ = surface.performBindingAction(.{ .search = std.mem.sliceTo(needle orelse "", 0) }) catch |err| { + log.warn("unable to perform search action err={}", .{err}); + }; + } + + fn searchNextMatch(_: *SearchOverlay, self: *Self) callconv(.c) void { + const surface = self.core() orelse return; + _ = surface.performBindingAction(.{ .navigate_search = .next }) catch |err| { + log.warn("unable to perform navigate_search action err={}", .{err}); + }; + } + + fn searchPreviousMatch(_: *SearchOverlay, self: *Self) callconv(.c) void { + const surface = self.core() orelse return; + _ = surface.performBindingAction(.{ .navigate_search = .previous }) catch |err| { + log.warn("unable to perform navigate_search action err={}", .{err}); + }; + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const refSink = C.refSink; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(ResizeOverlay); + gobject.ext.ensureType(SearchOverlay); + gobject.ext.ensureType(KeyStateOverlay); + gobject.ext.ensureType(ChildExited); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 2, + .name = "surface", + }), + ); + + // Bindings + class.bindTemplateChildPrivate("gl_area", .{}); + class.bindTemplateChildPrivate("url_left", .{}); + class.bindTemplateChildPrivate("url_right", .{}); + class.bindTemplateChildPrivate("child_exited_overlay", .{}); + class.bindTemplateChildPrivate("context_menu", .{}); + class.bindTemplateChildPrivate("error_page", .{}); + class.bindTemplateChildPrivate("progress_bar_overlay", .{}); + class.bindTemplateChildPrivate("resize_overlay", .{}); + class.bindTemplateChildPrivate("search_overlay", .{}); + class.bindTemplateChildPrivate("key_state_overlay", .{}); + class.bindTemplateChildPrivate("terminal_page", .{}); + class.bindTemplateChildPrivate("drop_target", .{}); + class.bindTemplateChildPrivate("im_context", .{}); + + // Template Callbacks + class.bindTemplateCallback("focus_enter", &ecFocusEnter); + class.bindTemplateCallback("focus_leave", &ecFocusLeave); + class.bindTemplateCallback("key_pressed", &ecKeyPressed); + class.bindTemplateCallback("key_released", &ecKeyReleased); + class.bindTemplateCallback("mouse_down", &gcMouseDown); + class.bindTemplateCallback("mouse_up", &gcMouseUp); + class.bindTemplateCallback("mouse_motion", &ecMouseMotion); + class.bindTemplateCallback("mouse_leave", &ecMouseLeave); + class.bindTemplateCallback("scroll_vertical", &ecMouseScrollVertical); + class.bindTemplateCallback("scroll_vertical_begin", &ecMouseScrollVerticalPrecisionBegin); + class.bindTemplateCallback("scroll_vertical_end", &ecMouseScrollVerticalPrecisionEnd); + class.bindTemplateCallback("scroll_horizontal", &ecMouseScrollHorizontal); + class.bindTemplateCallback("drop", &dtDrop); + class.bindTemplateCallback("gl_realize", &glareaRealize); + class.bindTemplateCallback("gl_unrealize", &glareaUnrealize); + class.bindTemplateCallback("gl_render", &glareaRender); + class.bindTemplateCallback("gl_resize", &glareaResize); + class.bindTemplateCallback("im_preedit_start", &imPreeditStart); + class.bindTemplateCallback("im_preedit_changed", &imPreeditChanged); + class.bindTemplateCallback("im_preedit_end", &imPreeditEnd); + class.bindTemplateCallback("im_commit", &imCommit); + class.bindTemplateCallback("url_mouse_enter", &ecUrlMouseEnter); + class.bindTemplateCallback("url_mouse_leave", &ecUrlMouseLeave); + class.bindTemplateCallback("child_exited_close", &childExitedClose); + class.bindTemplateCallback("context_menu_closed", &contextMenuClosed); + class.bindTemplateCallback("notify_config", &propConfig); + class.bindTemplateCallback("notify_error", &propError); + class.bindTemplateCallback("notify_mouse_hover_url", &propMouseHoverUrl); + class.bindTemplateCallback("notify_mouse_hidden", &propMouseHidden); + class.bindTemplateCallback("notify_mouse_shape", &propMouseShape); + class.bindTemplateCallback("notify_vadjustment", &propVAdjustment); + class.bindTemplateCallback("should_border_be_shown", &closureShouldBorderBeShown); + class.bindTemplateCallback("should_unfocused_split_be_shown", &closureShouldUnfocusedSplitBeShown); + class.bindTemplateCallback("search_stop", &searchStop); + class.bindTemplateCallback("search_changed", &searchChanged); + class.bindTemplateCallback("search_next_match", &searchNextMatch); + class.bindTemplateCallback("search_previous_match", &searchPreviousMatch); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.@"bell-ringing".impl, + properties.config.impl, + properties.@"child-exited".impl, + properties.@"default-size".impl, + properties.@"error".impl, + properties.@"font-size-request".impl, + properties.focused.impl, + properties.@"key-sequence".impl, + properties.@"key-table".impl, + properties.@"min-size".impl, + properties.@"mouse-shape".impl, + properties.@"mouse-hidden".impl, + properties.@"mouse-hover-url".impl, + properties.pwd.impl, + properties.title.impl, + properties.@"title-override".impl, + properties.zoom.impl, + properties.@"is-split".impl, + properties.readonly.impl, + + // For Gtk.Scrollable + properties.hadjustment.impl, + properties.vadjustment.impl, + properties.@"hscroll-policy".impl, + properties.@"vscroll-policy".impl, + }); + + // Signals + signals.bell.impl.register(.{}); + signals.@"close-request".impl.register(.{}); + signals.@"clipboard-read".impl.register(.{}); + signals.@"clipboard-write".impl.register(.{}); + signals.init.impl.register(.{}); + signals.menu.impl.register(.{}); + signals.@"present-request".impl.register(.{}); + signals.@"toggle-fullscreen".impl.register(.{}); + signals.@"toggle-maximize".impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; + + /// Simple dimensions struct for the surface used by various properties. + pub const Size = extern struct { + width: u32, + height: u32, + + pub const getGObjectType = gobject.ext.defineBoxed( + Size, + .{ .name = "GhosttySurfaceSize" }, + ); + }; +}; + +/// The state of the key event while we're doing IM composition. +/// See gtkKeyPressed for detailed descriptions. +pub const IMKeyEvent = enum { + /// Not in a key event. + false, + + /// In a key event but im_composing was either true or false + /// prior to the calling IME processing. This is important to + /// work around different input methods calling commit and + /// preedit end in a different order. + composing, + not_composing, +}; + +fn translateMouseButton(button: c_uint) input.MouseButton { + return switch (button) { + 1 => .left, + 2 => .middle, + 3 => .right, + 4 => .four, + 5 => .five, + 6 => .six, + 7 => .seven, + 8 => .eight, + 9 => .nine, + 10 => .ten, + 11 => .eleven, + else => .unknown, + }; +} + +/// A namespace for our clipboard-related functions so Surface isn't SO large. +const Clipboard = struct { + /// Set the clipboard contents. + pub fn set( + self: *Surface, + clipboard_type: apprt.Clipboard, + contents: []const apprt.ClipboardContent, + confirm: bool, + ) void { + const priv = self.private(); + + // Grab our plaintext content for use in confirmation dialogs + // and signals. We always expect one to exist. + const text: [:0]const u8 = for (contents) |content| { + if (std.mem.eql(u8, content.mime, "text/plain")) { + break content.data; + } + } else return; + + // If no confirmation is necessary, set the clipboard. + if (!confirm) { + const clipboard = get( + priv.gl_area.as(gtk.Widget), + clipboard_type, + ) orelse return; + + const alloc = Application.default().allocator(); + if (alloc.alloc(*gdk.ContentProvider, contents.len)) |providers| { + // Note: we don't need to unref the individual providers + // because new_union takes ownership of them. + defer alloc.free(providers); + + for (contents, 0..) |content, i| { + const bytes = glib.Bytes.new(content.data.ptr, content.data.len); + defer bytes.unref(); + if (std.mem.eql(u8, content.mime, "text/plain")) { + // Add an explicit UTF-8 encoding parameter to the + // text/plain type. The default charset when there is + // none is ASCII, and lots of things look for UTF-8 + // specifically. + // The specs are not clear about the order here, but + // some clients apparently pick the first match in the + // order we set here then garble up bare 'text/plain' + // with non-ASCII UTF-8 content, so offer UTF-8 first. + // + // Note that under X11, GTK automatically adds the + // UTF8_STRING atom when this is present. + const text_provider_atoms = [_][:0]const u8{ + "text/plain;charset=utf-8", + "text/plain", + }; + var text_providers: [text_provider_atoms.len]*gdk.ContentProvider = undefined; + for (text_provider_atoms, 0..) |atom, j| { + const provider = gdk.ContentProvider.newForBytes(atom, bytes); + text_providers[j] = provider; + } + const text_union = gdk.ContentProvider.newUnion( + &text_providers, + text_providers.len, + ); + providers[i] = text_union; + } else { + const provider = gdk.ContentProvider.newForBytes(content.mime, bytes); + providers[i] = provider; + } + } + + const all = gdk.ContentProvider.newUnion(providers.ptr, providers.len); + defer all.unref(); + _ = clipboard.setContent(all); + } else |_| { + // If we fail to alloc, we can at least set the text content. + clipboard.setText(text); + } + + Surface.signals.@"clipboard-write".impl.emit( + self, + null, + .{ clipboard_type, text.ptr }, + null, + ); + + return; + } + + showClipboardConfirmation( + self, + .{ .osc_52_write = clipboard_type }, + text, + ); + } + + /// Request data from the clipboard (read the clipboard). This + /// completes asynchronously and will call the `completeClipboardRequest` + /// core surface API when done. + /// + /// Returns true if the request was started, false if the clipboard + /// doesn't contain text (allowing performable keybinds to pass through). + pub fn request( + self: *Surface, + clipboard_type: apprt.Clipboard, + state: apprt.ClipboardRequest, + ) Allocator.Error!bool { + // Get our requested clipboard + const clipboard = get( + self.private().gl_area.as(gtk.Widget), + clipboard_type, + ) orelse return false; + + // For paste requests, check if clipboard has text format available. + // This is a synchronous check that allows performable keybinds to + // pass through when the clipboard contains non-text content (e.g., images). + if (state == .paste) { + const formats = clipboard.getFormats(); + if (formats.containGtype(gobject.ext.types.string) == 0) { + log.debug("clipboard has no text format, not starting paste request", .{}); + return false; + } + } + + // Allocate our userdata + const alloc = Application.default().allocator(); + const ud = try alloc.create(Request); + errdefer alloc.destroy(ud); + ud.* = .{ + // Important: we ref self here so that we can't free memory + // while we have an outstanding clipboard read. + .self = self.ref(), + .state = state, + }; + errdefer self.unref(); + + // Read + clipboard.readTextAsync( + null, + clipboardReadText, + ud, + ); + + return true; + } + + /// Paste explicit text directly into the surface, regardless of the + /// actual clipboard contents. + pub fn paste( + self: *Surface, + text: [:0]const u8, + ) void { + if (text.len == 0) return; + + const surface = self.private().core_surface orelse return; + surface.completeClipboardRequest( + .paste, + text, + false, + ) catch |err| switch (err) { + error.UnsafePaste, + error.UnauthorizedPaste, + => { + showClipboardConfirmation( + self, + .paste, + text, + ); + return; + }, + + else => { + log.warn( + "failed to complete clipboard request err={}", + .{err}, + ); + return; + }, + }; + } + + /// Get the specific type of clipboard for a widget. + fn get( + widget: *gtk.Widget, + clipboard: apprt.Clipboard, + ) ?*gdk.Clipboard { + return switch (clipboard) { + .standard => widget.getClipboard(), + .selection, .primary => widget.getPrimaryClipboard(), + }; + } + + fn showClipboardConfirmation( + self: *Surface, + req: apprt.ClipboardRequest, + str: [:0]const u8, + ) void { + // Build a text buffer for our contents + const contents_buf: *gtk.TextBuffer = .new(null); + defer contents_buf.unref(); + contents_buf.insertAtCursor(str, @intCast(str.len)); + + // Confirm + const dialog = gobject.ext.newInstance( + ClipboardConfirmationDialog, + .{ + .request = &req, + .@"can-remember" = switch (req) { + .osc_52_read, .osc_52_write => true, + .paste => false, + }, + .@"clipboard-contents" = contents_buf, + }, + ); + + _ = ClipboardConfirmationDialog.signals.confirm.connect( + dialog, + *Surface, + clipboardConfirmationConfirm, + self, + .{}, + ); + _ = ClipboardConfirmationDialog.signals.deny.connect( + dialog, + *Surface, + clipboardConfirmationDeny, + self, + .{}, + ); + + dialog.present(self.as(gtk.Widget)); + } + + fn clipboardConfirmationConfirm( + dialog: *ClipboardConfirmationDialog, + remember: bool, + self: *Surface, + ) callconv(.c) void { + const priv = self.private(); + const surface = priv.core_surface orelse return; + const req = dialog.getRequest() orelse return; + + // Handle remember + if (remember) switch (req.*) { + .osc_52_read => surface.config.clipboard_read = .allow, + .osc_52_write => surface.config.clipboard_write = .allow, + .paste => {}, + }; + + // Get our text + const text_buf = dialog.getClipboardContents() orelse return; + var text_val = gobject.ext.Value.new(?[:0]const u8); + defer text_val.unset(); + gobject.Object.getProperty( + text_buf.as(gobject.Object), + "text", + &text_val, + ); + const text = gobject.ext.Value.get( + &text_val, + ?[:0]const u8, + ) orelse return; + + surface.completeClipboardRequest( + req.*, + text, + true, + ) catch |err| { + log.warn("failed to complete clipboard request: {}", .{err}); + }; + } + + fn clipboardConfirmationDeny( + dialog: *ClipboardConfirmationDialog, + remember: bool, + self: *Surface, + ) callconv(.c) void { + const priv = self.private(); + const surface = priv.core_surface orelse return; + const req = dialog.getRequest() orelse return; + + // Handle remember + if (remember) switch (req.*) { + .osc_52_read => surface.config.clipboard_read = .deny, + .osc_52_write => surface.config.clipboard_write = .deny, + .paste => @panic("paste should not be able to be remembered"), + }; + } + + fn clipboardReadText( + source: ?*gobject.Object, + res: *gio.AsyncResult, + ud: ?*anyopaque, + ) callconv(.c) void { + const clipboard = gobject.ext.cast( + gdk.Clipboard, + source orelse return, + ) orelse return; + const req: *Request = @ptrCast(@alignCast(ud orelse return)); + + const alloc = Application.default().allocator(); + defer alloc.destroy(req); + + const self = req.self; + defer self.unref(); + + var gerr: ?*glib.Error = null; + const cstr_ = clipboard.readTextFinish(res, &gerr); + if (gerr) |err| { + defer err.free(); + log.warn( + "failed to read clipboard err={s}", + .{err.f_message orelse "(no message)"}, + ); + return; + } + const cstr = cstr_ orelse return; + defer glib.free(cstr); + const str = std.mem.sliceTo(cstr, 0); + + const surface = self.private().core_surface orelse return; + surface.completeClipboardRequest( + req.state, + str, + false, + ) catch |err| switch (err) { + error.UnsafePaste, + error.UnauthorizedPaste, + => { + showClipboardConfirmation( + self, + req.state, + str, + ); + return; + }, + + else => { + log.warn( + "failed to complete clipboard request err={}", + .{err}, + ); + return; + }, + }; + + Surface.signals.@"clipboard-read".impl.emit( + self, + null, + .{}, + null, + ); + } + + /// The request we send as userdata to the clipboard read. + const Request = struct { + /// "Self" is reffed so we can't dispose it until the clipboard + /// read is complete. Callers must unref when done. + self: *Surface, + state: apprt.ClipboardRequest, + }; +}; + +/// Compute a fraction [0.0, 1.0] from the supplied progress, which is clamped +/// to [0, 100]. +fn computeFraction(progress: u8) f64 { + return @as(f64, @floatFromInt(std.math.clamp(progress, 0, 100))) / 100.0; +} + +test "computeFraction" { + try std.testing.expectEqual(1.0, computeFraction(100)); + try std.testing.expectEqual(1.0, computeFraction(255)); + try std.testing.expectEqual(0.0, computeFraction(0)); + try std.testing.expectEqual(0.5, computeFraction(50)); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/surface_child_exited.zig b/vendor/libghostty-vt/src/apprt/gtk/class/surface_child_exited.zig new file mode 100644 index 00000000..d7dd41bc --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/surface_child_exited.zig @@ -0,0 +1,264 @@ +const std = @import("std"); +const adw = @import("adw"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const adw_version = @import("../adw_version.zig"); +const apprt = @import("../../../apprt.zig"); +const gresource = @import("../build/gresource.zig"); +const i18n = @import("../../../os/main.zig").i18n; +const Common = @import("../class.zig").Common; + +const log = std.log.scoped(.gtk_ghostty_surface_child_exited); + +pub const SurfaceChildExited = if (adw_version.supportsBanner()) + SurfaceChildExitedBanner +else + SurfaceChildExitedNoop; + +/// Child exited overlay based on adw.Banner introduced in +/// Adwaita 1.3. +const SurfaceChildExitedBanner = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttySurfaceChildExited", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const data = struct { + pub const name = "data"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*apprt.surface.Message.ChildExited, + .{ + .accessor = C.privateBoxedFieldAccessor("data"), + }, + ); + }; + }; + + pub const signals = struct { + /// Emitted when the banner would like to be closed. + pub const @"close-request" = struct { + pub const name = "close-request"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + + const Private = struct { + /// The child exited data sent by the apprt. + data: ?*apprt.surface.Message.ChildExited = null, + + // Template bindings + banner: *adw.Banner, + + pub var offset: c_int = 0; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + pub fn setData( + self: *Self, + data_: ?*const apprt.surface.Message.ChildExited, + ) void { + const priv = self.private(); + if (priv.data) |v| glib.ext.destroy(v); + const data = data_ orelse { + priv.data = null; + return; + }; + + const ptr = glib.ext.create(apprt.surface.Message.ChildExited); + ptr.* = data.*; + priv.data = ptr; + self.as(gobject.Object).notifyByPspec(properties.data.impl.param_spec); + } + + //--------------------------------------------------------------- + // Signal handlers + + fn propData( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + const banner = priv.banner; + const data = priv.data orelse { + // Not localized on purpose. + banner.as(adw.Banner).setTitle("This is a bug in Ghostty. Please report it."); + return; + }; + if (data.exit_code == 0) { + banner.as(adw.Banner).setTitle(i18n._("Command succeeded")); + self.as(gtk.Widget).addCssClass("normal"); + self.as(gtk.Widget).removeCssClass("abnormal"); + } else { + banner.as(adw.Banner).setTitle(i18n._("Command failed")); + self.as(gtk.Widget).removeCssClass("normal"); + self.as(gtk.Widget).addCssClass("abnormal"); + } + } + + fn closeButtonClicked( + _: *adw.Banner, + self: *Self, + ) callconv(.c) void { + signals.@"close-request".impl.emit( + self, + null, + .{}, + null, + ); + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.data) |v| { + glib.ext.destroy(v); + priv.data = null; + } + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 3, + .name = "surface-child-exited", + }), + ); + + // Template bindings + class.bindTemplateChildPrivate("banner", .{}); + class.bindTemplateCallback("clicked", &closeButtonClicked); + class.bindTemplateCallback("notify_data", &propData); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.data.impl, + }); + + // Signals + signals.@"close-request".impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; + +/// Empty widget that does nothing if we don't have a new enough +/// Adwaita version to support the child exited banner. +const SurfaceChildExitedNoop = extern struct { + /// Can be detected at comptime + pub const noop = true; + + const Self = @This(); + parent_instance: Parent, + pub const Parent = gtk.Widget; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttySurfaceChildExited", + .classInit = &Class.init, + .parent_class = &Class.parent, + }); + + pub const signals = struct { + pub const @"close-request" = struct { + pub const name = "close-request"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + + pub fn setData( + self: *Self, + _: ?*const apprt.surface.Message.ChildExited, + ) void { + signals.@"close-request".impl.emit( + self, + null, + .{}, + null, + ); + } + + const C = Common(Self, null); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + _ = class; + signals.@"close-request".impl.register(.{}); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/surface_scrolled_window.zig b/vendor/libghostty-vt/src/apprt/gtk/class/surface_scrolled_window.zig new file mode 100644 index 00000000..dd4f17d1 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/surface_scrolled_window.zig @@ -0,0 +1,228 @@ +const std = @import("std"); +const adw = @import("adw"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); +const gtk_version = @import("../gtk_version.zig"); + +const gresource = @import("../build/gresource.zig"); +const Common = @import("../class.zig").Common; +const Surface = @import("surface.zig").Surface; +const Config = @import("config.zig").Config; + +const log = std.log.scoped(.gtk_ghostty_surface_scrolled_window); + +/// A wrapper widget that embeds a Surface inside a GtkScrolledWindow. +/// This provides scrollbar functionality for the terminal surface. +/// The surface property can be set during initialization or changed +/// dynamically via the surface property. +pub const SurfaceScrolledWindow = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.Bin; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhostttySurfaceScrolledWindow", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Config, + .{ + .accessor = C.privateObjFieldAccessor("config"), + }, + ); + }; + + pub const surface = struct { + pub const name = "surface"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface, + .{ + .accessor = .{ + .getter = getSurfaceValue, + .setter = setSurfaceValue, + }, + }, + ); + }; + }; + + const Private = struct { + config: ?*Config = null, + config_binding: ?*gobject.Binding = null, + surface: ?*Surface = null, + scrolled_window: *gtk.ScrolledWindow, + pub var offset: c_int = 0; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + if (gtk_version.runtimeUntil(4, 20, 1)) self.disableKineticScroll(); + } + + fn disableKineticScroll(self: *Self) void { + // Until gtk 4.20.1 trackpads have kinetic scrolling behavior regardless + // of `Gtk.ScrolledWindow.kinetic_scrolling`. As a workaround, disable + // EventControllerScroll.kinetic + const controllers = self.private().scrolled_window.as(gtk.Widget).observeControllers(); + defer controllers.unref(); + var i: c_uint = 0; + while (controllers.getObject(i)) |obj| : (i += 1) { + defer obj.unref(); + const controller = gobject.ext.cast(gtk.EventControllerScroll, obj) orelse continue; + var flags = controller.getFlags(); + flags.kinetic = false; + controller.setFlags(flags); + } + } + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + + if (priv.config_binding) |binding| { + binding.as(gobject.Object).unref(); + priv.config_binding = null; + } + + if (priv.config) |v| { + v.unref(); + priv.config = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + fn getSurfaceValue(self: *Self, value: *gobject.Value) void { + gobject.ext.Value.set( + value, + self.private().surface, + ); + } + + fn setSurfaceValue(self: *Self, value: *const gobject.Value) void { + self.setSurface(gobject.ext.Value.get( + value, + ?*Surface, + )); + } + + pub fn getSurface(self: *Self) ?*Surface { + return self.private().surface; + } + + pub fn setSurface(self: *Self, surface_: ?*Surface) void { + const priv = self.private(); + + if (surface_ == priv.surface) return; + + self.as(gobject.Object).freezeNotify(); + defer self.as(gobject.Object).thawNotify(); + self.as(gobject.Object).notifyByPspec(properties.surface.impl.param_spec); + + priv.surface = surface_; + } + + fn closureScrollbarPolicy( + _: *Self, + config_: ?*Config, + ) callconv(.c) gtk.PolicyType { + const config = if (config_) |c| c.get() else return .automatic; + return switch (config.scrollbar) { + .never => .never, + .system => .automatic, + }; + } + + fn propSurface( + self: *Self, + _: *gobject.ParamSpec, + _: ?*anyopaque, + ) callconv(.c) void { + const priv = self.private(); + const child: *gtk.Widget = self.as(Parent).getChild().?; + const scrolled_window = gobject.ext.cast(gtk.ScrolledWindow, child).?; + scrolled_window.setChild(if (priv.surface) |s| s.as(gtk.Widget) else null); + + // Unbind old config binding if it exists + if (priv.config_binding) |binding| { + binding.as(gobject.Object).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( + properties.config.name, + self.as(gobject.Object), + properties.config.name, + .{ .sync_create = true }, + ); + } + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "surface-scrolled-window", + }), + ); + + // Bindings + class.bindTemplateCallback("scrollbar_policy", &closureScrollbarPolicy); + class.bindTemplateCallback("notify_surface", &propSurface); + class.bindTemplateChildPrivate("scrolled_window", .{}); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.config.impl, + properties.surface.impl, + }); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/tab.zig b/vendor/libghostty-vt/src/apprt/gtk/class/tab.zig new file mode 100644 index 00000000..0c60c8cc --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/tab.zig @@ -0,0 +1,592 @@ +const std = @import("std"); +const adw = @import("adw"); +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const configpkg = @import("../../../config.zig"); +const apprt = @import("../../../apprt.zig"); +const CoreSurface = @import("../../../Surface.zig"); +const ext = @import("../ext.zig"); +const gresource = @import("../build/gresource.zig"); +const Common = @import("../class.zig").Common; +const Config = @import("config.zig").Config; +const Application = @import("application.zig").Application; +const SplitTree = @import("split_tree.zig").SplitTree; +const Surface = @import("surface.zig").Surface; +const TitleDialog = @import("title_dialog.zig").TitleDialog; + +const log = std.log.scoped(.gtk_ghostty_window); + +pub const Tab = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = gtk.Box; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyTab", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + /// The active surface is the surface that should be receiving all + /// surface-targeted actions. This is usually the focused surface, + /// but may also not be focused if the user has selected a non-surface + /// widget. + pub const @"active-surface" = struct { + pub const name = "active-surface"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*Surface, + .{ + .getter = Self.getActiveSurface, + }, + ), + }, + ); + }; + + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Config, + .{ + .accessor = C.privateObjFieldAccessor("config"), + }, + ); + }; + + pub const @"split-tree" = struct { + pub const name = "split-tree"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*SplitTree, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*SplitTree, + .{ + .getter = getSplitTree, + }, + ), + }, + ); + }; + + pub const @"surface-tree" = struct { + pub const name = "surface-tree"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface.Tree, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*Surface.Tree, + .{ + .getter = getSurfaceTree, + }, + ), + }, + ); + }; + + pub const tooltip = struct { + pub const name = "tooltip"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("tooltip"), + }, + ); + }; + + pub const title = struct { + pub const name = "title"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("title"), + }, + ); + }; + pub const @"title-override" = struct { + pub const name = "title-override"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("title_override"), + }, + ); + }; + }; + + pub const signals = struct { + /// Emitted whenever the tab would like to be closed. + pub const @"close-request" = struct { + pub const name = "close-request"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{}, + void, + ); + }; + }; + + const Private = struct { + /// The configuration that this surface is using. + config: ?*Config = null, + + /// The title of this tab. This is usually bound to the active surface. + title: ?[:0]const u8 = null, + + /// The manually overridden title from `promptTabTitle`. + title_override: ?[:0]const u8 = null, + + /// The tooltip of this tab. This is usually bound to the active surface. + tooltip: ?[:0]const u8 = null, + + // Template bindings + split_tree: *SplitTree, + + pub var offset: c_int = 0; + }; + + /// Set the parent of this tab page. This only affects the first surface + /// ever created for a tab. If a surface was already created this does + /// nothing. + pub fn setParent(self: *Self, parent: *CoreSurface) void { + self.setParentWithContext(parent, .tab); + } + + pub fn setParentWithContext(self: *Self, parent: *CoreSurface, context: apprt.surface.NewSurfaceContext) void { + if (self.getActiveSurface()) |surface| { + surface.setParent(parent, context); + } + } + + pub fn new(config: ?*Config, overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }) *Self { + const tab = gobject.ext.newInstance(Tab, .{}); + + const priv: *Private = tab.private(); + + if (config) |c| priv.config = c.ref(); + + // If our configuration is null then we get the configuration + // from the application. + if (priv.config == null) { + const app = Application.default(); + priv.config = app.getConfig(); + } + + tab.as(gobject.Object).notifyByPspec(properties.config.impl.param_spec); + + // Create our initial surface in the split tree. + priv.split_tree.newSplit(.right, null, .{ + .command = overrides.command, + .working_directory = overrides.working_directory, + .title = overrides.title, + }) catch |err| switch (err) { + error.OutOfMemory => { + // TODO: We should make our "no surfaces" state more aesthetically + // pleasing and show something like an "Oops, something went wrong" + // message. For now, this is incredibly unlikely. + @panic("oom"); + }, + }; + + return tab; + } + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + // Init our actions + self.initActionMap(); + } + + fn initActionMap(self: *Self) void { + const s_param_type = glib.ext.VariantType.newFor([:0]const u8); + defer s_param_type.free(); + + const actions = [_]ext.actions.Action(Self){ + .init("close", actionClose, s_param_type), + .init("ring-bell", actionRingBell, null), + .init("next-page", actionNextPage, null), + .init("previous-page", actionPreviousPage, null), + .init("prompt-tab-title", actionPromptTabTitle, null), + }; + + _ = ext.actions.addAsGroup(Self, self, "tab", &actions); + } + + //--------------------------------------------------------------- + // Properties + + /// Overridden title. This will be generally be shown over the title + /// unless this is unset (null). + pub fn setTitleOverride(self: *Self, title: ?[:0]const u8) void { + const priv = self.private(); + if (priv.title_override) |v| glib.free(@ptrCast(@constCast(v))); + priv.title_override = null; + if (title) |v| priv.title_override = glib.ext.dupeZ(u8, v); + self.as(gobject.Object).notifyByPspec(properties.@"title-override".impl.param_spec); + } + fn titleDialogSet( + _: *TitleDialog, + title_ptr: [*:0]const u8, + self: *Self, + ) callconv(.c) void { + const title = std.mem.span(title_ptr); + self.setTitleOverride(if (title.len == 0) null else title); + } + pub fn promptTabTitle(self: *Self) void { + const priv = self.private(); + const dialog = TitleDialog.new(.tab, priv.title_override orelse priv.title); + _ = TitleDialog.signals.set.connect( + dialog, + *Self, + titleDialogSet, + self, + .{}, + ); + + dialog.present(self.as(gtk.Widget)); + } + + /// Get the currently active surface. See the "active-surface" property. + /// This does not ref the value. + pub fn getActiveSurface(self: *Self) ?*Surface { + return self.getSplitTree().getActiveSurface(); + } + + /// Get the surface tree of this tab. + pub fn getSurfaceTree(self: *Self) ?*Surface.Tree { + const priv = self.private(); + return priv.split_tree.getTree(); + } + + /// Get the split tree widget that is in this tab. + pub fn getSplitTree(self: *Self) *SplitTree { + const priv = self.private(); + return priv.split_tree; + } + + /// Returns true if this tab needs confirmation before quitting based + /// on the various Ghostty configurations. + pub fn getNeedsConfirmQuit(self: *Self) bool { + const tree = self.getSplitTree(); + return tree.getNeedsConfirmQuit(); + } + + /// Get the tab view holding this tab, if any. + fn getTabView(self: *Self) ?*adw.TabView { + return ext.getAncestor( + adw.TabView, + self.as(gtk.Widget), + ); + } + + /// Get the tab page holding this tab, if any. + fn getTabPage(self: *Self) ?*adw.TabPage { + const tab_view = self.getTabView() orelse return null; + return tab_view.getPage(self.as(gtk.Widget)); + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.config) |v| { + v.unref(); + priv.config = null; + } + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.tooltip) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.tooltip = null; + } + if (priv.title) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.title = null; + } + if (priv.title_override) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.title_override = null; + } + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + //--------------------------------------------------------------- + // Signal handlers + + fn propSplitTree( + _: *SplitTree, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.as(gobject.Object).notifyByPspec(properties.@"surface-tree".impl.param_spec); + + // If our tree is empty we close the tab. + const tree: *const Surface.Tree = self.getSurfaceTree() orelse &.empty; + if (tree.isEmpty()) { + signals.@"close-request".impl.emit( + self, + null, + .{}, + null, + ); + return; + } + } + + fn propActiveSurface( + _: *SplitTree, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec); + } + + fn actionClose( + _: *gio.SimpleAction, + param_: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const param = param_ orelse { + log.warn("tab.close-tab called without a parameter", .{}); + return; + }; + + var str: ?[*:0]const u8 = null; + param.get("&s", &str); + + const tab_view = self.getTabView() orelse return; + const page = tab_view.getPage(self.as(gtk.Widget)); + + const mode = std.meta.stringToEnum( + apprt.action.CloseTabMode, + std.mem.span( + str orelse { + log.warn("invalid mode provided to tab.close-tab", .{}); + return; + }, + ), + ) orelse { + // Need to be defensive here since actions can be triggered externally. + log.warn("invalid mode provided to tab.close-tab: {s}", .{str.?}); + return; + }; + + // Delegate to our parent to handle this, since this will emit + // a close-page signal that the parent can intercept. + switch (mode) { + .this => tab_view.closePage(page), + .other => tab_view.closeOtherPages(page), + .right => tab_view.closePagesAfter(page), + } + } + + fn actionPromptTabTitle( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + self.promptTabTitle(); + } + + fn actionRingBell( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + // Future note: I actually don't like this logic living here at all. + // I think a better approach will be for the ring bell action to + // specify its sending surface and then do all this in the window. + + // If the page is selected already we don't mark it as needing + // attention. We only want to mark unfocused pages. This will then + // clear when the page is selected. + const page = self.getTabPage() orelse return; + if (page.getSelected() != 0) return; + page.setNeedsAttention(@intFromBool(true)); + } + + /// Select the next tab page. + fn actionNextPage( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const tab_view = self.getTabView() orelse return; + _ = tab_view.selectNextPage(); + } + + /// Select the previous tab page. + fn actionPreviousPage( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const tab_view = self.getTabView() orelse return; + _ = tab_view.selectPreviousPage(); + } + + fn closureComputedTitle( + _: *Self, + config_: ?*Config, + terminal_: ?[*:0]const u8, + surface_override_: ?[*:0]const u8, + tab_override_: ?[*:0]const u8, + zoomed_: c_int, + bell_ringing_: c_int, + _: *gobject.ParamSpec, + ) callconv(.c) ?[*:0]const u8 { + const zoomed = zoomed_ != 0; + const bell_ringing = bell_ringing_ != 0; + + // Our plain title is the manually tab overridden title if it exists, + // otherwise the overridden title if it exists, otherwise + // the terminal title if it exists, otherwise a default string. + const plain = plain: { + const default = "Ghostty"; + const config_title: ?[*:0]const u8 = title: { + const config = config_ orelse break :title null; + break :title config.get().title orelse null; + }; + + const plain = tab_override_ orelse + surface_override_ orelse + terminal_ orelse + config_title orelse + break :plain default; + break :plain std.mem.span(plain); + }; + + // We don't need a config in every case, but if we don't have a config + // let's just assume something went terribly wrong and use our + // default title. Its easier then guarding on the config existing + // in every case for something so unlikely. + const config = if (config_) |v| v.get() else { + log.warn("config unavailable for computed title, likely bug", .{}); + return glib.ext.dupeZ(u8, plain); + }; + + // Use an allocator to build up our string as we write it. + var buf: std.Io.Writer.Allocating = .init(Application.default().allocator()); + defer buf.deinit(); + + // If our bell is ringing, then we prefix the bell icon to the title. + if (bell_ringing and config.@"bell-features".title) { + buf.writer.writeAll("๐Ÿ”” ") catch {}; + } + + // If we're zoomed, prefix with the magnifying glass emoji. + if (zoomed) { + buf.writer.writeAll("๐Ÿ” ") catch {}; + } + + buf.writer.writeAll(plain) catch return glib.ext.dupeZ(u8, plain); + return glib.ext.dupeZ(u8, buf.written()); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(SplitTree); + gobject.ext.ensureType(Surface); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "tab", + }), + ); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.@"active-surface".impl, + properties.config.impl, + properties.@"split-tree".impl, + properties.@"surface-tree".impl, + properties.title.impl, + properties.@"title-override".impl, + properties.tooltip.impl, + }); + + // Bindings + class.bindTemplateChildPrivate("split_tree", .{}); + + // Template Callbacks + class.bindTemplateCallback("computed_title", &closureComputedTitle); + class.bindTemplateCallback("notify_active_surface", &propActiveSurface); + class.bindTemplateCallback("notify_tree", &propSplitTree); + + // Signals + signals.@"close-request".impl.register(.{}); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/title_dialog.zig b/vendor/libghostty-vt/src/apprt/gtk/class/title_dialog.zig new file mode 100644 index 00000000..ac95ae4b --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/title_dialog.zig @@ -0,0 +1,234 @@ +const std = @import("std"); +const adw = @import("adw"); +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gresource = @import("../build/gresource.zig"); +const i18n = @import("../../../os/main.zig").i18n; +const ext = @import("../ext.zig"); +const Common = @import("../class.zig").Common; +const Dialog = @import("dialog.zig").Dialog; + +const log = std.log.scoped(.gtk_ghostty_title_dialog); + +pub const TitleDialog = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.AlertDialog; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyTitleDialog", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + pub const target = struct { + pub const name = "target"; + const impl = gobject.ext.defineProperty( + name, + Self, + Target, + .{ + .default = .surface, + .accessor = gobject.ext + .privateFieldAccessor( + Self, + Private, + &Private.offset, + "target", + ), + }, + ); + }; + pub const @"initial-value" = struct { + pub const name = "initial-value"; + pub const get = impl.get; + pub const set = impl.set; + const impl = gobject.ext.defineProperty( + name, + Self, + ?[:0]const u8, + .{ + .default = null, + .accessor = C.privateStringFieldAccessor("initial_value"), + }, + ); + }; + }; + + pub const signals = struct { + /// Set the title to the given value. + pub const set = struct { + pub const name = "set"; + pub const connect = impl.connect; + const impl = gobject.ext.defineSignal( + name, + Self, + &.{[*:0]const u8}, + void, + ); + }; + }; + + const Private = struct { + /// The initial value of the entry field. + initial_value: ?[:0]const u8 = null, + + // Template bindings + target: Target, + entry: *gtk.Entry, + + pub var offset: c_int = 0; + }; + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + } + + pub fn new(target: Target, initial_value: ?[:0]const u8) *Self { + return gobject.ext.newInstance(Self, .{ .target = target, .@"initial-value" = initial_value }); + } + + pub fn present(self: *Self, parent_: *gtk.Widget) void { + // If we have a window we can attach to, we prefer that. + const parent: *gtk.Widget = if (ext.getAncestor( + adw.ApplicationWindow, + parent_, + )) |window| + window.as(gtk.Widget) + else if (ext.getAncestor( + adw.Window, + parent_, + )) |window| + window.as(gtk.Widget) + else + parent_; + + // Set our initial value + const priv = self.private(); + if (priv.initial_value) |v| { + priv.entry.getBuffer().setText(v, -1); + } + + // Set the title for the dialog + self.as(Dialog.Parent).setHeading(priv.target.title()); + + // Show it. We could also just use virtual methods to bind to + // response but this is pretty simple. + self.as(adw.AlertDialog).choose( + parent, + null, + alertDialogReady, + self, + ); + } + + fn alertDialogReady( + _: ?*gobject.Object, + result: *gio.AsyncResult, + ud: ?*anyopaque, + ) callconv(.c) void { + const self: *Self = @ptrCast(@alignCast(ud)); + const response = self.as(adw.AlertDialog).chooseFinish(result); + + // If we didn't hit "okay" then we do nothing. + if (std.mem.orderZ(u8, "ok", response) != .eq) return; + + // Emit our signal with the new title. + const title = std.mem.span(self.private().entry.getBuffer().getText()); + signals.set.impl.emit( + self, + null, + .{title.ptr}, + null, + ); + } + + fn dispose(self: *Self) callconv(.c) void { + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + if (priv.initial_value) |v| { + glib.free(@ptrCast(@constCast(v))); + priv.initial_value = null; + } + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "title-dialog", + }), + ); + + // Signals + signals.set.impl.register(.{}); + + // Bindings + class.bindTemplateChildPrivate("entry", .{}); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.@"initial-value".impl, + properties.target.impl, + }); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; + +pub const Target = enum(c_int) { + surface, + tab, + pub fn title(self: Target) [*:0]const u8 { + return switch (self) { + .surface => i18n._("Change Terminal Title"), + .tab => i18n._("Change Tab Title"), + }; + } + + pub const getGObjectType = gobject.ext.defineEnum( + Target, + .{ .name = "GhosttyTitleDialogTarget" }, + ); +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/class/window.zig b/vendor/libghostty-vt/src/apprt/gtk/class/window.zig new file mode 100644 index 00000000..bf2a2fe7 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/class/window.zig @@ -0,0 +1,2111 @@ +const std = @import("std"); +const build_config = @import("../../../build_config.zig"); +const assert = @import("../../../quirks.zig").inlineAssert; +const adw = @import("adw"); +const gdk = @import("gdk"); +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const i18n = @import("../../../os/main.zig").i18n; +const apprt = @import("../../../apprt.zig"); +const configpkg = @import("../../../config.zig"); +const TitlebarStyle = configpkg.Config.GtkTitlebarStyle; +const input = @import("../../../input.zig"); +const CoreSurface = @import("../../../Surface.zig"); +const ext = @import("../ext.zig"); +const gtk_version = @import("../gtk_version.zig"); +const adw_version = @import("../adw_version.zig"); +const gresource = @import("../build/gresource.zig"); +const winprotopkg = @import("../winproto.zig"); +const Common = @import("../class.zig").Common; +const Config = @import("config.zig").Config; +const Application = @import("application.zig").Application; +const CloseConfirmationDialog = @import("close_confirmation_dialog.zig").CloseConfirmationDialog; +const SplitTree = @import("split_tree.zig").SplitTree; +const Surface = @import("surface.zig").Surface; +const Tab = @import("tab.zig").Tab; +const DebugWarning = @import("debug_warning.zig").DebugWarning; +const CommandPalette = @import("command_palette.zig").CommandPalette; +const WeakRef = @import("../weak_ref.zig").WeakRef; + +const log = std.log.scoped(.gtk_ghostty_window); + +pub const Window = extern struct { + const Self = @This(); + parent_instance: Parent, + pub const Parent = adw.ApplicationWindow; + pub const getGObjectType = gobject.ext.defineClass(Self, .{ + .name = "GhosttyWindow", + .instanceInit = &init, + .classInit = &Class.init, + .parent_class = &Class.parent, + .private = .{ .Type = Private, .offset = &Private.offset }, + }); + + pub const properties = struct { + /// The active surface is the focus that should be receiving all + /// surface-targeted actions. This is usually the focused surface, + /// but may also not be focused if the user has selected a non-surface + /// widget. + pub const @"active-surface" = struct { + pub const name = "active-surface"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Surface, + .{ + .accessor = gobject.ext.typedAccessor( + Self, + ?*Surface, + .{ + .getter = Self.getActiveSurface, + }, + ), + }, + ); + }; + + pub const config = struct { + pub const name = "config"; + const impl = gobject.ext.defineProperty( + name, + Self, + ?*Config, + .{ + .accessor = C.privateObjFieldAccessor("config"), + }, + ); + }; + + pub const debug = struct { + pub const name = "debug"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = build_config.is_debug, + .accessor = gobject.ext.typedAccessor(Self, bool, .{ + .getter = struct { + pub fn getter(_: *Self) bool { + return build_config.is_debug; + } + }.getter, + }), + }, + ); + }; + + pub const @"titlebar-style" = struct { + pub const name = "titlebar-style"; + const impl = gobject.ext.defineProperty( + name, + Self, + TitlebarStyle, + .{ + .default = .native, + .accessor = gobject.ext.typedAccessor( + Self, + TitlebarStyle, + .{ + .getter = Self.getTitlebarStyle, + }, + ), + }, + ); + }; + + pub const @"headerbar-visible" = struct { + pub const name = "headerbar-visible"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = true, + .accessor = gobject.ext.typedAccessor(Self, bool, .{ + .getter = Self.getHeaderbarVisible, + }), + }, + ); + }; + + pub const @"quick-terminal" = struct { + pub const name = "quick-terminal"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = true, + .accessor = gobject.ext.privateFieldAccessor( + Self, + Private, + &Private.offset, + "quick_terminal", + ), + }, + ); + }; + + pub const @"tabs-autohide" = struct { + pub const name = "tabs-autohide"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = true, + .accessor = gobject.ext.typedAccessor(Self, bool, .{ + .getter = Self.getTabsAutohide, + }), + }, + ); + }; + + pub const @"tabs-wide" = struct { + pub const name = "tabs-wide"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = true, + .accessor = gobject.ext.typedAccessor(Self, bool, .{ + .getter = Self.getTabsWide, + }), + }, + ); + }; + + pub const @"tabs-visible" = struct { + pub const name = "tabs-visible"; + const impl = gobject.ext.defineProperty( + name, + Self, + bool, + .{ + .default = true, + .accessor = gobject.ext.typedAccessor(Self, bool, .{ + .getter = Self.getTabsVisible, + }), + }, + ); + }; + + pub const @"toolbar-style" = struct { + pub const name = "toolbar-style"; + const impl = gobject.ext.defineProperty( + name, + Self, + adw.ToolbarStyle, + .{ + .default = .raised, + .accessor = gobject.ext.typedAccessor( + Self, + adw.ToolbarStyle, + .{ + .getter = Self.getToolbarStyle, + }, + ), + }, + ); + }; + }; + + const Private = struct { + /// Whether this window is a quick terminal. If it is then it + /// behaves slightly differently under certain scenarios. + quick_terminal: bool = false, + + /// The window decoration override. If this is not set then we'll + /// inherit whatever the config has. This allows overriding the + /// config on a per-window basis. + window_decoration: ?configpkg.WindowDecoration = null, + + /// Binding group for our active tab. + tab_bindings: *gobject.BindingGroup, + + /// The configuration that this surface is using. + config: ?*Config = null, + + /// State and logic for windowing protocol for a window. + winproto: winprotopkg.Window, + + /// Kind of hacky to have this but this lets us know if we've + /// initialized any single surface yet. We need this because we + /// gate default size on this so that we don't resize the window + /// after surfaces already exist. + /// + /// I think long term we can probably get rid of this by implementing + /// a property or method that gets us all the surfaces in all the + /// tabs and checking if we have zero or one that isn't initialized. + /// + /// For now, this logic is more similar to our legacy GTK side. + surface_init: bool = false, + + /// See tabOverviewOpen for why we have this. + tab_overview_focus_timer: ?c_uint = null, + + /// A weak reference to a command palette. + command_palette: WeakRef(CommandPalette) = .empty, + + /// Tab page that the context menu was opened for. + /// setup by `setup-menu`. + context_menu_page: ?*adw.TabPage = null, + + // Template bindings + tab_overview: *adw.TabOverview, + tab_bar: *adw.TabBar, + tab_view: *adw.TabView, + toolbar: *adw.ToolbarView, + toast_overlay: *adw.ToastOverlay, + + pub var offset: c_int = 0; + }; + + pub fn new( + app: *Application, + overrides: struct { + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }, + ) *Self { + const win = gobject.ext.newInstance(Self, .{ + .application = app, + }); + + if (overrides.title) |title| { + // If the overrides have a title set, we set that immediately + // so that any applications inspecting the window states see an + // immediate title set when the window appears, rather than waiting + // possibly a few event loop ticks for it to sync from the surface. + win.as(gtk.Window).setTitle(title); + } + + return win; + } + + fn init(self: *Self, _: *Class) callconv(.c) void { + gtk.Widget.initTemplate(self.as(gtk.Widget)); + + // If our configuration is null then we get the configuration + // from the application. + const priv = self.private(); + + const config = config: { + if (priv.config) |config| break :config config.get(); + const app = Application.default(); + const config = app.getConfig(); + priv.config = config; + break :config config.get(); + }; + + // We initialize our windowing protocol to none because we can't + // actually initialize this until we get realized. + priv.winproto = .none; + + // Add our dev CSS class if we're in debug mode. + if (comptime build_config.is_debug) { + self.as(gtk.Widget).addCssClass("devel"); + } + + // Setup our tab binding group. This ensures certain properties + // are only synced from the currently active tab. + priv.tab_bindings = gobject.BindingGroup.new(); + priv.tab_bindings.bind("title", self.as(gobject.Object), "title", .{}); + + // Set our window icon. We can't set this in the blueprint file + // because its dependent on the build config. + self.as(gtk.Window).setIconName(build_config.bundle_id); + + // Initialize our actions + self.initActionMap(); + + // Start states based on config. + if (config.maximize) self.as(gtk.Window).maximize(); + if (config.fullscreen != .false) self.as(gtk.Window).fullscreen(); + + // If we have an explicit title set, we set that immediately + // so that any applications inspecting the window states see + // an immediate title set when the window appears, rather than + // waiting possibly a few event loop ticks for it to sync from + // the surface. + if (config.title) |title| { + self.as(gtk.Window).setTitle(title); + } + + // We always sync our appearance at the end because loading our + // config and such can affect our bindings which are setup initially + // in initTemplate. + self.syncAppearance(); + + // We need to do this so that the title initializes properly, + // I think because its a dynamic getter. + self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec); + } + + /// Setup our action map. + fn initActionMap(self: *Self) void { + const s_variant_type = glib.ext.VariantType.newFor([:0]const u8); + defer s_variant_type.free(); + + const actions = [_]ext.actions.Action(Self){ + .init("about", actionAbout, null), + .init("close", actionClose, null), + .init("close-tab", actionCloseTab, s_variant_type), + .init("new-tab", actionNewTab, null), + .init("new-window", actionNewWindow, null), + .init("prompt-surface-title", actionPromptSurfaceTitle, null), + .init("prompt-tab-title", actionPromptTabTitle, null), + .init("prompt-context-tab-title", actionPromptContextTabTitle, null), + .init("ring-bell", actionRingBell, null), + .init("split-right", actionSplitRight, null), + .init("split-left", actionSplitLeft, null), + .init("split-up", actionSplitUp, null), + .init("split-down", actionSplitDown, null), + .init("copy", actionCopy, null), + .init("paste", actionPaste, null), + .init("reset", actionReset, null), + .init("clear", actionClear, null), + // TODO: accept the surface that toggled the command palette + .init("toggle-command-palette", actionToggleCommandPalette, null), + .init("toggle-inspector", actionToggleInspector, null), + }; + + ext.actions.add(Self, self, &actions); + } + + /// Winproto backend for this window. + pub fn winproto(self: *Self) *winprotopkg.Window { + return &self.private().winproto; + } + + /// Create a new tab with the given parent. The tab will be inserted + /// at the position dictated by the `window-new-tab-position` config. + /// The new tab will be selected. + pub fn newTab(self: *Self, parent_: ?*CoreSurface) void { + _ = self.newTabPage(parent_, .tab, .none); + } + + pub fn newTabForWindow( + self: *Self, + parent_: ?*CoreSurface, + overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }, + ) void { + _ = self.newTabPage( + parent_, + .window, + .{ + .command = overrides.command, + .working_directory = overrides.working_directory, + .title = overrides.title, + }, + ); + } + + fn newTabPage( + self: *Self, + parent_: ?*CoreSurface, + context: apprt.surface.NewSurfaceContext, + overrides: struct { + command: ?configpkg.Command = null, + working_directory: ?[:0]const u8 = null, + title: ?[:0]const u8 = null, + + pub const none: @This() = .{}; + }, + ) *adw.TabPage { + const priv: *Private = self.private(); + const tab_view = priv.tab_view; + + // Create our new tab object + const tab = Tab.new( + priv.config, + .{ + .command = overrides.command, + .working_directory = overrides.working_directory, + .title = overrides.title, + }, + ); + + if (parent_) |p| { + // For a new window's first tab, inherit the parent's initial size hints. + if (context == .window) { + surfaceInit(p.rt_surface.gobj(), self); + } + tab.setParentWithContext(p, context); + } + + // Get the position that we should insert the new tab at. + const config = if (priv.config) |v| v.get() else { + // If we don't have a config we just append it at the end. + // This should never happen. + return tab_view.append(tab.as(gtk.Widget)); + }; + const position = switch (config.@"window-new-tab-position") { + .current => current: { + const selected = tab_view.getSelectedPage() orelse + break :current tab_view.getNPages(); + const current = tab_view.getPagePosition(selected); + break :current current + 1; + }, + + .end => tab_view.getNPages(), + }; + + // Add the page and select it + const page = tab_view.insert(tab.as(gtk.Widget), position); + tab_view.setSelectedPage(page); + + // Create some property bindings + _ = tab.as(gobject.Object).bindProperty( + "title", + page.as(gobject.Object), + "title", + .{ .sync_create = true }, + ); + _ = tab.as(gobject.Object).bindProperty( + "tooltip", + page.as(gobject.Object), + "tooltip", + .{ .sync_create = true }, + ); + + // Bind signals + const split_tree = tab.getSplitTree(); + _ = SplitTree.signals.changed.connect( + split_tree, + *Self, + tabSplitTreeChanged, + self, + .{}, + ); + + // Run an initial notification for the surface tree so we can setup + // initial state. + tabSplitTreeChanged( + split_tree, + null, + split_tree.getTree(), + self, + ); + + return page; + } + + pub const SelectTab = union(enum) { + previous, + next, + last, + n: usize, + }; + + /// Select the tab as requested. Returns true if the tab selection + /// changed. + pub fn selectTab(self: *Self, n: SelectTab) bool { + const priv = self.private(); + const tab_view = priv.tab_view; + + // Get our current tab numeric position + const selected = tab_view.getSelectedPage() orelse return false; + const current = tab_view.getPagePosition(selected); + + // Get our total + const total = tab_view.getNPages(); + + const goto: c_int = switch (n) { + .previous => if (current > 0) + current - 1 + else + total - 1, + + .next => if (current < total - 1) + current + 1 + else + 0, + + .last => total - 1, + + .n => |v| n: { + // 1-indexed + if (v == 0) return false; + + const n_int = std.math.cast( + c_int, + v, + ) orelse return false; + break :n @min(n_int - 1, total - 1); + }, + }; + assert(goto >= 0); + assert(goto < total); + + // If our target is the same as our current then we do nothing. + if (goto == current) return false; + + // Add the page and select it + const page = tab_view.getNthPage(goto); + tab_view.setSelectedPage(page); + + return true; + } + + /// Move the tab containing the given surface by the given amount. + /// Returns if this affected any tab positioning. + pub fn moveTab( + self: *Self, + surface: *Surface, + amount: isize, + ) bool { + const priv = self.private(); + const tab_view = priv.tab_view; + + // If we have one tab we never move. + const total = tab_view.getNPages(); + if (total == 1) return false; + + // Get the tab that contains the given surface. + const tab = ext.getAncestor( + Tab, + surface.as(gtk.Widget), + ) orelse return false; + + // Get the page position that contains the tab. + const page = tab_view.getPage(tab.as(gtk.Widget)); + const pos = tab_view.getPagePosition(page); + + // Move it + const desired_pos: c_int = desired: { + const initial: c_int = @intCast(pos + amount); + const max = total - 1; + break :desired if (initial < 0) + max + initial + 1 + else if (initial > max) + initial - max - 1 + else + initial; + }; + assert(desired_pos >= 0); + assert(desired_pos < total); + + return tab_view.reorderPage(page, desired_pos) != 0; + } + + pub fn toggleTabOverview(self: *Self) void { + const priv = self.private(); + const tab_overview = priv.tab_overview; + const is_open = tab_overview.getOpen() != 0; + tab_overview.setOpen(@intFromBool(!is_open)); + } + + /// Toggle the visible property. + pub fn toggleVisibility(self: *Self) void { + const widget = self.as(gtk.Widget); + widget.setVisible(@intFromBool(widget.isVisible() == 0)); + } + + /// Updates various appearance properties. This should always be safe + /// to call multiple times. This should be called whenever a change + /// happens that might affect how the window appears (config change, + /// fullscreen, etc.). + fn syncAppearance(self: *Self) void { + const priv = self.private(); + const widget = self.as(gtk.Widget); + + // Toggle style classes based on whether we're using CSDs or SSDs. + // + // These classes are defined in the gtk.Window documentation: + // https://docs.gtk.org/gtk4/class.Window.html#css-nodes. + { + // Reset all style classes first + inline for (&.{ + "ssd", + "csd", + "solid-csd", + "no-border-radius", + }) |class| + widget.removeCssClass(class); + + const csd_enabled = priv.winproto.clientSideDecorationEnabled(); + self.as(gtk.Window).setDecorated(@intFromBool(csd_enabled)); + + if (csd_enabled) { + const display = widget.getDisplay(); + + // We do the exact same check GTK is doing internally and toggle + // either the `csd` or `solid-csd` style, based on whether the user's + // window manager is deemed _non-compositing_. + // + // In practice this only impacts users of traditional X11 window + // managers (e.g. i3, dwm, awesomewm, etc.) and not X11 desktop + // environments or Wayland compositors/DEs. + if (display.isRgba() != 0 and display.isComposited() != 0) { + widget.addCssClass("csd"); + } else { + widget.addCssClass("solid-csd"); + } + } else { + widget.addCssClass("ssd"); + // Fix any artifacting that may occur in window corners. + widget.addCssClass("no-border-radius"); + } + } + + // Trigger all our dynamic properties that depend on the config. + inline for (&.{ + "headerbar-visible", + "tabs-autohide", + "tabs-visible", + "tabs-wide", + "toolbar-style", + "titlebar-style", + }) |key| { + self.as(gobject.Object).notifyByPspec( + @field(properties, key).impl.param_spec, + ); + } + + // Remainder uses the config + const config = if (priv.config) |v| v.get() else return; + + // Only add a solid background if we're opaque. + self.toggleCssClass( + "background", + config.@"background-opacity" >= 1, + ); + + // Apply class to color headerbar if window-theme is set to `ghostty` and + // GTK version is before 4.16. The conditional is because above 4.16 + // we use GTK CSS color variables. + self.toggleCssClass( + "window-theme-ghostty", + !gtk_version.atLeast(4, 16, 0) and + config.@"window-theme" == .ghostty, + ); + + // Move the tab bar to the proper location. + priv.toolbar.remove(priv.tab_bar.as(gtk.Widget)); + switch (config.@"gtk-tabs-location") { + .top => priv.toolbar.addTopBar(priv.tab_bar.as(gtk.Widget)), + .bottom => priv.toolbar.addBottomBar(priv.tab_bar.as(gtk.Widget)), + } + + // Do our window-protocol specific appearance sync. + priv.winproto.syncAppearance() catch |err| { + log.warn("failed to sync winproto appearance error={}", .{err}); + }; + } + + /// Sync the state of any actions on this window. + fn syncActions(self: *Self) void { + const has_selection = selection: { + const surface = self.getActiveSurface() orelse + break :selection false; + const core_surface = surface.core() orelse + break :selection false; + break :selection core_surface.hasSelection(); + }; + + const action_map: *gio.ActionMap = gobject.ext.cast( + gio.ActionMap, + self, + ) orelse return; + const action: *gio.SimpleAction = gobject.ext.cast( + gio.SimpleAction, + action_map.lookupAction("copy") orelse return, + ) orelse return; + action.setEnabled(@intFromBool(has_selection)); + } + + fn toggleCssClass(self: *Self, class: [:0]const u8, value: bool) void { + const widget = self.as(gtk.Widget); + if (value) + widget.addCssClass(class.ptr) + else + widget.removeCssClass(class.ptr); + } + + /// Perform a binding action on the window's active surface. + fn performBindingAction( + self: *Self, + action: input.Binding.Action, + ) void { + const surface = self.getActiveSurface() orelse return; + const core_surface = surface.core() orelse return; + _ = core_surface.performBindingAction(action) catch |err| { + log.warn("error performing binding action error={}", .{err}); + return; + }; + } + + /// Queue a simple text-based toast. All text-based toasts share the + /// same timeout for consistency. + /// + // This is not `pub` because we should be using signals emitted by + // other widgets to trigger our toasts. Other objects should not + // trigger toasts directly. + fn addToast(self: *Self, title: [*:0]const u8) void { + const toast = adw.Toast.new(title); + toast.setTimeout(3); + self.private().toast_overlay.addToast(toast); + } + + fn connectSurfaceHandlers( + self: *Self, + tree: *const Surface.Tree, + ) void { + const priv = self.private(); + var it = tree.iterator(); + while (it.next()) |entry| { + const surface = entry.view; + // Before adding any new signal handlers, disconnect any that we may + // have added before. Otherwise we may get multiple handlers for the + // same signal. + _ = gobject.signalHandlersDisconnectMatched( + surface.as(gobject.Object), + .{ .data = true }, + 0, + 0, + null, + null, + self, + ); + + _ = Surface.signals.@"present-request".connect( + surface, + *Self, + surfacePresentRequest, + self, + .{}, + ); + _ = Surface.signals.@"clipboard-write".connect( + surface, + *Self, + surfaceClipboardWrite, + self, + .{}, + ); + _ = Surface.signals.menu.connect( + surface, + *Self, + surfaceMenu, + self, + .{}, + ); + _ = Surface.signals.@"toggle-fullscreen".connect( + surface, + *Self, + surfaceToggleFullscreen, + self, + .{}, + ); + _ = Surface.signals.@"toggle-maximize".connect( + surface, + *Self, + surfaceToggleMaximize, + self, + .{}, + ); + + // If we've never had a surface initialize yet, then we register + // this signal. Its theoretically possible to launch multiple surfaces + // before init so we could register this on multiple and that is not + // a problem because we'll check the flag again in each handler. + if (!priv.surface_init) { + _ = Surface.signals.init.connect( + surface, + *Self, + surfaceInit, + self, + .{}, + ); + } + } + } + + /// Disconnect all the surface handlers for the given tree. This should + /// be called whenever a tree is no longer present in the window, e.g. + /// when a tab is detached or the tree changes. + fn disconnectSurfaceHandlers( + self: *Self, + tree: *const Surface.Tree, + ) void { + var it = tree.iterator(); + while (it.next()) |entry| { + const surface = entry.view; + _ = gobject.signalHandlersDisconnectMatched( + surface.as(gobject.Object), + .{ .data = true }, + 0, + 0, + null, + null, + self, + ); + } + } + + //--------------------------------------------------------------- + // Properties + + /// Whether this terminal is a quick terminal or not. + pub fn isQuickTerminal(self: *Self) bool { + return self.private().quick_terminal; + } + + /// Get the currently active surface. See the "active-surface" property. + /// This does not ref the value. + pub fn getActiveSurface(self: *Self) ?*Surface { + const tab = self.getSelectedTab() orelse return null; + return tab.getActiveSurface(); + } + + /// Returns the configuration for this window. The reference count + /// is not increased. + pub fn getConfig(self: *Self) ?*Config { + return self.private().config; + } + + /// Get the tab view for this window. + pub fn getTabView(self: *Self) *adw.TabView { + return self.private().tab_view; + } + + /// Get the current window decoration value for this window. + pub fn getWindowDecoration(self: *Self) configpkg.WindowDecoration { + const priv = self.private(); + if (priv.window_decoration) |v| return v; + if (priv.config) |v| return v.get().@"window-decoration"; + return .auto; + } + + /// Toggle the window decorations for this window. + pub fn toggleWindowDecorations(self: *Self) void { + const priv = self.private(); + + if (priv.window_decoration) |_| { + // Unset any previously set window decoration settings + self.setWindowDecoration(null); + return; + } + + const config = if (priv.config) |v| v.get() else return; + self.setWindowDecoration(switch (config.@"window-decoration") { + // Use auto when the decoration is initially none + .none => .auto, + + // Anything non-none to none + .auto, .client, .server => .none, + }); + } + + /// Set the window decoration override for this window. If this is null, + /// then we'll revert back to the configuration's default. + fn setWindowDecoration( + self: *Self, + new_: ?configpkg.WindowDecoration, + ) void { + const priv = self.private(); + priv.window_decoration = new_; + self.syncAppearance(); + } + + /// Get the currently selected tab as a Tab object. + fn getSelectedTab(self: *Self) ?*Tab { + const priv = self.private(); + const page = priv.tab_view.getSelectedPage() orelse return null; + const child = page.getChild(); + assert(gobject.ext.isA(child, Tab)); + return gobject.ext.cast(Tab, child); + } + + /// Returns true if this window needs confirmation before quitting. + fn getNeedsConfirmQuit(self: *Self) bool { + const priv = self.private(); + const n = priv.tab_view.getNPages(); + assert(n >= 0); + + for (0..@intCast(n)) |i| { + const page = priv.tab_view.getNthPage(@intCast(i)); + const child = page.getChild(); + const tab = gobject.ext.cast(Tab, child) orelse { + log.warn("unexpected non-Tab child in tab view", .{}); + continue; + }; + if (tab.getNeedsConfirmQuit()) return true; + } + + return false; + } + + fn isFullscreen(self: *Window) bool { + return self.as(gtk.Window).isFullscreen() != 0; + } + + fn isMaximized(self: *Window) bool { + return self.as(gtk.Window).isMaximized() != 0; + } + + fn getHeaderbarVisible(self: *Self) bool { + const priv = self.private(); + + // Never display the header bar when CSDs are disabled. + const csd_enabled = priv.winproto.clientSideDecorationEnabled(); + if (!csd_enabled) return false; + + // Never display the header bar as a quick terminal. + if (priv.quick_terminal) return false; + + // If we're fullscreen we never show the header bar. + if (self.isFullscreen()) return false; + + // The remainder needs a config + const config_obj = self.private().config orelse return true; + const config = config_obj.get(); + + // *Conditionally* disable the header bar when maximized, and + // gtk-titlebar-hide-when-maximized is set + if (self.isMaximized() and config.@"gtk-titlebar-hide-when-maximized") { + return false; + } + + return switch (config.@"gtk-titlebar-style") { + // If the titlebar style is tabs never show the titlebar. + .tabs => false, + + // If the titlebar style is native show the titlebar if configured + // to do so. + .native => config.@"gtk-titlebar", + }; + } + + fn getTabsAutohide(self: *Self) bool { + const priv = self.private(); + const config = if (priv.config) |v| v.get() else return true; + + return switch (config.@"gtk-titlebar-style") { + // If the titlebar style is tabs we cannot autohide. + .tabs => false, + + .native => switch (config.@"window-show-tab-bar") { + // Auto we always autohide... obviously. + .auto => true, + + // Always we never autohide because we always show the tab bar. + .always => false, + + // Never we autohide because it doesn't actually matter, + // since getTabsVisible will return false. + .never => true, + }, + }; + } + + fn getTabsVisible(self: *Self) bool { + const priv = self.private(); + const config = if (priv.config) |v| v.get() else return true; + + switch (config.@"gtk-titlebar-style") { + .tabs => { + // *Conditionally* disable the tab bar when maximized, the titlebar + // style is tabs, and gtk-titlebar-hide-when-maximized is set. + if (self.isMaximized() and config.@"gtk-titlebar-hide-when-maximized") return false; + + // If the titlebar style is tabs the tab bar must always be visible. + return true; + }, + .native => { + return switch (config.@"window-show-tab-bar") { + .always, .auto => true, + .never => false, + }; + }, + } + } + + fn getTabsWide(self: *Self) bool { + const priv = self.private(); + const config = if (priv.config) |v| v.get() else return true; + return config.@"gtk-wide-tabs"; + } + + fn getToolbarStyle(self: *Self) adw.ToolbarStyle { + const priv = self.private(); + const config = if (priv.config) |v| v.get() else return .raised; + return switch (config.@"gtk-toolbar-style") { + .flat => .flat, + .raised => .raised, + .@"raised-border" => .raised_border, + }; + } + + fn getTitlebarStyle(self: *Self) TitlebarStyle { + const priv = self.private(); + const config = if (priv.config) |v| v.get() else return .native; + return config.@"gtk-titlebar-style"; + } + + fn propConfig( + _: *adw.ApplicationWindow, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + if (priv.config) |config_obj| { + const config = config_obj.get(); + if (config.@"app-notifications".@"config-reload") { + self.addToast(i18n._("Reloaded the configuration")); + } + } + + self.syncAppearance(); + } + + fn propIsActive( + _: *gtk.Window, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // Hide quick-terminal if set to autohide + if (self.isQuickTerminal()) { + if (self.getConfig()) |cfg| { + if (cfg.get().@"quick-terminal-autohide" and self.as(gtk.Window).isActive() == 0) { + self.toggleVisibility(); + } + } + } + + // Don't change urgency if we're not the active window. + if (self.as(gtk.Window).isActive() == 0) return; + + self.winproto().setUrgent(false) catch |err| { + log.warn( + "winproto failed to reset urgency={}", + .{err}, + ); + }; + } + + fn propGdkSurfaceDims( + _: *gdk.Surface, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // X11 needs to fix blurring on resize, but winproto implementations + // could do anything. + self.private().winproto.resizeEvent() catch |err| { + log.warn( + "winproto resize event failed error={}", + .{err}, + ); + }; + } + + fn propFullscreened( + _: *adw.ApplicationWindow, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.syncAppearance(); + } + + fn propMaximized( + _: *adw.ApplicationWindow, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + self.syncAppearance(); + } + + fn propMenuActive( + button: *gtk.MenuButton, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // Debian 12 is stuck on GTK 4.8 + if (!gtk_version.atLeast(4, 10, 0)) return; + + // We only care if we're activating. If we're activating then + // we need to check the validity of our menu items. + const active = button.getActive() != 0; + if (!active) return; + + self.syncActions(); + } + + fn propQuickTerminal( + _: *adw.ApplicationWindow, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + if (priv.surface_init) { + log.warn("quick terminal property can't be changed after surfaces have been initialized", .{}); + return; + } + + if (priv.quick_terminal) { + // Initialize the quick terminal at the app-layer + Application.default().winproto().initQuickTerminal(self) catch |err| { + log.warn("failed to initialize quick terminal error={}", .{err}); + return; + }; + } + } + + fn propScaleFactor( + _: *adw.ApplicationWindow, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // On some platforms (namely X11) we need to refresh our appearance when + // the scale factor changes. In theory this could be more fine-grained as + // a full refresh could be expensive, but a) this *should* be rare, and + // b) quite noticeable visual bugs would occur if this is not present. + self.private().winproto.syncAppearance() catch |err| { + log.warn( + "failed to sync appearance after scale factor has been updated={}", + .{err}, + ); + return; + }; + } + + fn closureTitlebarStyleIsTab( + _: *Self, + value: TitlebarStyle, + ) callconv(.c) c_int { + return @intFromBool(switch (value) { + .native => false, + .tabs => true, + }); + } + + fn closureSubtitle( + _: *Self, + config_: ?*Config, + pwd_: ?[*:0]const u8, + ) callconv(.c) ?[*:0]const u8 { + const config = if (config_) |v| v.get() else return null; + return switch (config.@"window-subtitle") { + .false => null, + .@"working-directory" => pwd: { + const pwd = pwd_ orelse return null; + break :pwd glib.ext.dupeZ(u8, std.mem.span(pwd)); + }, + }; + } + + //--------------------------------------------------------------- + // Virtual methods + + fn dispose(self: *Self) callconv(.c) void { + const priv = self.private(); + + priv.command_palette.set(null); + + if (priv.config) |v| { + v.unref(); + priv.config = null; + } + + priv.tab_bindings.setSource(null); + + gtk.Widget.disposeTemplate( + self.as(gtk.Widget), + getGObjectType(), + ); + + gobject.Object.virtual_methods.dispose.call( + Class.parent, + self.as(Parent), + ); + } + + fn finalize(self: *Self) callconv(.c) void { + const priv = self.private(); + priv.tab_bindings.unref(); + priv.winproto.deinit(); + + gobject.Object.virtual_methods.finalize.call( + Class.parent, + self.as(Parent), + ); + } + + //--------------------------------------------------------------- + // Signal handlers + + fn windowRealize(_: *gtk.Widget, self: *Window) callconv(.c) void { + const app = Application.default(); + + // Initialize our window protocol logic + if (winprotopkg.Window.init( + app.allocator(), + app.winproto(), + self, + )) |wp| { + self.private().winproto = wp; + } else |err| { + log.warn("failed to initialize window protocol error={}", .{err}); + return; + } + + // We need to setup resize notifications on our surface, + // which is only available after the window had been realized. + if (self.as(gtk.Native).getSurface()) |gdk_surface| { + _ = gobject.Object.signals.notify.connect( + gdk_surface, + *Self, + propGdkSurfaceDims, + self, + .{ .detail = "width" }, + ); + _ = gobject.Object.signals.notify.connect( + gdk_surface, + *Self, + propGdkSurfaceDims, + self, + .{ .detail = "height" }, + ); + } + + // When we are realized we always setup our appearance since this + // calls some winproto functions. + self.syncAppearance(); + } + + fn btnNewTab(_: *adw.SplitButton, self: *Self) callconv(.c) void { + self.performBindingAction(.new_tab); + } + + fn tabOverviewCreateTab( + _: *adw.TabOverview, + self: *Self, + ) callconv(.c) *adw.TabPage { + return self.newTabPage(if (self.getActiveSurface()) |v| v.core() else null, .tab, .none); + } + + fn tabOverviewOpen( + tab_overview: *adw.TabOverview, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + // We only care about when the tab overview is closed. + if (tab_overview.getOpen() != 0) return; + + // On tab overview close, focus is sometimes lost. This is an + // upstream issue in libadwaita[1]. When this is resolved we + // can put a runtime version check here to avoid this workaround. + // + // Our workaround is to start a timer after 500ms to refocus + // the currently selected tab. We choose 500ms because the adw + // animation is 400ms. + // + // [1]: https://gitlab.gnome.org/GNOME/libadwaita/-/issues/670 + + // If we have an old timer remove it + const priv = self.private(); + if (priv.tab_overview_focus_timer) |timer| { + _ = glib.Source.remove(timer); + } + + // Restart our timer + priv.tab_overview_focus_timer = glib.timeoutAdd( + 500, + tabOverviewFocusTimer, + self, + ); + } + + fn tabOverviewFocusTimer( + ud: ?*anyopaque, + ) callconv(.c) c_int { + const self: *Self = @ptrCast(@alignCast(ud orelse return 0)); + + // Always note our timer is removed + self.private().tab_overview_focus_timer = null; + + // Get our currently active surface which should respect the newly + // selected tab. Grab focus. + const surface = self.getActiveSurface() orelse return 0; + surface.grabFocus(); + + // Remove the timer + return 0; + } + + fn windowCloseRequest( + _: *gtk.Window, + self: *Self, + ) callconv(.c) c_int { + if (self.getNeedsConfirmQuit()) { + // Show a confirmation dialog + const dialog: *CloseConfirmationDialog = .new(.window); + _ = CloseConfirmationDialog.signals.@"close-request".connect( + dialog, + *Self, + closeConfirmationClose, + self, + .{}, + ); + + // Show it + dialog.present(self.as(gtk.Widget)); + return @intFromBool(true); + } + + self.as(gtk.Window).destroy(); + return @intFromBool(false); + } + + fn closeConfirmationClose( + _: *CloseConfirmationDialog, + self: *Self, + ) callconv(.c) void { + self.as(gtk.Window).destroy(); + } + + fn closeConfirmationCloseTab( + _: *CloseConfirmationDialog, + page: *adw.TabPage, + ) callconv(.c) void { + const tab_view = ext.getAncestor( + adw.TabView, + page.getChild().as(gtk.Widget), + ) orelse { + log.warn("close confirmation called for non-existent page", .{}); + return; + }; + tab_view.closePageFinish(page, @intFromBool(true)); + } + + fn closeConfirmationCancelTab( + _: *CloseConfirmationDialog, + page: *adw.TabPage, + ) callconv(.c) void { + const tab_view = ext.getAncestor( + adw.TabView, + page.getChild().as(gtk.Widget), + ) orelse { + log.warn("close confirmation called for non-existent page", .{}); + return; + }; + tab_view.closePageFinish(page, @intFromBool(false)); + } + + fn tabViewClosePage( + _: *adw.TabView, + page: *adw.TabPage, + self: *Self, + ) callconv(.c) c_int { + const priv = self.private(); + const child = page.getChild(); + const tab = gobject.ext.cast(Tab, child) orelse + return @intFromBool(false); + + // If the tab says it doesn't need confirmation then we go ahead + // and close immediately. + if (!tab.getNeedsConfirmQuit()) { + priv.tab_view.closePageFinish(page, @intFromBool(true)); + return @intFromBool(true); + } + + // Show a confirmation dialog + const dialog: *CloseConfirmationDialog = .new(.tab); + _ = CloseConfirmationDialog.signals.@"close-request".connect( + dialog, + *adw.TabPage, + closeConfirmationCloseTab, + page, + .{}, + ); + _ = CloseConfirmationDialog.signals.cancel.connect( + dialog, + *adw.TabPage, + closeConfirmationCancelTab, + page, + .{}, + ); + + // Show it + dialog.present(child); + return @intFromBool(true); + } + + fn tabViewSelectedPage( + _: *adw.TabView, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + + // Always reset our binding source in case we have no pages. + priv.tab_bindings.setSource(null); + + // Get our current page which MUST be a Tab object. + const page = priv.tab_view.getSelectedPage() orelse return; + const child = page.getChild(); + assert(gobject.ext.isA(child, Tab)); + + // Setup our binding group. This ensures things like the title + // are synced from the active tab. + priv.tab_bindings.setSource(child.as(gobject.Object)); + + // If the tab was previously marked as needing attention + // (e.g. due to a bell character), we now unmark that + page.setNeedsAttention(@intFromBool(false)); + } + + fn tabViewPageAttached( + _: *adw.TabView, + page: *adw.TabPage, + _: c_int, + self: *Self, + ) callconv(.c) void { + // Get the attached page which must be a Tab object. + const child = page.getChild(); + const tab = gobject.ext.cast(Tab, child) orelse return; + + // Attach listeners for the tab. + _ = Tab.signals.@"close-request".connect( + tab, + *Self, + tabCloseRequest, + self, + .{}, + ); + + // Attach listeners for the surface. + // + // Interesting behavior here that was previously undocumented but + // I'm going to make it explicit here: we accept all the signals here + // (like toggle-fullscreen) regardless of whether the surface or tab + // is focused. At the time of writing this we have no API that could + // really trigger these that way but its theoretically possible. + // + // What is DEFINITELY possible is something like OSC52 triggering + // a clipboard-write signal on an unfocused tab/surface. We definitely + // want to show the user a notification about that but our notification + // right now is a toast that doesn't make it clear WHO used the + // clipboard. We probably want to change that in the future. + // + // I'm not sure how desirable all the above is, and we probably + // should be thoughtful about future signals here. But all of this + // behavior is consistent with macOS and the previous GTK apprt, + // but that behavior was all implicit and not documented, so here + // I am. + if (tab.getSurfaceTree()) |tree| { + self.connectSurfaceHandlers(tree); + } + } + + fn tabViewPageDetached( + _: *adw.TabView, + page: *adw.TabPage, + _: c_int, + self: *Self, + ) callconv(.c) void { + // We need to get the tab to disconnect the signals. + const child = page.getChild(); + const tab = gobject.ext.cast(Tab, child) orelse return; + _ = gobject.signalHandlersDisconnectMatched( + tab.as(gobject.Object), + .{ .data = true }, + 0, + 0, + null, + null, + self, + ); + + // Remove the tree handlers + if (tab.getSurfaceTree()) |tree| { + self.disconnectSurfaceHandlers(tree); + } + } + + fn tabViewCreateWindow( + _: *adw.TabView, + _: *Self, + ) callconv(.c) *adw.TabView { + // Create a new window without creating a new tab. + const win = gobject.ext.newInstance( + Self, + .{ + .application = Application.default(), + }, + ); + + // We have to show it otherwise it'll just be hidden. + gtk.Window.present(win.as(gtk.Window)); + + // Get our tab view + return win.private().tab_view; + } + + fn tabCloseRequest( + tab: *Tab, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + const page = priv.tab_view.getPage(tab.as(gtk.Widget)); + // TODO: connect close page handler to tab to check for confirmation + priv.tab_view.closePage(page); + } + + fn tabViewNPages( + _: *adw.TabView, + _: *gobject.ParamSpec, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + if (priv.tab_view.getNPages() == 0) { + // If we have no pages left then we want to close window. + + // If the tab overview is open, then we don't close the window + // because its a rather abrupt experience. This also fixes an + // issue where dragging out the last tab in the tab overview + // won't cause Ghostty to exit. + if (priv.tab_overview.getOpen() != 0) return; + + self.as(gtk.Window).close(); + } + } + fn setupTabMenu( + _: *adw.TabView, + page: ?*adw.TabPage, + self: *Self, + ) callconv(.c) void { + self.private().context_menu_page = page; + } + + fn surfaceClipboardWrite( + _: *Surface, + clipboard_type: apprt.Clipboard, + text: [*:0]const u8, + self: *Self, + ) callconv(.c) void { + // We only toast for the standard clipboard. + if (clipboard_type != .standard) return; + + // We only toast if configured to + const priv = self.private(); + const config_obj = priv.config orelse return; + const config = config_obj.get(); + if (!config.@"app-notifications".@"clipboard-copy") { + return; + } + + if (text[0] != 0) + self.addToast(i18n._("Copied to clipboard")) + else + self.addToast(i18n._("Cleared clipboard")); + } + + fn surfaceMenu( + _: *Surface, + self: *Self, + ) callconv(.c) void { + self.syncActions(); + } + + fn surfacePresentRequest( + surface: *Surface, + self: *Self, + ) callconv(.c) void { + // Verify that this surface is actually in this window. + { + const surface_window = ext.getAncestor( + Self, + surface.as(gtk.Widget), + ) orelse { + log.warn( + "present request called for non-existent surface", + .{}, + ); + return; + }; + if (surface_window != self) { + log.warn( + "present request called for surface in different window", + .{}, + ); + return; + } + } + + // Get the tab for this surface. + const tab = ext.getAncestor( + Tab, + surface.as(gtk.Widget), + ) orelse { + log.warn("present request surface not found", .{}); + return; + }; + + // Get the page that contains this tab + const priv = self.private(); + const tab_view = priv.tab_view; + const page = tab_view.getPage(tab.as(gtk.Widget)); + tab_view.setSelectedPage(page); + + // Grab focus + surface.grabFocus(); + + // Bring the window to the front. + self.as(gtk.Window).present(); + } + + fn surfaceToggleFullscreen( + surface: *Surface, + self: *Self, + ) callconv(.c) void { + _ = surface; + if (self.as(gtk.Window).isFullscreen() != 0) { + self.as(gtk.Window).unfullscreen(); + } else { + self.as(gtk.Window).fullscreen(); + } + + // We react to the changes in the propFullscreen callback + } + + fn surfaceToggleMaximize( + surface: *Surface, + self: *Self, + ) callconv(.c) void { + _ = surface; + if (self.as(gtk.Window).isMaximized() != 0) { + self.as(gtk.Window).unmaximize(); + } else { + self.as(gtk.Window).maximize(); + } + + // We react to the changes in the propMaximized callback + } + + fn surfaceInit( + surface: *Surface, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + + // Make sure we init only once + if (priv.surface_init) return; + priv.surface_init = true; + + // Setup our default and minimum size. + if (surface.getDefaultSize()) |size| { + self.as(gtk.Window).setDefaultSize( + @intCast(size.width), + @intCast(size.height), + ); + } + if (surface.getMinSize()) |size| { + self.as(gtk.Widget).setSizeRequest( + @intCast(size.width), + @intCast(size.height), + ); + } + } + + fn tabSplitTreeChanged( + _: *SplitTree, + old_tree: ?*const Surface.Tree, + new_tree: ?*const Surface.Tree, + self: *Self, + ) callconv(.c) void { + if (old_tree) |tree| { + self.disconnectSurfaceHandlers(tree); + } + + if (new_tree) |tree| { + self.connectSurfaceHandlers(tree); + } + } + + fn actionAbout( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const name = "Ghostty"; + const icon = "com.mitchellh.ghostty"; + const website = "https://ghostty.org"; + + if (adw_version.supportsDialogs()) { + adw.showAboutDialog( + self.as(gtk.Widget), + "application-name", + name, + "developer-name", + i18n._("Ghostty Developers"), + "application-icon", + icon, + "version", + build_config.version_string.ptr, + "issue-url", + "https://github.com/ghostty-org/ghostty/issues", + "website", + website, + @as(?*anyopaque, null), + ); + } else { + gtk.showAboutDialog( + self.as(gtk.Window), + "program-name", + name, + "logo-icon-name", + icon, + "title", + i18n._("About Ghostty"), + "version", + build_config.version_string.ptr, + "website", + website, + @as(?*anyopaque, null), + ); + } + } + + fn actionClose( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + self.as(gtk.Window).close(); + } + + fn actionCloseTab( + _: *gio.SimpleAction, + param_: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + const param = param_ orelse { + log.warn("win.close-tab called without a parameter", .{}); + return; + }; + + var str: ?[*:0]const u8 = null; + param.get("&s", &str); + + const mode = std.meta.stringToEnum( + input.Binding.Action.CloseTabMode, + std.mem.span( + str orelse { + log.warn("invalid mode provided to win.close-tab", .{}); + return; + }, + ), + ) orelse { + log.warn("invalid mode provided to win.close-tab: {s}", .{str.?}); + return; + }; + + self.performBindingAction(.{ .close_tab = mode }); + } + + fn actionNewWindow( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.new_window); + } + + fn actionNewTab( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.new_tab); + } + + fn actionPromptContextTabTitle( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Self, + ) callconv(.c) void { + const priv = self.private(); + const page = priv.context_menu_page orelse return; + const child = page.getChild(); + const tab = gobject.ext.cast(Tab, child) orelse return; + tab.promptTabTitle(); + } + + fn actionPromptSurfaceTitle( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.prompt_surface_title); + } + + fn actionPromptTabTitle( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.prompt_tab_title); + } + + fn actionSplitRight( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.{ .new_split = .right }); + } + + fn actionSplitLeft( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.{ .new_split = .left }); + } + + fn actionSplitUp( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.{ .new_split = .up }); + } + + fn actionSplitDown( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.{ .new_split = .down }); + } + + fn actionCopy( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.{ .copy_to_clipboard = .mixed }); + } + + fn actionPaste( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.paste_from_clipboard); + } + + fn actionReset( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.reset); + } + + fn actionClear( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + self.performBindingAction(.clear_screen); + } + + fn actionRingBell( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + const priv = self.private(); + const config = if (priv.config) |v| v.get() else return; + + if (config.@"bell-features".system) system: { + const native = self.as(gtk.Native).getSurface() orelse { + log.warn("unable to get native surface from window", .{}); + break :system; + }; + native.beep(); + } + + if (config.@"bell-features".attention) attention: { + // Dont set urgency if the window is already active. + if (self.as(gtk.Window).isActive() != 0) break :attention; + + // Request user attention + self.winproto().setUrgent(true) catch |err| { + log.warn("winproto failed to set urgency={}", .{err}); + }; + } + } + + /// Toggle the command palette. + /// + /// TODO: accept the surface that toggled the command palette as a parameter + fn toggleCommandPalette(self: *Window) void { + const priv = self.private(); + + // Get a reference to a command palette. First check the weak reference + // that we save to see if we already have one stored. If we don't then + // create a new one. + const command_palette = priv.command_palette.get() orelse command_palette: { + // Create a fresh command palette. + const command_palette = CommandPalette.new(); + + // Synchronize our config to the command palette's config. + _ = gobject.Object.bindProperty( + self.as(gobject.Object), + "config", + command_palette.as(gobject.Object), + "config", + .{ .sync_create = true }, + ); + + // Listen to the activate signal to know if the user selected an option in + // the command palette. + _ = CommandPalette.signals.trigger.connect( + command_palette, + *Window, + signalCommandPaletteTrigger, + self, + .{}, + ); + + // Save a weak reference to the command palette. We use a weak reference to avoid + // reference counting cycles that might cause problems later. + priv.command_palette.set(command_palette); + + break :command_palette command_palette; + }; + defer command_palette.unref(); + + // Tell the command palette to toggle itself. If the dialog gets + // presented (instead of hidden) it will be modal over our window. + command_palette.toggle(self); + } + + // React to a signal from a command palette asking an action to be performed. + fn signalCommandPaletteTrigger(_: *CommandPalette, action: *const input.Binding.Action, self: *Self) callconv(.c) void { + // If the activation actually has an action, perform it. + self.performBindingAction(action.*); + } + + /// React to a GTK action requesting that the command palette be toggled. + fn actionToggleCommandPalette( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + // TODO: accept the surface that toggled the command palette as a + // parameter + self.toggleCommandPalette(); + } + + /// Toggle the Ghostty inspector for the active surface. + fn toggleInspector(self: *Self) void { + const surface = self.getActiveSurface() orelse return; + _ = surface.controlInspector(.toggle); + } + + /// React to a GTK action requesting that the Ghostty inspector be toggled. + fn actionToggleInspector( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *Window, + ) callconv(.c) void { + // TODO: accept the surface that toggled the command palette as a + // parameter + self.toggleInspector(); + } + + const C = Common(Self, Private); + pub const as = C.as; + pub const ref = C.ref; + pub const unref = C.unref; + const private = C.private; + + pub const Class = extern struct { + parent_class: Parent.Class, + var parent: *Parent.Class = undefined; + pub const Instance = Self; + + fn init(class: *Class) callconv(.c) void { + gobject.ext.ensureType(DebugWarning); + gobject.ext.ensureType(SplitTree); + gobject.ext.ensureType(Surface); + gobject.ext.ensureType(Tab); + gtk.Widget.Class.setTemplateFromResource( + class.as(gtk.Widget.Class), + comptime gresource.blueprint(.{ + .major = 1, + .minor = 5, + .name = "window", + }), + ); + + // Properties + gobject.ext.registerProperties(class, &.{ + properties.@"active-surface".impl, + properties.config.impl, + properties.debug.impl, + properties.@"headerbar-visible".impl, + properties.@"quick-terminal".impl, + properties.@"tabs-autohide".impl, + properties.@"tabs-visible".impl, + properties.@"tabs-wide".impl, + properties.@"toolbar-style".impl, + properties.@"titlebar-style".impl, + }); + + // Bindings + class.bindTemplateChildPrivate("tab_overview", .{}); + class.bindTemplateChildPrivate("tab_bar", .{}); + class.bindTemplateChildPrivate("tab_view", .{}); + class.bindTemplateChildPrivate("toolbar", .{}); + class.bindTemplateChildPrivate("toast_overlay", .{}); + + // Template Callbacks + class.bindTemplateCallback("realize", &windowRealize); + class.bindTemplateCallback("new_tab", &btnNewTab); + class.bindTemplateCallback("overview_create_tab", &tabOverviewCreateTab); + class.bindTemplateCallback("overview_notify_open", &tabOverviewOpen); + class.bindTemplateCallback("close_request", &windowCloseRequest); + class.bindTemplateCallback("close_page", &tabViewClosePage); + class.bindTemplateCallback("page_attached", &tabViewPageAttached); + class.bindTemplateCallback("page_detached", &tabViewPageDetached); + class.bindTemplateCallback("setup_tab_menu", &setupTabMenu); + class.bindTemplateCallback("tab_create_window", &tabViewCreateWindow); + class.bindTemplateCallback("notify_n_pages", &tabViewNPages); + class.bindTemplateCallback("notify_selected_page", &tabViewSelectedPage); + class.bindTemplateCallback("notify_config", &propConfig); + class.bindTemplateCallback("notify_fullscreened", &propFullscreened); + class.bindTemplateCallback("notify_is_active", &propIsActive); + class.bindTemplateCallback("notify_maximized", &propMaximized); + class.bindTemplateCallback("notify_menu_active", &propMenuActive); + class.bindTemplateCallback("notify_quick_terminal", &propQuickTerminal); + class.bindTemplateCallback("notify_scale_factor", &propScaleFactor); + class.bindTemplateCallback("titlebar_style_is_tabs", &closureTitlebarStyleIsTab); + class.bindTemplateCallback("computed_subtitle", &closureSubtitle); + + // Virtual methods + gobject.Object.virtual_methods.dispose.implement(class, &dispose); + gobject.Object.virtual_methods.finalize.implement(class, &finalize); + } + + pub const as = C.Class.as; + pub const bindTemplateChildPrivate = C.Class.bindTemplateChildPrivate; + pub const bindTemplateCallback = C.Class.bindTemplateCallback; + }; +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/css/style-dark.css b/vendor/libghostty-vt/src/apprt/gtk/css/style-dark.css new file mode 100644 index 00000000..f13b4f4f --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/css/style-dark.css @@ -0,0 +1,8 @@ +.transparent { + background-color: transparent; +} + +.window .split paned > separator { + background-color: rgba(36, 36, 36, 1); + background-clip: content-box; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/css/style-hc-dark.css b/vendor/libghostty-vt/src/apprt/gtk/css/style-hc-dark.css new file mode 100644 index 00000000..a9aa2dcc --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/css/style-hc-dark.css @@ -0,0 +1,3 @@ +.transparent { + background-color: transparent; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/css/style-hc.css b/vendor/libghostty-vt/src/apprt/gtk/css/style-hc.css new file mode 100644 index 00000000..a9aa2dcc --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/css/style-hc.css @@ -0,0 +1,3 @@ +.transparent { + background-color: transparent; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/css/style.css b/vendor/libghostty-vt/src/apprt/gtk/css/style.css new file mode 100644 index 00000000..9c0f115f --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/css/style.css @@ -0,0 +1,183 @@ +/* Application CSS that applies to the entire application. + * + * This is automatically loaded by AdwApplication: + * https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.3/styles-and-appearance.html#custom-styles + */ + +window.ssd.no-border-radius { + /* Without clearing the border radius, at least on Mutter with + * gtk-titlebar=true and gtk-adwaita=false, there is some window artifacting + * that this will mitigate. + */ + border-radius: 0 0; +} + +/* + * GhosttySurface URL overlay + */ +label.url-overlay { + padding: 4px 8px 4px 8px; + outline-style: solid; + outline-color: #555555; + outline-width: 1px; +} + +label.url-overlay:hover { + opacity: 0; +} + +label.url-overlay.left { + border-radius: 0px 6px 0px 0px; +} + +label.url-overlay.right { + border-radius: 6px 0px 0px 0px; +} + +/* + * GhosttySurface search overlay + */ +.search-overlay { + padding: 6px 8px; + margin: 8px; + border-radius: 8px; + outline-style: solid; + outline-color: #555555; + outline-width: 1px; +} + +/* + * GhosttySurface key state overlay + */ +.key-state-overlay { + padding: 6px 10px; + margin: 8px; + border-radius: 8px; + outline-style: solid; + outline-color: #555555; + outline-width: 1px; +} + +/* + * GhosttySurface resize overlay + */ +label.resize-overlay { + padding: 4px 8px 4px 8px; + border-radius: 6px 6px 6px 6px; + outline-style: solid; + outline-color: #555555; + outline-width: 1px; +} + +/* + * GhosttyClipboardConfirmationDialog + * + * Based on boxed-list-separate: + * https://gitlab.gnome.org/GNOME/libadwaita/-/blob/ad446167acf3e6d1ee693f98ca636268be8592a1/src/stylesheet/widgets/_lists.scss#L548 + */ +.clipboard-confirmation-dialog list { + background: none; +} + +.clipboard-confirmation-dialog list > row { + border: none; + margin-bottom: 12px; +} + +.clipboard-confirmation-dialog list > row:last-child { + margin-bottom: 0; +} + +.clipboard-confirmation-dialog .clipboard-overlay { + border-radius: 10px; +} + +.clipboard-confirmation-dialog .clipboard-contents { + filter: blur(0px); + transition: filter 0.3s ease; + border-radius: 10px; +} + +.clipboard-confirmation-dialog .clipboard-contents.blurred { + filter: blur(5px); +} + +/* + * Child Exited Overlay + */ +.child-exited.normal revealer widget { + background-color: rgba(38, 162, 105, 0.5); + /* after GTK 4.16 is a requirement, switch to the following: */ + /* background-color: color-mix(in srgb, var(--success-bg-color), transparent 50%); */ +} + +.child-exited.abnormal revealer widget { + background-color: rgba(192, 28, 40, 0.5); + /* after GTK 4.16 is a requirement, switch to the following: */ + /* background-color: color-mix(in srgb, var(--error-bg-color), transparent 50%); */ +} + +/* + * Surface + */ +.surface progressbar.error trough progress { + background-color: rgba(192, 28, 40, 0.5); + /* after GTK 4.16 is a requirement, switch to the following: */ + /* background-color: color-mix(in srgb, var(--error-bg-color), transparent 50%); */ +} + +.surface .bell-overlay { + border-color: rgba(58, 148, 74, 0.5); + /* after GTK 4.16 is a requirement, switch to the following: */ + /* background-color: color-mix(in srgb, var(--accent-color), transparent 50%); */ + border-width: 3px; + border-style: solid; +} + +.surface .readonly_overlay { + /* Should be the equivalent of the following SwiftUI color: */ + /* Color(hue: 0.08, saturation: 0.5, brightness: 0.8) */ + color: hsl(25 50 75); + padding: 8px 8px 8px 8px; + margin: 8px 8px 8px 8px; + border-radius: 6px 6px 6px 6px; + outline-style: solid; + outline-width: 1px; +} +/* + * Command Palette + */ +.command-palette-search > image:first-child { + margin-left: 8px; + margin-right: 4px; +} + +.command-palette-search > image:last-child { + margin-left: 4px; + margin-right: 8px; +} + +/* + * Splits + */ + +.window .split paned > separator { + background-color: rgba(250, 250, 250, 1); + /* after GTK 4.16 is a requirement, switch to the following: */ + /* background-color: color-mix(in srgb, var(--window-bg-color), transparent 0%); */ + background-clip: content-box; + + /* This works around the oversized drag area for the right side of GtkPaned. + * + * Upstream Gtk issue: + * https://gitlab.gnome.org/GNOME/gtk/-/issues/4484#note_2362002 + * + * Ghostty issue: + * https://github.com/ghostty-org/ghostty/issues/3020 + * + * Without this, it's not possible to select the first character on the + * right-hand side of a split. + */ + margin: 0; + padding: 0; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ext.zig b/vendor/libghostty-vt/src/apprt/gtk/ext.zig new file mode 100644 index 00000000..df9ab4ea --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ext.zig @@ -0,0 +1,70 @@ +//! Extensions/helpers for GTK objects, following a similar naming +//! style to zig-gobject. These should, wherever possible, be Zig-friendly +//! wrappers around existing GTK functionality, rather than complex new +//! helpers. + +const std = @import("std"); +const assert = @import("../../quirks.zig").inlineAssert; +const testing = std.testing; + +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +pub const actions = @import("ext/actions.zig"); +const slice = @import("ext/slice.zig"); +pub const StringList = slice.StringList; + +/// Wrapper around `gobject.boxedCopy` to copy a boxed type `T`. +pub fn boxedCopy(comptime T: type, ptr: *const T) *T { + const copy = gobject.boxedCopy(T.getGObjectType(), ptr); + return @ptrCast(@alignCast(copy)); +} + +/// Wrapper around `gobject.boxedFree` to free a boxed type `T`. +pub fn boxedFree(comptime T: type, ptr: ?*T) void { + if (ptr) |p| gobject.boxedFree( + T.getGObjectType(), + p, + ); +} + +/// A wrapper around `glib.List.findCustom` to find an element in the list. +/// The type `T` must be the guaranteed type of every list element. +pub fn listFind( + comptime T: type, + list: *glib.List, + comptime func: *const fn (*T) bool, +) ?*T { + const elem_: ?*glib.List = list.findCustom(null, struct { + fn callback(data: ?*const anyopaque, _: ?*const anyopaque) callconv(.c) c_int { + const ptr = data orelse return 1; + const v: *T = @ptrCast(@alignCast(@constCast(ptr))); + return if (func(v)) 0 else 1; + } + }.callback); + const elem = elem_ orelse return null; + return @ptrCast(@alignCast(elem.f_data)); +} + +/// Wrapper around `gtk.Widget.getAncestor` to get the widget ancestor +/// of the given type `T`, or null if it doesn't exist. +pub fn getAncestor(comptime T: type, widget: *gtk.Widget) ?*T { + const ancestor_ = widget.getAncestor(gobject.ext.typeFor(T)); + const ancestor = ancestor_ orelse return null; + // We can assert the unwrap because getAncestor above + return gobject.ext.cast(T, ancestor).?; +} + +/// Check a gobject.Value to see what type it is wrapping. This is equivalent to GTK's +/// `G_VALUE_HOLDS()` macro but Zig's C translator does not like it. +pub fn gValueHolds(value_: ?*const gobject.Value, g_type: gobject.Type) bool { + const value = value_ orelse return false; + if (value.f_g_type == g_type) return true; + return gobject.typeCheckValueHolds(value, g_type) != 0; +} + +test { + _ = actions; + _ = slice; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ext/actions.zig b/vendor/libghostty-vt/src/apprt/gtk/ext/actions.zig new file mode 100644 index 00000000..3232bc18 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ext/actions.zig @@ -0,0 +1,198 @@ +const std = @import("std"); + +const assert = @import("../../../quirks.zig").inlineAssert; +const testing = std.testing; + +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +const gValueHolds = @import("../ext.zig").gValueHolds; + +/// Check that an action name is valid. +/// +/// Reimplementation of `g_action_name_is_valid()` so that it can be +/// used at comptime. +/// +/// See: +/// https://docs.gtk.org/gio/type_func.Action.name_is_valid.html +fn gActionNameIsValid(name: [:0]const u8) bool { + if (name.len == 0) return false; + + for (name) |c| switch (c) { + '-' => continue, + '.' => continue, + '0'...'9' => continue, + 'a'...'z' => continue, + 'A'...'Z' => continue, + else => return false, + }; + + return true; +} + +test "gActionNameIsValid" { + try testing.expect(gActionNameIsValid("ring-bell")); + try testing.expect(!gActionNameIsValid("ring_bell")); +} + +/// Function to create a structure for describing an action. +pub fn Action(comptime T: type) type { + return struct { + const Self = @This(); + pub const Callback = *const fn (*gio.SimpleAction, ?*glib.Variant, *T) callconv(.c) void; + + name: [:0]const u8, + callback: Callback, + parameter_type: ?*const glib.VariantType, + state: ?*glib.Variant = null, + + /// Function to initialize a new action so that we can comptime check + /// the name. + pub fn init( + comptime name: [:0]const u8, + callback: Callback, + parameter_type: ?*const glib.VariantType, + ) Self { + comptime assert(gActionNameIsValid(name)); + + return .{ + .name = name, + .callback = callback, + .parameter_type = parameter_type, + }; + } + + /// Function to initialize a new stateful action so that we can comptime + /// check the name. + pub fn initStateful( + comptime name: [:0]const u8, + callback: Callback, + parameter_type: ?*const glib.VariantType, + state: *glib.Variant, + ) Self { + comptime assert(gActionNameIsValid(name)); + return .{ + .name = name, + .callback = callback, + .parameter_type = parameter_type, + .state = state, + }; + } + }; +} + +/// Add actions to a widget that implements gio.ActionMap. +pub fn add(comptime T: type, self: *T, actions: []const Action(T)) void { + addToMap(T, self, self.as(gio.ActionMap), actions); +} + +/// Add actions to the given map. +pub fn addToMap(comptime T: type, self: *T, map: *gio.ActionMap, actions: []const Action(T)) void { + for (actions) |entry| { + assert(gActionNameIsValid(entry.name)); + const action = action: { + if (entry.state) |state| { + break :action gio.SimpleAction.newStateful( + entry.name, + entry.parameter_type, + state, + ); + } + break :action gio.SimpleAction.new( + entry.name, + entry.parameter_type, + ); + }; + defer action.unref(); + _ = gio.SimpleAction.signals.activate.connect( + action, + *T, + entry.callback, + self, + .{}, + ); + map.addAction(action.as(gio.Action)); + } +} + +/// Add actions to a widget that doesn't implement ActionGroup directly. +pub fn addAsGroup(comptime T: type, self: *T, comptime name: [:0]const u8, actions: []const Action(T)) *gio.SimpleActionGroup { + comptime assert(gActionNameIsValid(name)); + + // Collect our actions into a group since we're just a plain widget that + // doesn't implement ActionGroup directly. + const group = gio.SimpleActionGroup.new(); + errdefer group.unref(); + + addToMap(T, self, group.as(gio.ActionMap), actions); + + self.as(gtk.Widget).insertActionGroup( + name, + group.as(gio.ActionGroup), + ); + + return group; +} + +test "adding actions to an object" { + // This test requires a connection to an active display environment. + if (gtk.initCheck() == 0) return error.SkipZigTest; + + _ = glib.MainContext.acquire(null); + defer glib.MainContext.release(null); + + const callbacks = struct { + fn callback(_: *gio.SimpleAction, variant_: ?*glib.Variant, self: *gtk.Box) callconv(.c) void { + const i32_variant_type = glib.ext.VariantType.newFor(i32); + defer i32_variant_type.free(); + + const variant = variant_ orelse return; + assert(variant.isOfType(i32_variant_type) != 0); + + var value = std.mem.zeroes(gobject.Value); + _ = value.init(gobject.ext.types.int); + defer value.unset(); + + value.setInt(variant.getInt32()); + + self.as(gobject.Object).setProperty("spacing", &value); + } + }; + + const box = gtk.Box.new(.vertical, 0); + _ = box.as(gobject.Object).refSink(); + defer box.unref(); + + { + const i32_variant_type = glib.ext.VariantType.newFor(i32); + defer i32_variant_type.free(); + + const actions = [_]Action(gtk.Box){ + .init("test", callbacks.callback, i32_variant_type), + }; + + _ = addAsGroup(gtk.Box, box, "test", &actions); + } + + const expected = std.crypto.random.intRangeAtMost(i32, 1, std.math.maxInt(u31)); + const parameter = glib.Variant.newInt32(expected); + + try testing.expect(box.as(gtk.Widget).activateActionVariant("test.test", parameter) != 0); + + _ = glib.MainContext.iteration(null, @intFromBool(true)); + + var value = std.mem.zeroes(gobject.Value); + _ = value.init(gobject.ext.types.int); + defer value.unset(); + + box.as(gobject.Object).getProperty("spacing", &value); + + try testing.expect(gValueHolds(&value, gobject.ext.types.int)); + + const actual = value.getInt(); + try testing.expectEqual(expected, actual); + + while (glib.MainContext.iteration(null, 0) != 0) {} +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ext/slice.zig b/vendor/libghostty-vt/src/apprt/gtk/ext/slice.zig new file mode 100644 index 00000000..49ad63d8 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ext/slice.zig @@ -0,0 +1,111 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const glib = @import("glib"); +const gobject = @import("gobject"); + +/// A boxed type that holds a list of string slices. +pub const StringList = struct { + arena: ArenaAllocator, + strings: []const [:0]const u8, + + pub fn create( + alloc: Allocator, + strings: []const [:0]const u8, + ) Allocator.Error!*StringList { + var arena: ArenaAllocator = .init(alloc); + errdefer arena.deinit(); + const arena_alloc = arena.allocator(); + var stored = try arena_alloc.alloc([:0]const u8, strings.len); + for (strings, 0..) |s, i| stored[i] = try arena_alloc.dupeZ(u8, s); + + const ptr = try alloc.create(StringList); + errdefer alloc.destroy(ptr); + ptr.* = .{ .arena = arena, .strings = stored }; + + return ptr; + } + + pub fn deinit(self: *StringList) void { + self.arena.deinit(); + } + + pub fn destroy(self: *StringList) void { + const alloc = self.arena.child_allocator; + self.deinit(); + alloc.destroy(self); + } + + /// Returns the general-purpose allocator used by this StringList. + pub fn allocator(self: *const StringList) Allocator { + return self.arena.child_allocator; + } + + pub const getGObjectType = gobject.ext.defineBoxed( + StringList, + .{ + .name = "GhosttyStringList", + .funcs = .{ + .copy = &struct { + fn copy(self: *StringList) callconv(.c) *StringList { + return StringList.create( + self.arena.child_allocator, + self.strings, + ) catch @panic("OOM"); + } + }.copy, + .free = &struct { + fn free(self: *StringList) callconv(.c) void { + self.destroy(); + } + }.free, + }, + }, + ); +}; + +test "StringList create and destroy" { + const testing = std.testing; + const alloc = testing.allocator; + + const input: []const [:0]const u8 = &.{ "hello", "world" }; + const list = try StringList.create(alloc, input); + defer list.destroy(); + + try testing.expectEqual(@as(usize, 2), list.strings.len); + try testing.expectEqualStrings("hello", list.strings[0]); + try testing.expectEqualStrings("world", list.strings[1]); +} + +test "StringList create empty list" { + const testing = std.testing; + const alloc = testing.allocator; + + const input: []const [:0]const u8 = &.{}; + const list = try StringList.create(alloc, input); + defer list.destroy(); + + try testing.expectEqual(@as(usize, 0), list.strings.len); +} + +test "StringList boxedCopy and boxedFree" { + const testing = std.testing; + const alloc = testing.allocator; + + const input: []const [:0]const u8 = &.{ "foo", "bar", "baz" }; + const original = try StringList.create(alloc, input); + defer original.destroy(); + + const copied: *StringList = @ptrCast(@alignCast(gobject.boxedCopy( + StringList.getGObjectType(), + original, + ))); + defer gobject.boxedFree(StringList.getGObjectType(), copied); + + try testing.expectEqual(@as(usize, 3), copied.strings.len); + try testing.expectEqualStrings("foo", copied.strings[0]); + try testing.expectEqualStrings("bar", copied.strings[1]); + try testing.expectEqualStrings("baz", copied.strings[2]); + + try testing.expect(original.strings.ptr != copied.strings.ptr); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/flatpak.zig b/vendor/libghostty-vt/src/apprt/gtk/flatpak.zig new file mode 100644 index 00000000..dc47c671 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/flatpak.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const build_config = @import("../../build_config.zig"); +const internal_os = @import("../../os/main.zig"); +const glib = @import("glib"); + +pub fn resourcesDir(alloc: Allocator) !internal_os.ResourcesDir { + if (comptime build_config.flatpak) { + // Only consult Flatpak runtime data for host case. + if (internal_os.isFlatpak()) { + var result: internal_os.ResourcesDir = .{ + .app_path = try alloc.dupe(u8, "/app/share/ghostty"), + }; + errdefer alloc.free(result.app_path.?); + + const keyfile = glib.KeyFile.new(); + defer keyfile.unref(); + + if (keyfile.loadFromFile("/.flatpak-info", .{}, null) == 0) return result; + const app_dir = std.mem.span(keyfile.getString("Instance", "app-path", null)) orelse return result; + defer glib.free(app_dir.ptr); + + result.host_path = try std.fs.path.join(alloc, &[_][]const u8{ app_dir, "share", "ghostty" }); + return result; + } + } + + return try internal_os.resourcesDir(alloc); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/gsettings.zig b/vendor/libghostty-vt/src/apprt/gtk/gsettings.zig new file mode 100644 index 00000000..8cf7f12d --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/gsettings.zig @@ -0,0 +1,101 @@ +const std = @import("std"); +const gtk = @import("gtk"); +const gobject = @import("gobject"); + +/// GTK Settings keys with well-defined types. +pub const Key = enum { + @"gtk-enable-primary-paste", + @"gtk-xft-dpi", + @"gtk-font-name", + + fn Type(comptime self: Key) type { + return switch (self) { + .@"gtk-enable-primary-paste" => bool, + .@"gtk-xft-dpi" => c_int, + .@"gtk-font-name" => []const u8, + }; + } + + fn GValueType(comptime self: Key) type { + return switch (self.Type()) { + bool => c_int, + c_int => c_int, + []const u8 => ?[*:0]const u8, + else => @compileError("Unsupported type for GTK settings"), + }; + } + + /// Returns true if this setting type requires memory allocation. + /// Types that do not need allocation must be explicitly marked. + fn requiresAllocation(comptime self: Key) bool { + const T = self.Type(); + return switch (T) { + bool, c_int => false, + else => true, + }; + } +}; + +/// Reads a GTK setting for non-allocating types. +/// Automatically uses XDG Desktop Portal in Flatpak environments. +/// Returns null if the setting is unavailable. +pub fn get(comptime key: Key) ?key.Type() { + if (comptime key.requiresAllocation()) { + @compileError("Allocating types require an allocator; use getAlloc() instead"); + } + const settings = gtk.Settings.getDefault() orelse return null; + return getImpl(settings, null, key) catch unreachable; +} + +/// Reads a GTK setting, allocating memory if necessary. +/// Automatically uses XDG Desktop Portal in Flatpak environments. +/// Caller must free returned memory with the provided allocator. +/// Returns null if the setting is unavailable. +pub fn getAlloc(allocator: std.mem.Allocator, comptime key: Key) !?key.Type() { + const settings = gtk.Settings.getDefault() orelse return null; + return getImpl(settings, allocator, key); +} + +fn getImpl(settings: *gtk.Settings, allocator: ?std.mem.Allocator, comptime key: Key) !?key.Type() { + const GValType = key.GValueType(); + var value = gobject.ext.Value.new(GValType); + defer value.unset(); + + settings.as(gobject.Object).getProperty(@tagName(key).ptr, &value); + + return switch (key.Type()) { + bool => value.getInt() != 0, + c_int => value.getInt(), + []const u8 => blk: { + const alloc = allocator.?; + const ptr = value.getString() orelse break :blk null; + const str = std.mem.span(ptr); + break :blk try alloc.dupe(u8, str); + }, + else => @compileError("Unsupported type for GTK settings"), + }; +} + +test "Key.Type returns correct types" { + try std.testing.expectEqual(bool, Key.@"gtk-enable-primary-paste".Type()); + try std.testing.expectEqual(c_int, Key.@"gtk-xft-dpi".Type()); + try std.testing.expectEqual([]const u8, Key.@"gtk-font-name".Type()); +} + +test "Key.requiresAllocation identifies allocating types" { + try std.testing.expectEqual(false, Key.@"gtk-enable-primary-paste".requiresAllocation()); + try std.testing.expectEqual(false, Key.@"gtk-xft-dpi".requiresAllocation()); + try std.testing.expectEqual(true, Key.@"gtk-font-name".requiresAllocation()); +} + +test "Key.GValueType returns correct GObject types" { + try std.testing.expectEqual(c_int, Key.@"gtk-enable-primary-paste".GValueType()); + try std.testing.expectEqual(c_int, Key.@"gtk-xft-dpi".GValueType()); + try std.testing.expectEqual(?[*:0]const u8, Key.@"gtk-font-name".GValueType()); +} + +test "@tagName returns correct GTK property names" { + try std.testing.expectEqualStrings("gtk-enable-primary-paste", @tagName(Key.@"gtk-enable-primary-paste")); + try std.testing.expectEqualStrings("gtk-xft-dpi", @tagName(Key.@"gtk-xft-dpi")); + try std.testing.expectEqualStrings("gtk-font-name", @tagName(Key.@"gtk-font-name")); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/gtk_version.zig b/vendor/libghostty-vt/src/apprt/gtk/gtk_version.zig new file mode 100644 index 00000000..71edb076 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/gtk_version.zig @@ -0,0 +1,140 @@ +const std = @import("std"); + +// Until the gobject bindings are built at the same time we are building +// Ghostty, we need to import `gtk/gtk.h` directly to ensure that the version +// macros match the version of `gtk4` that we are building/linking against. +const c = @cImport({ + @cInclude("gtk/gtk.h"); +}); + +const gtk = @import("gtk"); + +const log = std.log.scoped(.gtk); + +pub const comptime_version: std.SemanticVersion = .{ + .major = c.GTK_MAJOR_VERSION, + .minor = c.GTK_MINOR_VERSION, + .patch = c.GTK_MICRO_VERSION, +}; + +pub fn getRuntimeVersion() std.SemanticVersion { + return .{ + .major = gtk.getMajorVersion(), + .minor = gtk.getMinorVersion(), + .patch = gtk.getMicroVersion(), + }; +} + +pub fn logVersion() void { + log.info("GTK version build={f} runtime={f}", .{ + comptime_version, + getRuntimeVersion(), + }); +} + +/// Verifies that the GTK version is at least the given version. +/// +/// This can be run in both a comptime and runtime context. If it is run in a +/// comptime context, it will only check the version in the headers. If it is +/// run in a runtime context, it will check the actual version of the library we +/// are linked against. +/// +/// This function should be used in cases where the version check would affect +/// code generation, such as using symbols that are only available beyond a +/// certain version. For checks which only depend on GTK's runtime behavior, +/// use `runtimeAtLeast`. +/// +/// This is inlined so that the comptime checks will disable the runtime checks +/// if the comptime checks fail. +pub inline fn atLeast( + comptime major: u16, + comptime minor: u16, + comptime micro: u16, +) bool { + // If our header has lower versions than the given version, + // we can return false immediately. This prevents us from + // compiling against unknown symbols and makes runtime checks + // very slightly faster. + if (comptime comptime_version.order(.{ + .major = major, + .minor = minor, + .patch = micro, + }) == .lt) return false; + + // If we're in comptime then we can't check the runtime version. + if (@inComptime()) return true; + + return runtimeAtLeast(major, minor, micro); +} + +/// Verifies that the GTK version at runtime is at least the given version. +/// +/// This function should be used in cases where the only the runtime behavior +/// is affected by the version check. For checks which would affect code +/// generation, use `atLeast`. +pub inline fn runtimeAtLeast( + comptime major: u16, + comptime minor: u16, + comptime micro: u16, +) bool { + // We use the functions instead of the constants such as c.GTK_MINOR_VERSION + // because the function gets the actual runtime version. + const runtime_version = getRuntimeVersion(); + return runtime_version.order(.{ + .major = major, + .minor = minor, + .patch = micro, + }) != .lt; +} + +pub inline fn runtimeUntil( + comptime major: u16, + comptime minor: u16, + comptime micro: u16, +) bool { + const runtime_version = getRuntimeVersion(); + return runtime_version.order(.{ + .major = major, + .minor = minor, + .patch = micro, + }) == .lt; +} + +test "atLeast" { + const testing = std.testing; + + const funs = &.{ atLeast, runtimeAtLeast }; + inline for (funs) |fun| { + try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION)); + + try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1)); + try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION)); + try testing.expect(!fun(c.GTK_MAJOR_VERSION + 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION)); + + try testing.expect(fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION)); + try testing.expect(fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION)); + try testing.expect(fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1)); + + try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION - 1, c.GTK_MICRO_VERSION + 1)); + } +} + +test "runtimeUntil" { + const testing = std.testing; + + // This is an array in case we add a comptime variant. + const funs = &.{runtimeUntil}; + inline for (funs) |fun| { + try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION)); + + try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1)); + try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION)); + try testing.expect(fun(c.GTK_MAJOR_VERSION + 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION)); + + try testing.expect(!fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION)); + try testing.expect(!fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION + 1, c.GTK_MICRO_VERSION)); + try testing.expect(!fun(c.GTK_MAJOR_VERSION - 1, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION + 1)); + + try testing.expect(!fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION - 1, c.GTK_MICRO_VERSION + 1)); + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig b/vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig new file mode 100644 index 00000000..fa4a6723 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig @@ -0,0 +1,196 @@ +//! DBus helper for IPC +const Self = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const gio = @import("gio"); +const glib = @import("glib"); + +const apprt = @import("../../../apprt.zig"); +const ApprtApp = @import("../App.zig"); + +/// The target for this IPC. +target: apprt.ipc.Target, + +/// Connection to the DBus session bus. +dbus: *gio.DBusConnection, + +/// The bus name of the Ghostty instance that we are calling. +bus_name: [:0]const u8, + +/// The object path of the Ghostty instance that we are calling. +object_path: [:0]const u8, + +/// Used to build the DBus payload. +payload_builder: *glib.VariantBuilder, + +/// Used to build the parameters for the IPC. +parameters_builder: *glib.VariantBuilder, + +/// Initialize the helper. +pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!Self { + var buf: [256]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&buf); + const stderr = &stderr_writer.interface; + + // Get the appropriate bus name and object path for contacting the + // Ghostty instance we're interested in. + const bus_name: [:0]const u8, const object_path: [:0]const u8 = switch (target) { + .class => |class| result: { + // Force the usage of the class specified on the CLI to determine the + // bus name and object path. + const object_path = try std.fmt.allocPrintSentinel(alloc, "/{s}", .{class}, 0); + + std.mem.replaceScalar(u8, object_path, '.', '/'); + std.mem.replaceScalar(u8, object_path, '-', '_'); + + break :result .{ class, object_path }; + }, + .detect => .{ ApprtApp.application_id, ApprtApp.object_path }, + }; + errdefer { + switch (target) { + .class => alloc.free(object_path), + .detect => {}, + } + } + + if (gio.Application.idIsValid(bus_name.ptr) == 0) { + try stderr.print("D-Bus bus name is not valid: {s}\n", .{bus_name}); + try stderr.flush(); + return error.IPCFailed; + } + + if (glib.Variant.isObjectPath(object_path.ptr) == 0) { + try stderr.print("D-Bus object path is not valid: {s}\n", .{object_path}); + try stderr.flush(); + return error.IPCFailed; + } + + // Get a connection to the DBus session bus. + const dbus = dbus: { + var err_: ?*glib.Error = null; + defer if (err_) |err| err.free(); + + const dbus_ = gio.busGetSync(.session, null, &err_); + if (err_) |err| { + try stderr.print( + "Unable to establish connection to D-Bus session bus: {s}\n", + .{err.f_message orelse "(unknown)"}, + ); + try stderr.flush(); + return error.IPCFailed; + } + + break :dbus dbus_ orelse { + try stderr.print("gio.busGetSync returned null\n", .{}); + try stderr.flush(); + return error.IPCFailed; + }; + }; + + // Set up the payload builder. + const payload_variant_type = glib.VariantType.new("(sava{sv})"); + defer glib.free(payload_variant_type); + + const payload_builder = glib.VariantBuilder.new(payload_variant_type); + + // Add the action name to the payload. + { + const s_variant_type = glib.VariantType.new("s"); + defer s_variant_type.free(); + + const bytes = glib.Bytes.new(action.ptr, action.len + 1); + defer bytes.unref(); + const value = glib.Variant.newFromBytes(s_variant_type, bytes, @intFromBool(true)); + + payload_builder.addValue(value); + } + + // Set up the parameter builder. + const parameters_variant_type = glib.VariantType.new("av"); + defer parameters_variant_type.free(); + + const parameters_builder = glib.VariantBuilder.new(parameters_variant_type); + + return .{ + .target = target, + .dbus = dbus, + .bus_name = bus_name, + .object_path = object_path, + .payload_builder = payload_builder, + .parameters_builder = parameters_builder, + }; +} + +/// Add a parameter to the IPC call. +pub fn addParameter(self: *Self, variant: *glib.Variant) void { + self.parameters_builder.add("v", variant); +} + +/// Send the IPC to the remote Ghostty. Once it completes, nothing further +/// should be done with this object other than call `deinit`. +pub fn send(self: *Self) (std.Io.Writer.Error || apprt.ipc.Errors)!void { + var buf: [256]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&buf); + const stderr = &stderr_writer.interface; + + // finish building the parameters + const parameters = self.parameters_builder.end(); + + // Add the parameters to the payload. + self.payload_builder.addValue(parameters); + + // Add the platform data to the payload. + { + const platform_data_variant_type = glib.VariantType.new("a{sv}"); + defer platform_data_variant_type.free(); + + self.payload_builder.open(platform_data_variant_type); + defer self.payload_builder.close(); + + // We have no platform data. + } + + const payload = self.payload_builder.end(); + + { + var err_: ?*glib.Error = null; + defer if (err_) |err| err.free(); + + const result_ = self.dbus.callSync( + self.bus_name, + self.object_path, + "org.gtk.Actions", + "Activate", + payload, + null, // We don't care about the return type, we don't do anything with it. + .{}, // no flags + -1, // default timeout + null, // not cancellable + &err_, + ); + defer if (result_) |result| result.unref(); + + if (err_) |err| { + try stderr.print( + "D-Bus method call returned an error err={s}\n", + .{err.f_message orelse "(unknown)"}, + ); + try stderr.flush(); + return error.IPCFailed; + } + } +} + +/// Free/unref any data held by this instance. +pub fn deinit(self: *Self, alloc: Allocator) void { + switch (self.target) { + .class => alloc.free(self.object_path), + .detect => {}, + } + self.parameters_builder.unref(); + self.payload_builder.unref(); + self.dbus.unref(); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ipc/new_window.zig b/vendor/libghostty-vt/src/apprt/gtk/ipc/new_window.zig new file mode 100644 index 00000000..02fed322 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ipc/new_window.zig @@ -0,0 +1,62 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const glib = @import("glib"); + +const apprt = @import("../../../apprt.zig"); +const DBus = @import("DBus.zig"); + +// Use a D-Bus method call to open a new window on GTK. +// See: https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI +// +// `ghostty +new-window` is equivalent to the following command (on a release build): +// +// ``` +// gdbus call --session --dest com.mitchellh.ghostty --object-path /com/mitchellh/ghostty --method org.gtk.Actions.Activate new-window [] [] +// ``` +// +// `ghostty +new-window -e echo hello` would be equivalent to the following command (on a release build): +// +// ``` +// gdbus call --session --dest com.mitchellh.ghostty --object-path /com/mitchellh/ghostty --method org.gtk.Actions.Activate new-window-command '[<@as ["-e" "echo" "hello"]>]' [] +// ``` +pub fn newWindow(alloc: Allocator, target: apprt.ipc.Target, value: apprt.ipc.Action.NewWindow) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!bool { + var dbus = try DBus.init( + alloc, + target, + if (value.arguments == null) + "new-window" + else + "new-window-command", + ); + defer dbus.deinit(alloc); + + if (value.arguments) |arguments| { + // If any arguments were specified on the command line, the first + // parameter is an array of strings that contain the arguments. They + // will be sent to the main Ghostty instance and interpreted as CLI + // arguments. + const as_variant_type = glib.VariantType.new("as"); + defer as_variant_type.free(); + + const s_variant_type = glib.VariantType.new("s"); + defer s_variant_type.free(); + + var command: glib.VariantBuilder = undefined; + command.init(as_variant_type); + errdefer command.clear(); + + for (arguments) |argument| { + const bytes = glib.Bytes.new(argument.ptr, argument.len + 1); + defer bytes.unref(); + const string = glib.Variant.newFromBytes(s_variant_type, bytes, @intFromBool(true)); + command.addValue(string); + } + + dbus.addParameter(command.end()); + } + + try dbus.send(); + + return true; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/key.zig b/vendor/libghostty-vt/src/apprt/gtk/key.zig new file mode 100644 index 00000000..5f717e14 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/key.zig @@ -0,0 +1,535 @@ +const std = @import("std"); + +const gdk = @import("gdk"); +const glib = @import("glib"); +const gtk = @import("gtk"); + +const input = @import("../../input.zig"); +const winproto = @import("winproto.zig"); + +/// Returns a GTK accelerator string from a trigger. +pub fn accelFromTrigger( + buf: []u8, + trigger: input.Binding.Trigger, +) error{WriteFailed}!?[:0]const u8 { + var writer: std.Io.Writer = .fixed(buf); + + // Modifiers + if (trigger.mods.shift) try writer.writeAll(""); + if (trigger.mods.ctrl) try writer.writeAll(""); + if (trigger.mods.alt) try writer.writeAll(""); + if (trigger.mods.super) try writer.writeAll(""); + + // Write our key + if (!try writeTriggerKey(&writer, trigger)) return null; + + // We need to make the string null terminated. + try writer.writeByte(0); + const slice = writer.buffered(); + return slice[0 .. slice.len - 1 :0]; +} + +/// Returns a XDG-compliant shortcuts string from a trigger. +/// Spec: https://specifications.freedesktop.org/shortcuts-spec/latest/ +pub fn xdgShortcutFromTrigger( + buf: []u8, + trigger: input.Binding.Trigger, +) error{WriteFailed}!?[:0]const u8 { + var writer: std.Io.Writer = .fixed(buf); + + // Modifiers + if (trigger.mods.shift) try writer.writeAll("SHIFT+"); + if (trigger.mods.ctrl) try writer.writeAll("CTRL+"); + if (trigger.mods.alt) try writer.writeAll("ALT+"); + if (trigger.mods.super) try writer.writeAll("LOGO+"); + + // Write our key + // NOTE: While the spec specifies that only libxkbcommon keysyms are + // expected, using GTK's keysyms should still work as they are identical + // to *X11's* keysyms (which I assume is a subset of libxkbcommon's). + // I haven't been able to any evidence to back up that assumption but + // this works for now + if (!try writeTriggerKey(&writer, trigger)) return null; + + // We need to make the string null terminated. + try writer.writeByte(0); + const slice = writer.buffered(); + return slice[0 .. slice.len - 1 :0]; +} + +fn writeTriggerKey( + writer: *std.Io.Writer, + trigger: input.Binding.Trigger, +) error{WriteFailed}!bool { + switch (trigger.key) { + .physical => |k| { + const keyval = keyvalFromKey(k) orelse return false; + try writer.writeAll(std.mem.span(gdk.keyvalName(keyval) orelse return false)); + }, + + .unicode => |cp| { + if (gdk.keyvalName(cp)) |name| { + try writer.writeAll(std.mem.span(name)); + } else { + try writer.print("{u}", .{cp}); + } + }, + + .catch_all => return false, + } + + return true; +} + +pub fn translateMods(state: gdk.ModifierType) input.Mods { + return .{ + .shift = state.shift_mask, + .ctrl = state.control_mask, + .alt = state.alt_mask, + .super = state.super_mask, + // Lock is dependent on the X settings but we just assume caps lock. + .caps_lock = state.lock_mask, + }; +} + +// Get the unshifted unicode value of the keyval. This is used +// by the Kitty keyboard protocol. +pub fn keyvalUnicodeUnshifted( + widget: *gtk.Widget, + event: *gdk.KeyEvent, + keycode: u32, +) u21 { + const display = widget.getDisplay(); + + // We need to get the currently active keyboard layout so we know + // what group to look at. + const layout = event.getLayout(); + + // Get all the possible keyboard mappings for this keycode. A keycode is the + // physical key pressed. + var keys: [*]gdk.KeymapKey = undefined; + var keyvals: [*]c_uint = undefined; + var n_entries: c_int = 0; + if (display.mapKeycode(keycode, &keys, &keyvals, &n_entries) == 0) return 0; + + defer glib.free(keys); + defer glib.free(keyvals); + + // debugging: + // std.log.debug("layout={}", .{layout}); + // for (0..@intCast(n_entries)) |i| { + // std.log.debug("keymap key={} codepoint={x}", .{ + // keys[i], + // gdk.keyvalToUnicode(keyvals[i]), + // }); + // } + + for (0..@intCast(n_entries)) |i| { + if (keys[i].f_group == layout and + keys[i].f_level == 0) + { + return std.math.cast( + u21, + gdk.keyvalToUnicode(keyvals[i]), + ) orelse 0; + } + } + + return 0; +} + +/// Returns the mods to use a key event from a GTK event. +/// This requires a lot of context because the GdkEvent +/// doesn't contain enough on its own. +pub fn eventMods( + event: *gdk.Event, + physical_key: input.Key, + gtk_mods: gdk.ModifierType, + action: input.Action, + app_winproto: *winproto.App, +) input.Mods { + const device = event.getDevice(); + + var mods = app_winproto.eventMods(device, gtk_mods); + mods.num_lock = if (device) |d| d.getNumLockState() != 0 else false; + + // We use the physical key to determine sided modifiers. As + // far as I can tell there's no other way to reliably determine + // this. + // + // We also set the main modifier to true if either side is true, + // since on both X11/Wayland, GTK doesn't set the main modifier + // if only the modifier key is pressed, but our core logic + // relies on it. + switch (physical_key) { + .shift_left => { + mods.shift = action != .release; + mods.sides.shift = .left; + }, + + .shift_right => { + mods.shift = action != .release; + mods.sides.shift = .right; + }, + + .control_left => { + mods.ctrl = action != .release; + mods.sides.ctrl = .left; + }, + + .control_right => { + mods.ctrl = action != .release; + mods.sides.ctrl = .right; + }, + + .alt_left => { + mods.alt = action != .release; + mods.sides.alt = .left; + }, + + .alt_right => { + mods.alt = action != .release; + mods.sides.alt = .right; + }, + + .meta_left => { + mods.super = action != .release; + mods.sides.super = .left; + }, + + .meta_right => { + mods.super = action != .release; + mods.sides.super = .right; + }, + + else => {}, + } + + return mods; +} + +/// Returns an input key from a keyval or null if we don't have a mapping. +pub fn keyFromKeyval(keyval: c_uint) ?input.Key { + for (keymap) |entry| { + if (entry[0] == keyval) return entry[1]; + } + + return null; +} + +/// Returns a keyval from an input key or null if we don't have a mapping. +pub fn keyvalFromKey(key: input.Key) ?c_uint { + switch (key) { + inline else => |key_comptime| { + return comptime value: { + @setEvalBranchQuota(50_000); + for (keymap) |entry| { + if (entry[1] == key_comptime) break :value entry[0]; + } + + break :value null; + }; + }, + } +} + +/// Converts a trigger to a human-readable label for display in UI. +/// +/// Uses GTK accelerator-style formatting (e.g., "Ctrl+Shift+A"). +/// Returns false if the trigger cannot be formatted (e.g., catch_all). +pub fn labelFromTrigger( + writer: *std.Io.Writer, + trigger: input.Binding.Trigger, +) std.Io.Writer.Error!bool { + // Modifiers first, using human-readable format + if (trigger.mods.super) try writer.writeAll("Super+"); + if (trigger.mods.ctrl) try writer.writeAll("Ctrl+"); + if (trigger.mods.alt) try writer.writeAll("Alt+"); + if (trigger.mods.shift) try writer.writeAll("Shift+"); + + // Write the key + return writeTriggerKeyLabel(writer, trigger); +} + +/// Writes the key portion of a trigger in human-readable format. +fn writeTriggerKeyLabel( + writer: *std.Io.Writer, + trigger: input.Binding.Trigger, +) error{WriteFailed}!bool { + switch (trigger.key) { + .physical => |k| { + const keyval = keyvalFromKey(k) orelse return false; + const name = gdk.keyvalName(keyval) orelse return false; + // Capitalize the first letter for nicer display + const span = std.mem.span(name); + if (span.len > 0) { + if (span[0] >= 'a' and span[0] <= 'z') { + try writer.writeByte(span[0] - 'a' + 'A'); + if (span.len > 1) try writer.writeAll(span[1..]); + } else { + try writer.writeAll(span); + } + } + }, + + .unicode => |cp| { + // Try to get a nice name from GDK first + if (gdk.keyvalName(cp)) |name| { + const span = std.mem.span(name); + if (span.len > 0) { + // Capitalize the first letter for nicer display + if (span[0] >= 'a' and span[0] <= 'z') { + try writer.writeByte(span[0] - 'a' + 'A'); + if (span.len > 1) try writer.writeAll(span[1..]); + } else { + try writer.writeAll(span); + } + } + } else { + // Fall back to printing the character + try writer.print("{u}", .{cp}); + } + }, + + .catch_all => return false, + } + + return true; +} + +test "accelFromTrigger" { + const testing = std.testing; + var buf: [256]u8 = undefined; + + try testing.expectEqualStrings("q", (try accelFromTrigger(&buf, .{ + .mods = .{ .super = true }, + .key = .{ .unicode = 'q' }, + })).?); + + try testing.expectEqualStrings("backslash", (try accelFromTrigger(&buf, .{ + .mods = .{ .ctrl = true, .alt = true, .super = true, .shift = true }, + .key = .{ .unicode = 92 }, + })).?); +} + +test "xdgShortcutFromTrigger" { + const testing = std.testing; + var buf: [256]u8 = undefined; + + try testing.expectEqualStrings("LOGO+q", (try xdgShortcutFromTrigger(&buf, .{ + .mods = .{ .super = true }, + .key = .{ .unicode = 'q' }, + })).?); + + try testing.expectEqualStrings("SHIFT+CTRL+ALT+LOGO+backslash", (try xdgShortcutFromTrigger(&buf, .{ + .mods = .{ .ctrl = true, .alt = true, .super = true, .shift = true }, + .key = .{ .unicode = 92 }, + })).?); +} + +test "labelFromTrigger" { + const testing = std.testing; + + // Simple unicode key with modifier + { + var buf: std.Io.Writer.Allocating = .init(testing.allocator); + defer buf.deinit(); + try testing.expect(try labelFromTrigger(&buf.writer, .{ + .mods = .{ .super = true }, + .key = .{ .unicode = 'q' }, + })); + try testing.expectEqualStrings("Super+Q", buf.written()); + } + + // Multiple modifiers + { + var buf: std.Io.Writer.Allocating = .init(testing.allocator); + defer buf.deinit(); + try testing.expect(try labelFromTrigger(&buf.writer, .{ + .mods = .{ .ctrl = true, .alt = true, .super = true, .shift = true }, + .key = .{ .unicode = 92 }, + })); + try testing.expectEqualStrings("Super+Ctrl+Alt+Shift+Backslash", buf.written()); + } + + // Physical key + { + var buf: std.Io.Writer.Allocating = .init(testing.allocator); + defer buf.deinit(); + try testing.expect(try labelFromTrigger(&buf.writer, .{ + .mods = .{ .ctrl = true }, + .key = .{ .physical = .key_a }, + })); + try testing.expectEqualStrings("Ctrl+A", buf.written()); + } + + // No modifiers + { + var buf: std.Io.Writer.Allocating = .init(testing.allocator); + defer buf.deinit(); + try testing.expect(try labelFromTrigger(&buf.writer, .{ + .mods = .{}, + .key = .{ .physical = .escape }, + })); + try testing.expectEqualStrings("Escape", buf.written()); + } + + // catch_all returns false + { + var buf: std.Io.Writer.Allocating = .init(testing.allocator); + defer buf.deinit(); + try testing.expect(!try labelFromTrigger(&buf.writer, .{ + .mods = .{}, + .key = .catch_all, + })); + } +} + +/// A raw entry in the keymap. Our keymap contains mappings between +/// GDK keys and our own key enum. +const RawEntry = struct { c_uint, input.Key }; + +const keymap: []const RawEntry = &.{ + .{ gdk.KEY_a, .key_a }, + .{ gdk.KEY_b, .key_b }, + .{ gdk.KEY_c, .key_c }, + .{ gdk.KEY_d, .key_d }, + .{ gdk.KEY_e, .key_e }, + .{ gdk.KEY_f, .key_f }, + .{ gdk.KEY_g, .key_g }, + .{ gdk.KEY_h, .key_h }, + .{ gdk.KEY_i, .key_i }, + .{ gdk.KEY_j, .key_j }, + .{ gdk.KEY_k, .key_k }, + .{ gdk.KEY_l, .key_l }, + .{ gdk.KEY_m, .key_m }, + .{ gdk.KEY_n, .key_n }, + .{ gdk.KEY_o, .key_o }, + .{ gdk.KEY_p, .key_p }, + .{ gdk.KEY_q, .key_q }, + .{ gdk.KEY_r, .key_r }, + .{ gdk.KEY_s, .key_s }, + .{ gdk.KEY_t, .key_t }, + .{ gdk.KEY_u, .key_u }, + .{ gdk.KEY_v, .key_v }, + .{ gdk.KEY_w, .key_w }, + .{ gdk.KEY_x, .key_x }, + .{ gdk.KEY_y, .key_y }, + .{ gdk.KEY_z, .key_z }, + + .{ gdk.KEY_0, .digit_0 }, + .{ gdk.KEY_1, .digit_1 }, + .{ gdk.KEY_2, .digit_2 }, + .{ gdk.KEY_3, .digit_3 }, + .{ gdk.KEY_4, .digit_4 }, + .{ gdk.KEY_5, .digit_5 }, + .{ gdk.KEY_6, .digit_6 }, + .{ gdk.KEY_7, .digit_7 }, + .{ gdk.KEY_8, .digit_8 }, + .{ gdk.KEY_9, .digit_9 }, + + .{ gdk.KEY_semicolon, .semicolon }, + .{ gdk.KEY_space, .space }, + .{ gdk.KEY_apostrophe, .quote }, + .{ gdk.KEY_comma, .comma }, + .{ gdk.KEY_grave, .backquote }, + .{ gdk.KEY_period, .period }, + .{ gdk.KEY_slash, .slash }, + .{ gdk.KEY_minus, .minus }, + .{ gdk.KEY_equal, .equal }, + .{ gdk.KEY_bracketleft, .bracket_left }, + .{ gdk.KEY_bracketright, .bracket_right }, + .{ gdk.KEY_backslash, .backslash }, + + .{ gdk.KEY_Up, .arrow_up }, + .{ gdk.KEY_Down, .arrow_down }, + .{ gdk.KEY_Right, .arrow_right }, + .{ gdk.KEY_Left, .arrow_left }, + .{ gdk.KEY_Home, .home }, + .{ gdk.KEY_End, .end }, + .{ gdk.KEY_Insert, .insert }, + .{ gdk.KEY_Delete, .delete }, + .{ gdk.KEY_Caps_Lock, .caps_lock }, + .{ gdk.KEY_Scroll_Lock, .scroll_lock }, + .{ gdk.KEY_Num_Lock, .num_lock }, + .{ gdk.KEY_Page_Up, .page_up }, + .{ gdk.KEY_Page_Down, .page_down }, + .{ gdk.KEY_Escape, .escape }, + .{ gdk.KEY_Return, .enter }, + .{ gdk.KEY_Tab, .tab }, + .{ gdk.KEY_BackSpace, .backspace }, + .{ gdk.KEY_Print, .print_screen }, + .{ gdk.KEY_Pause, .pause }, + + .{ gdk.KEY_F1, .f1 }, + .{ gdk.KEY_F2, .f2 }, + .{ gdk.KEY_F3, .f3 }, + .{ gdk.KEY_F4, .f4 }, + .{ gdk.KEY_F5, .f5 }, + .{ gdk.KEY_F6, .f6 }, + .{ gdk.KEY_F7, .f7 }, + .{ gdk.KEY_F8, .f8 }, + .{ gdk.KEY_F9, .f9 }, + .{ gdk.KEY_F10, .f10 }, + .{ gdk.KEY_F11, .f11 }, + .{ gdk.KEY_F12, .f12 }, + .{ gdk.KEY_F13, .f13 }, + .{ gdk.KEY_F14, .f14 }, + .{ gdk.KEY_F15, .f15 }, + .{ gdk.KEY_F16, .f16 }, + .{ gdk.KEY_F17, .f17 }, + .{ gdk.KEY_F18, .f18 }, + .{ gdk.KEY_F19, .f19 }, + .{ gdk.KEY_F20, .f20 }, + .{ gdk.KEY_F21, .f21 }, + .{ gdk.KEY_F22, .f22 }, + .{ gdk.KEY_F23, .f23 }, + .{ gdk.KEY_F24, .f24 }, + .{ gdk.KEY_F25, .f25 }, + + .{ gdk.KEY_KP_0, .numpad_0 }, + .{ gdk.KEY_KP_1, .numpad_1 }, + .{ gdk.KEY_KP_2, .numpad_2 }, + .{ gdk.KEY_KP_3, .numpad_3 }, + .{ gdk.KEY_KP_4, .numpad_4 }, + .{ gdk.KEY_KP_5, .numpad_5 }, + .{ gdk.KEY_KP_6, .numpad_6 }, + .{ gdk.KEY_KP_7, .numpad_7 }, + .{ gdk.KEY_KP_8, .numpad_8 }, + .{ gdk.KEY_KP_9, .numpad_9 }, + .{ gdk.KEY_KP_Decimal, .numpad_decimal }, + .{ gdk.KEY_KP_Divide, .numpad_divide }, + .{ gdk.KEY_KP_Multiply, .numpad_multiply }, + .{ gdk.KEY_KP_Subtract, .numpad_subtract }, + .{ gdk.KEY_KP_Add, .numpad_add }, + .{ gdk.KEY_KP_Enter, .numpad_enter }, + .{ gdk.KEY_KP_Equal, .numpad_equal }, + + .{ gdk.KEY_KP_Separator, .numpad_separator }, + .{ gdk.KEY_KP_Left, .numpad_left }, + .{ gdk.KEY_KP_Right, .numpad_right }, + .{ gdk.KEY_KP_Up, .numpad_up }, + .{ gdk.KEY_KP_Down, .numpad_down }, + .{ gdk.KEY_KP_Page_Up, .numpad_page_up }, + .{ gdk.KEY_KP_Page_Down, .numpad_page_down }, + .{ gdk.KEY_KP_Home, .numpad_home }, + .{ gdk.KEY_KP_End, .numpad_end }, + .{ gdk.KEY_KP_Insert, .numpad_insert }, + .{ gdk.KEY_KP_Delete, .numpad_delete }, + .{ gdk.KEY_KP_Begin, .numpad_begin }, + + .{ gdk.KEY_Copy, .copy }, + .{ gdk.KEY_Cut, .cut }, + .{ gdk.KEY_Paste, .paste }, + + .{ gdk.KEY_Shift_L, .shift_left }, + .{ gdk.KEY_Control_L, .control_left }, + .{ gdk.KEY_Alt_L, .alt_left }, + .{ gdk.KEY_Super_L, .meta_left }, + .{ gdk.KEY_Shift_R, .shift_right }, + .{ gdk.KEY_Control_R, .control_right }, + .{ gdk.KEY_Alt_R, .alt_right }, + .{ gdk.KEY_Super_R, .meta_right }, + + // TODO: media keys +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/media.zig b/vendor/libghostty-vt/src/apprt/gtk/media.zig new file mode 100644 index 00000000..1015c933 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/media.zig @@ -0,0 +1,102 @@ +const std = @import("std"); +const assert = @import("../../quirks.zig").inlineAssert; + +const log = std.log.scoped(.gtk_media); + +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); + +pub fn fromFilename(path: [:0]const u8) ?*gtk.MediaFile { + assert(std.fs.path.isAbsolute(path)); + std.fs.accessAbsolute(path, .{ .mode = .read_only }) catch |err| { + log.warn("unable to access {s}: {t}", .{ path, err }); + return null; + }; + return gtk.MediaFile.newForFilename(path); +} + +pub fn fromResource(path: [:0]const u8) ?*gtk.MediaFile { + assert(std.fs.path.isAbsolute(path)); + var gerr: ?*glib.Error = null; + + const found = gio.resourcesGetInfo(path, .{}, null, null, &gerr); + if (gerr) |err| { + defer err.free(); + log.warn( + "failed to find resource {s}: {s} {d} {s}", + .{ + path, + glib.quarkToString(err.f_domain), + err.f_code, + err.f_message orelse "(no message)", + }, + ); + return null; + } + + if (found == 0) { + log.warn("failed to find resource {s}", .{path}); + return null; + } + + return gtk.MediaFile.newForResource(path); +} + +pub fn playMediaFile(media_file: *gtk.MediaFile, volume: f64, required: bool) void { + // If the audio file is marked as required, we'll emit an error if + // there was a problem playing it. Otherwise there will be silence. + if (required) { + _ = gobject.Object.signals.notify.connect( + media_file, + ?*anyopaque, + mediaFileError, + null, + .{ .detail = "error" }, + ); + } + + // Watch for the "ended" signal so that we can clean up after + // ourselves. + _ = gobject.Object.signals.notify.connect( + media_file, + ?*anyopaque, + mediaFileEnded, + null, + .{ .detail = "ended" }, + ); + + const media_stream = media_file.as(gtk.MediaStream); + media_stream.setVolume(volume); + media_stream.play(); +} + +fn mediaFileError( + media_file: *gtk.MediaFile, + _: *gobject.ParamSpec, + _: ?*anyopaque, +) callconv(.c) void { + const path = path: { + const file = media_file.getFile() orelse break :path null; + break :path file.getPath(); + }; + defer if (path) |p| glib.free(p); + + const media_stream = media_file.as(gtk.MediaStream); + const err = media_stream.getError() orelse return; + log.warn("error playing sound from {s}: {s} {d} {s}", .{ + path orelse "<>", + glib.quarkToString(err.f_domain), + err.f_code, + err.f_message orelse "", + }); +} + +fn mediaFileEnded( + media_file: *gtk.MediaFile, + _: *gobject.ParamSpec, + _: ?*anyopaque, +) callconv(.c) void { + media_file.unref(); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/portal.zig b/vendor/libghostty-vt/src/apprt/gtk/portal.zig new file mode 100644 index 00000000..3e51042d --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/portal.zig @@ -0,0 +1,105 @@ +const std = @import("std"); + +const gio = @import("gio"); + +const Allocator = std.mem.Allocator; + +pub const OpenURI = @import("portal/OpenURI.zig"); +pub const token_hex_len = @sizeOf(usize) * 2; +pub const TokenBuffer = [token_hex_len + 1]u8; +const token_format = std.fmt.comptimePrint("{{x:0>{}}}", .{token_hex_len}); + +/// Generate a token suitable for use in requests to the XDG Desktop Portal +pub fn generateToken() usize { + return std.crypto.random.int(usize); +} + +/// Format a request token consistently for use in portal object paths and payloads. +pub fn formatToken(buf: *TokenBuffer, token: usize) [:0]const u8 { + return std.fmt.bufPrintZ(buf, token_format, .{token}) catch unreachable; +} + +/// Get the XDG portal request path for the current Ghostty instance. +/// +/// See https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html +/// for the protocol of the Request interface. +pub fn getRequestPath(alloc: Allocator, dbus: *gio.DBusConnection, token: usize) (Allocator.Error || error{NoDBusUniqueName})![:0]const u8 { + // Get the unique name from D-Bus and strip the leading `:` + const unique_name = std.mem.span( + dbus.getUniqueName() orelse { + return error.NoDBusUniqueName; + }, + )[1..]; + + return buildRequestPath(alloc, unique_name, token); +} + +/// Build the XDG portal request path for given unique name and token. +fn buildRequestPath(alloc: Allocator, unique_name: []const u8, token: usize) Allocator.Error![:0]const u8 { + var token_buf: TokenBuffer = undefined; + const token_string = formatToken(&token_buf, token); + + const object_path = try std.mem.joinZ( + alloc, + "/", + &.{ + "/org/freedesktop/portal/desktop/request", + unique_name, + token_string, + }, + ); + + // Sanitize the unique name by replacing every `.` with `_`. In effect, this + // will turn a unique name like `1.192` into `1_192`. + // This sounds arbitrary, but it's part of the Request protocol. + _ = std.mem.replaceScalar(u8, object_path, '.', '_'); + + return object_path; +} + +/// Try and parse the token out of a request path. +pub fn parseRequestPathToken(request_path: []const u8) ?usize { + const index = std.mem.lastIndexOfScalar(u8, request_path, '/') orelse return null; + const token = request_path[index + 1 ..]; + return std.fmt.parseUnsigned(usize, token, 16) catch return null; +} + +test "formatToken pads to fixed width" { + const testing = std.testing; + + var token_buf: TokenBuffer = undefined; + const token = formatToken(&token_buf, 0x42); + + try testing.expectEqual(@as(usize, token_hex_len), token.len); + try testing.expectEqualStrings("0000000000000042", token); +} + +test "buildRequestPath" { + const testing = std.testing; + + const path = try buildRequestPath(testing.allocator, "1.42", 0x75af01a79c6fea34); + try testing.expectEqualStrings( + "/org/freedesktop/portal/desktop/request/1_42/75af01a79c6fea34", + path, + ); + testing.allocator.free(path); +} + +test "buildRequestPath pads token" { + const testing = std.testing; + const path = try buildRequestPath(testing.allocator, "1.42", 0x42); + + try testing.expectEqualStrings( + "/org/freedesktop/portal/desktop/request/1_42/0000000000000042", + path, + ); + testing.allocator.free(path); +} + +test "parseRequestPathToken" { + const testing = std.testing; + + try testing.expectEqual(0x75af01a79c6fea34, parseRequestPathToken("/org/freedesktop/portal/desktop/request/1_42/75af01a79c6fea34").?); + try testing.expectEqual(null, parseRequestPathToken("/org/freedesktop/portal/desktop/request/1_42/75af01a79c6fGa34")); + try testing.expectEqual(null, parseRequestPathToken("75af01a79c6fea34")); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/portal/OpenURI.zig b/vendor/libghostty-vt/src/apprt/gtk/portal/OpenURI.zig new file mode 100644 index 00000000..97aa013e --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/portal/OpenURI.zig @@ -0,0 +1,565 @@ +//! Use DBus to call the XDG Desktop Portal to open an URI. +//! See: https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.OpenURI.html#org-freedesktop-portal-openuri-openuri +const OpenURI = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const gio = @import("gio"); +const glib = @import("glib"); +const gobject = @import("gobject"); + +const App = @import("../App.zig"); +const portal = @import("../portal.zig"); +const apprt = @import("../../../apprt.zig"); + +const log = std.log.scoped(.openuri); + +/// The GTK app that we "belong" to. +app: *App, + +/// Connection to the D-Bus session bus that we'll use for all of our messaging. +dbus: ?*gio.DBusConnection = null, + +/// Mutex to protect modification of the entries map or the cleanup timer. +mutex: std.Thread.Mutex = .{}, + +/// Map to store data about any in-flight calls to the portal. +entries: std.AutoArrayHashMapUnmanaged(usize, *Entry) = .empty, + +/// Used to manage a timer to clean up any orphan entries in the map. +cleanup_timer: ?c_uint = null, + +/// Set to false during shutdown so callbacks stop touching internal state. +alive: bool = true, + +const RequestData = struct { + open_uri: *OpenURI, + token: usize, + kind: apprt.action.OpenUrl.Kind, + uri: [:0]const u8, + request_path: [:0]const u8, + + pub fn init( + alloc: Allocator, + open_uri: *OpenURI, + token: usize, + kind: apprt.action.OpenUrl.Kind, + uri: []const u8, + request_path: []const u8, + ) Allocator.Error!*RequestData { + const uri_copy = try alloc.dupeZ(u8, uri); + errdefer alloc.free(uri_copy); + + const request_path_copy = try alloc.dupeZ(u8, request_path); + errdefer alloc.free(request_path_copy); + + const data = try alloc.create(RequestData); + errdefer alloc.destroy(data); + + data.* = .{ + .open_uri = open_uri, + .token = token, + .kind = kind, + .uri = uri_copy, + .request_path = request_path_copy, + }; + + return data; + } + + pub fn deinit(self: *const RequestData, alloc: Allocator) void { + alloc.free(self.uri); + alloc.free(self.request_path); + } +}; + +/// Data about any in-flight calls to the portal. +pub const Entry = struct { + /// When the request started. + start: std.time.Instant, + /// A token used by the portal to identify requests and responses. The + /// actual format of the token does not really matter as long as it can be + /// used as part of a D-Bus object path. `usize` was chosen since it's easy + /// to hash and to generate random tokens. + token: usize, + /// The "kind" of URI. Unused here, but we may need to pass it on to the + /// fallback URL opener if the D-Bus method fails. + kind: apprt.action.OpenUrl.Kind, + /// A copy of the URI that we are opening. We need our own copy since the + /// method calls are asynchronous and the original may have been freed by + /// the time we need it. + uri: [:0]const u8, + /// Used to manage a subscription to a D-Bus signal, which is how the XDG + /// Portal reports results of the method call. + subscription: ?c_uint = null, + + pub fn deinit(self: *const Entry, alloc: Allocator) void { + alloc.free(self.uri); + } +}; + +pub const Errors = error{ + /// Could not get a D-Bus connection + DBusConnectionRequired, + /// The D-Bus connection did not have a unique name. This _should_ be + /// impossible, but is handled for safety's sake. + NoDBusUniqueName, + /// The system was unable to give us the time. + TimerUnavailable, +}; + +pub fn init(app: *App) OpenURI { + return .{ + .app = app, + }; +} + +pub fn setDbusConnection(self: *OpenURI, dbus: ?*gio.DBusConnection) void { + self.dbus = dbus; +} + +pub fn deinit(self: *OpenURI) void { + const alloc = self.app.app.allocator(); + + self.mutex.lock(); + defer self.mutex.unlock(); + + if (!self.alive) return; + self.alive = false; + + self.stopCleanupTimer(); + + for (self.entries.entries.items(.value)) |entry| { + self.unsubscribeFromResponse(entry); + destroyEntry(alloc, entry); + } + + self.entries.deinit(alloc); + self.entries = .empty; + self.dbus = null; +} + +/// Send the D-Bus method call to the XDG Desktop portal. The result of the +/// method call will be reported asynchronously. +pub fn start(self: *OpenURI, value: apprt.action.OpenUrl) (Allocator.Error || Errors)!void { + const alloc = self.app.app.allocator(); + const dbus = self.dbus orelse return error.DBusConnectionRequired; + + const token = portal.generateToken(); + const request_path = try portal.getRequestPath(alloc, dbus, token); + defer alloc.free(request_path); + + const request = try RequestData.init(alloc, self, token, value.kind, value.url, request_path); + errdefer { + request.deinit(alloc); + alloc.destroy(request); + } + + self.mutex.lock(); + defer self.mutex.unlock(); + + // Create an entry that is used to track the results of the D-Bus method + // call. + const entry = entry: { + const entry = try alloc.create(Entry); + errdefer alloc.destroy(entry); + entry.* = .{ + .start = std.time.Instant.now() catch return error.TimerUnavailable, + .token = token, + .kind = value.kind, + .uri = try alloc.dupeZ(u8, value.url), + }; + errdefer entry.deinit(alloc); + try self.entries.putNoClobber(alloc, token, entry); + break :entry entry; + }; + + errdefer { + _ = self.entries.swapRemove(token); + destroyEntry(alloc, entry); + } + + self.startCleanupTimer(); + self.subscribeToResponse(entry, dbus, request_path.ptr); + self.sendRequest(entry, dbus, request); +} + +/// Subscribe to the D-Bus signal that will contain the results of our method +/// call to the portal. This must be called with the mutex locked. +fn subscribeToResponse( + self: *OpenURI, + entry: *Entry, + dbus: *gio.DBusConnection, + request_path: [*:0]const u8, +) void { + assert(!self.mutex.tryLock()); + + if (entry.subscription != null) return; + + entry.subscription = dbus.signalSubscribe( + null, + "org.freedesktop.portal.Request", + "Response", + request_path, + null, + .{}, + responseReceived, + self, + null, + ); +} + +/// Unsubscribe to the D-Bus signal that contains the result of the method call. +/// This will prevent a response from being processed multiple times. This must +/// be called when the mutex is locked. +fn unsubscribeFromResponse(self: *OpenURI, entry: *Entry) void { + assert(!self.mutex.tryLock()); + + // Unsubscribe from the response signal + if (entry.subscription) |subscription| { + const dbus = self.dbus orelse { + entry.subscription = null; + log.warn("unable to unsubscribe open uri response without dbus connection", .{}); + return; + }; + dbus.signalUnsubscribe(subscription); + entry.subscription = null; + } +} + +fn destroyEntry(alloc: Allocator, entry: *Entry) void { + entry.deinit(alloc); + alloc.destroy(entry); +} + +fn failRequest(self: *OpenURI, token: usize) ?*Entry { + self.mutex.lock(); + defer self.mutex.unlock(); + + if (!self.alive) return null; + + const entry = (self.entries.fetchSwapRemove(token) orelse return null).value; + self.unsubscribeFromResponse(entry); + return entry; +} + +fn failRequestAndFallback(self: *OpenURI, request: *const RequestData) void { + const alloc = self.app.app.allocator(); + const entry = self.failRequest(request.token) orelse return; + defer destroyEntry(alloc, entry); + + self.app.app.openUrlFallback(request.kind, request.uri); +} + +/// Send the D-Bus method call to the portal. The mutex must be locked when this +/// is called. +fn sendRequest( + self: *OpenURI, + entry: *Entry, + dbus: *gio.DBusConnection, + request: *RequestData, +) void { + assert(!self.mutex.tryLock()); + + const payload = payload: { + const builder_type = glib.VariantType.new("(ssa{sv})"); + defer builder_type.free(); + + // Initialize our builder to build up our parameters + var builder: glib.VariantBuilder = undefined; + builder.init(builder_type); + + // parent window - empty string means we have no window + builder.add("s", ""); + + // URI to open + builder.add("s", entry.uri.ptr); + + // Options + { + const options = glib.VariantType.new("a{sv}"); + defer options.free(); + + builder.open(options); + defer builder.close(); + + { + const option = glib.VariantType.new("{sv}"); + defer option.free(); + + builder.open(option); + defer builder.close(); + + builder.add("s", "handle_token"); + + var token_buf: portal.TokenBuffer = undefined; + const token = portal.formatToken(&token_buf, entry.token); + + const handle_token = glib.Variant.newString(token.ptr); + builder.add("v", handle_token); + } + { + const option = glib.VariantType.new("{sv}"); + defer option.free(); + + builder.open(option); + defer builder.close(); + + builder.add("s", "writable"); + + const writable = glib.Variant.newBoolean(@intFromBool(false)); + builder.add("v", writable); + } + { + const option = glib.VariantType.new("{sv}"); + defer option.free(); + + builder.open(option); + defer builder.close(); + + builder.add("s", "ask"); + + const ask = glib.Variant.newBoolean(@intFromBool(false)); + builder.add("v", ask); + } + } + + break :payload builder.end(); + }; + + // We're expecting an object path back from the method call. + const reply_type = glib.VariantType.new("(o)"); + defer reply_type.free(); + + dbus.call( + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.OpenURI", + "OpenURI", + payload, + reply_type, + .{}, + -1, + null, + requestCallback, + request, + ); +} + +/// Process the result of the original method call. Receiving this result does +/// not indicate that the that the method call succeeded but it may contain an +/// error message that is useful to log for debugging purposes. +fn requestCallback( + source: ?*gobject.Object, + result: *gio.AsyncResult, + ud: ?*anyopaque, +) callconv(.c) void { + const request: *RequestData = @ptrCast(@alignCast(ud orelse return)); + const self = request.open_uri; + const alloc = self.app.app.allocator(); + defer { + request.deinit(alloc); + alloc.destroy(request); + } + + const dbus = gobject.ext.cast(gio.DBusConnection, source orelse { + log.err("Open URI request finished without a D-Bus source object", .{}); + self.failRequestAndFallback(request); + return; + }) orelse { + log.err("Open URI request finished with an unexpected source object", .{}); + self.failRequestAndFallback(request); + return; + }; + + var err_: ?*glib.Error = null; + defer if (err_) |err| err.free(); + + const reply_ = dbus.callFinish(result, &err_); + + if (err_) |err| { + log.err("Open URI request failed={s} ({})", .{ + err.f_message orelse "(unknown)", + err.f_code, + }); + self.failRequestAndFallback(request); + return; + } + + const reply = reply_ orelse { + log.err("D-Bus method call returned a null value!", .{}); + self.failRequestAndFallback(request); + return; + }; + defer reply.unref(); + + const reply_type = glib.VariantType.new("(o)"); + defer reply_type.free(); + + if (reply.isOfType(reply_type) == 0) { + log.warn("Reply from D-Bus method call does not contain an object path!", .{}); + self.failRequestAndFallback(request); + return; + } + + var object_path: [*:0]const u8 = undefined; + reply.get("(&o)", &object_path); + + const token = portal.parseRequestPathToken(std.mem.span(object_path)) orelse { + log.warn("Unable to parse token from the object path {s}", .{object_path}); + self.failRequestAndFallback(request); + return; + }; + + if (token != request.token) { + log.warn("Open URI request returned mismatched token expected={x} actual={x}", .{ + request.token, + token, + }); + self.failRequestAndFallback(request); + return; + } + + self.mutex.lock(); + defer self.mutex.unlock(); + + if (!self.alive) return; + + const entry = self.entries.get(token) orelse return; + if (std.mem.eql(u8, request.request_path, std.mem.span(object_path))) return; + + log.debug("updating open uri request path old={s} new={s}", .{ + request.request_path, + object_path, + }); + self.unsubscribeFromResponse(entry); + self.subscribeToResponse(entry, dbus, object_path); +} + +/// Handle the response signal from the portal. This should contain the actual +/// results of the method call (success or failure). +fn responseReceived( + _: *gio.DBusConnection, + _: ?[*:0]const u8, + object_path: [*:0]const u8, + _: [*:0]const u8, + _: [*:0]const u8, + params: *glib.Variant, + ud: ?*anyopaque, +) callconv(.c) void { + const self: *OpenURI = @ptrCast(@alignCast(ud orelse { + log.err("OpenURI response received with null userdata", .{}); + return; + })); + + const alloc = self.app.app.allocator(); + + const token = portal.parseRequestPathToken(std.mem.span(object_path)) orelse { + log.warn("invalid object path: {s}", .{std.mem.span(object_path)}); + return; + }; + + self.mutex.lock(); + defer self.mutex.unlock(); + + if (!self.alive) return; + + const entry = (self.entries.fetchSwapRemove(token) orelse { + log.warn("no entry for token {x}", .{token}); + return; + }).value; + + defer destroyEntry(alloc, entry); + + self.unsubscribeFromResponse(entry); + + var response: u32 = 0; + var results: ?*glib.Variant = null; + defer if (results) |variant| variant.unref(); + params.get("(u@a{sv})", &response, &results); + + switch (response) { + 0 => { + log.debug("open uri successful", .{}); + }, + 1 => { + log.debug("open uri request was cancelled by the user", .{}); + }, + 2 => { + log.warn("open uri request ended unexpectedly", .{}); + self.app.app.openUrlFallback(entry.kind, entry.uri); + }, + else => { + log.err("unrecognized response code={}", .{response}); + self.app.app.openUrlFallback(entry.kind, entry.uri); + }, + } +} + +/// Wait this number of seconds and then clean up any orphaned entries. +const cleanup_timeout = 30; + +/// If there is an active cleanup timer, cancel it. This must be called with the +/// mutex locked +fn stopCleanupTimer(self: *OpenURI) void { + assert(!self.mutex.tryLock()); + + if (self.cleanup_timer) |timer| { + if (glib.Source.remove(timer) == 0) { + log.warn("unable to remove cleanup timer source={d}", .{timer}); + } + self.cleanup_timer = null; + } +} + +/// Start a timer to clean up any entries that have not received a timely +/// response. If there is already a timer it will be stopped and replaced with a +/// new one. This must be called with the mutex locked. +fn startCleanupTimer(self: *OpenURI) void { + assert(!self.mutex.tryLock()); + + self.stopCleanupTimer(); + self.cleanup_timer = glib.timeoutAddSeconds(cleanup_timeout + 1, cleanup, self); +} + +/// The cleanup timer is used to free up any entries that may have failed +/// to get a response in a timely manner. +fn cleanup(ud: ?*anyopaque) callconv(.c) c_int { + const self: *OpenURI = @ptrCast(@alignCast(ud orelse { + log.warn("cleanup called with null userdata", .{}); + return @intFromBool(glib.SOURCE_REMOVE); + })); + + const alloc = self.app.app.allocator(); + + self.mutex.lock(); + defer self.mutex.unlock(); + + self.cleanup_timer = null; + if (!self.alive) return @intFromBool(glib.SOURCE_REMOVE); + + const now = std.time.Instant.now() catch { + // `now()` should never fail, but if it does, don't crash, just return. + // This might cause a small memory leak in rare circumstances but it + // should get cleaned up the next time a URL is clicked. + return @intFromBool(glib.SOURCE_REMOVE); + }; + + loop: while (true) { + for (self.entries.entries.items(.value)) |entry| { + if (now.since(entry.start) > cleanup_timeout * std.time.ns_per_s) { + log.warn("open uri request timed out token={x}", .{entry.token}); + self.unsubscribeFromResponse(entry); + _ = self.entries.swapRemove(entry.token); + self.app.app.openUrlFallback(entry.kind, entry.uri); + destroyEntry(alloc, entry); + continue :loop; + } + } + break :loop; + } + + return @intFromBool(glib.SOURCE_REMOVE); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/post_fork.zig b/vendor/libghostty-vt/src/apprt/gtk/post_fork.zig new file mode 100644 index 00000000..ff021950 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/post_fork.zig @@ -0,0 +1,121 @@ +const std = @import("std"); + +const gio = @import("gio"); +const glib = @import("glib"); + +const log = std.log.scoped(.gtk_post_fork); + +const configpkg = @import("../../config.zig"); +const internal_os = @import("../../os/main.zig"); +const Command = @import("../../Command.zig"); +const cgroup = @import("./cgroup.zig"); + +const Application = @import("class/application.zig").Application; + +pub const PostForkInfo = struct { + gtk_single_instance: configpkg.Config.GtkSingleInstance, + linux_cgroup: configpkg.Config.LinuxCgroup, + linux_cgroup_hard_fail: bool, + linux_cgroup_memory_limit: ?u64, + linux_cgroup_processes_limit: ?u64, + + pub fn init(cfg: *const configpkg.Config) PostForkInfo { + return .{ + .gtk_single_instance = cfg.@"gtk-single-instance", + .linux_cgroup = cfg.@"linux-cgroup", + .linux_cgroup_hard_fail = cfg.@"linux-cgroup-hard-fail", + .linux_cgroup_memory_limit = cfg.@"linux-cgroup-memory-limit", + .linux_cgroup_processes_limit = cfg.@"linux-cgroup-processes-limit", + }; + } +}; + +/// If we are configured to do so, tell `systemd` to move the new child PID into +/// a transient `systemd` scope with the configured resource limits. +/// +/// If we are configured to hard fail, log an error message and return an error +/// code if we don't detect the move in time. +pub fn postFork(cmd: *Command) Command.PostForkError!void { + switch (cmd.rt_post_fork_info.linux_cgroup) { + .always => {}, + .never => return, + .@"single-instance" => switch (cmd.rt_post_fork_info.gtk_single_instance) { + .true => {}, + .false => return, + .detect => { + log.err("gtk-single-instance is set to detect which should be impossible!", .{}); + return error.PostForkError; + }, + }, + } + + const pid: u32 = @intCast(cmd.pid orelse { + log.err("PID of child not known!", .{}); + return error.PostForkError; + }); + + var expected_cgroup_buf: [256]u8 = undefined; + const expected_cgroup = cgroup.fmtScope(&expected_cgroup_buf, pid); + + log.debug("beginning transition to transient systemd scope {s}", .{expected_cgroup}); + + const app = Application.default(); + + const dbus = app.as(gio.Application).getDbusConnection() orelse { + if (cmd.rt_post_fork_info.linux_cgroup_hard_fail) { + log.err("dbus connection required for cgroup isolation, exiting", .{}); + return error.PostForkError; + } + return; + }; + + cgroup.createScope( + dbus, + pid, + .{ + .memory_high = cmd.rt_post_fork_info.linux_cgroup_memory_limit, + .tasks_max = cmd.rt_post_fork_info.linux_cgroup_processes_limit, + }, + ) catch |err| { + if (cmd.rt_post_fork_info.linux_cgroup_hard_fail) { + log.err("unable to create transient systemd scope {s}: {t}", .{ expected_cgroup, err }); + return error.PostForkError; + } + log.warn("unable to create transient systemd scope {s}: {t}", .{ expected_cgroup, err }); + return; + }; + + const start = std.time.Instant.now() catch unreachable; + + loop: while (true) { + const now = std.time.Instant.now() catch unreachable; + + if (now.since(start) > 250 * std.time.ns_per_ms) { + if (cmd.rt_pre_exec_info.linux_cgroup_hard_fail) { + log.err("transition to new transient systemd scope {s} took too long", .{expected_cgroup}); + return error.PostForkError; + } + log.warn("transition to transient systemd scope {s} took too long", .{expected_cgroup}); + break :loop; + } + + not_found: { + var current_cgroup_buf: [4096]u8 = undefined; + + const current_cgroup_raw = internal_os.cgroup.current( + ¤t_cgroup_buf, + @intCast(pid), + ) orelse break :not_found; + + const index = std.mem.lastIndexOfScalar(u8, current_cgroup_raw, '/') orelse break :not_found; + const current_cgroup = current_cgroup_raw[index + 1 ..]; + + if (std.mem.eql(u8, current_cgroup, expected_cgroup)) { + log.debug("transition to transient systemd scope {s} complete", .{expected_cgroup}); + break :loop; + } + } + + std.Thread.sleep(25 * std.time.ns_per_ms); + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/pre_exec.zig b/vendor/libghostty-vt/src/apprt/gtk/pre_exec.zig new file mode 100644 index 00000000..6f6a9ed5 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/pre_exec.zig @@ -0,0 +1,81 @@ +const std = @import("std"); + +const log = std.log.scoped(.gtk_pre_exec); + +const configpkg = @import("../../config.zig"); + +const internal_os = @import("../../os/main.zig"); +const Command = @import("../../Command.zig"); +const cgroup = @import("./cgroup.zig"); + +pub const PreExecInfo = struct { + gtk_single_instance: configpkg.Config.GtkSingleInstance, + linux_cgroup: configpkg.Config.LinuxCgroup, + linux_cgroup_hard_fail: bool, + + pub fn init(cfg: *const configpkg.Config) PreExecInfo { + return .{ + .gtk_single_instance = cfg.@"gtk-single-instance", + .linux_cgroup = cfg.@"linux-cgroup", + .linux_cgroup_hard_fail = cfg.@"linux-cgroup-hard-fail", + }; + } +}; + +/// If we are expecting to be moved to a transient systemd scope, wait to see if +/// that happens by checking for the correct name of the current cgroup. Wait at +/// most 250ms so that we don't overly delay the soft-fail scenario. +/// +/// If we are configured to hard fail, log an error message and return an error +/// code if we don't detect the move in time. +pub fn preExec(cmd: *Command) ?u8 { + switch (cmd.rt_pre_exec_info.linux_cgroup) { + .always => {}, + .never => return null, + .@"single-instance" => switch (cmd.rt_pre_exec_info.gtk_single_instance) { + .true => {}, + .false => return null, + .detect => { + log.err("gtk-single-instance is set to detect", .{}); + return 127; + }, + }, + } + + const pid: u32 = @intCast(std.os.linux.getpid()); + + var expected_cgroup_buf: [256]u8 = undefined; + const expected_cgroup = cgroup.fmtScope(&expected_cgroup_buf, pid); + + const start = std.time.Instant.now() catch unreachable; + + while (true) { + const now = std.time.Instant.now() catch unreachable; + + if (now.since(start) > 250 * std.time.ns_per_ms) { + if (cmd.rt_pre_exec_info.linux_cgroup_hard_fail) { + log.err("transition to new transient systemd scope took too long", .{}); + return 127; + } + break; + } + + not_found: { + var current_cgroup_buf: [4096]u8 = undefined; + + const current_cgroup_raw = internal_os.cgroup.current( + ¤t_cgroup_buf, + @intCast(pid), + ) orelse break :not_found; + + const index = std.mem.lastIndexOfScalar(u8, current_cgroup_raw, '/') orelse break :not_found; + const current_cgroup = current_cgroup_raw[index + 1 ..]; + + if (std.mem.eql(u8, current_cgroup, expected_cgroup)) return null; + } + + std.Thread.sleep(25 * std.time.ns_per_ms); + } + + return null; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.0/clipboard-confirmation-dialog.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.0/clipboard-confirmation-dialog.blp new file mode 100644 index 00000000..5f29981d --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.0/clipboard-confirmation-dialog.blp @@ -0,0 +1,86 @@ +using Gtk 4.0; +// This is unused but if we remove it we get a blueprint-compiler error. +using Adw 1; + +template $GhosttyClipboardConfirmationDialog: $GhosttyDialog { + styles [ + "clipboard-confirmation-dialog", + ] + + notify::blur => $notify_blur(); + notify::request => $notify_request(); + heading: _("Authorize Clipboard Access"); + // Not localized because this is a placeholder users never see. + body: "If you see this text, there is a bug in Ghostty. Please report it."; + + responses [ + cancel: _("Deny") suggested, + ok: _("Allow") destructive, + ] + + default-response: "cancel"; + close-response: "cancel"; + + extra-child: ListBox { + selection-mode: none; + + Overlay { + styles [ + "osd", + "clipboard-overlay", + ] + + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 200; + + TextView text_view { + styles [ + "clipboard-contents", + ] + + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + buffer: bind template.clipboard-contents; + } + } + + [overlay] + Button reveal_button { + clicked => $reveal_clicked(); + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + clicked => $hide_clicked(); + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque", + ] + + Image { + icon-name: "view-conceal-symbolic"; + } + } + } + }; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/close-confirmation-dialog.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/close-confirmation-dialog.blp new file mode 100644 index 00000000..f58ef523 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/close-confirmation-dialog.blp @@ -0,0 +1,12 @@ +using Gtk 4.0; +// This is unused but if we remove it we get a blueprint-compiler error. +using Adw 1; + +template $GhosttyCloseConfirmationDialog: $GhosttyDialog { + responses [ + cancel: _("Cancel"), + close: _("Close") destructive, + ] + + close-response: "cancel"; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/config-errors-dialog.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/config-errors-dialog.blp new file mode 100644 index 00000000..845909eb --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/config-errors-dialog.blp @@ -0,0 +1,28 @@ +using Gtk 4.0; +// This is unused but if we remove it we get a blueprint-compiler error. +using Adw 1; + +template $GhosttyConfigErrorsDialog: $GhosttyDialog { + heading: _("Configuration Errors"); + body: _("One or more configuration errors were found. Please review the errors below, and either reload your configuration or ignore these errors."); + + responses [ + ignore: _("Ignore"), + reload: _("Reload Configuration") suggested, + ] + + extra-child: ScrolledWindow { + min-content-width: 500; + min-content-height: 100; + + TextView { + editable: false; + cursor-visible: false; + top-margin: 8; + bottom-margin: 8; + left-margin: 8; + right-margin: 8; + buffer: bind (template.config as <$GhosttyConfig>).diagnostics-buffer; + } + }; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/debug-warning.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/debug-warning.blp new file mode 100644 index 00000000..34411ce9 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/debug-warning.blp @@ -0,0 +1,11 @@ +using Gtk 4.0; + +template $GhosttyDebugWarning: Gtk.Box { + orientation: vertical; + + Gtk.Label { + label: _("โš ๏ธ You're running a debug build of Ghostty! Performance will be degraded."); + margin-top: 10; + margin-bottom: 10; + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/key-state-overlay.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/key-state-overlay.blp new file mode 100644 index 00000000..c8654bfb --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/key-state-overlay.blp @@ -0,0 +1,58 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttyKeyStateOverlay: Adw.Bin { + visible: bind $has_state(template.has-tables, template.has-sequence) as ; + valign-target: end; + halign: center; + valign: bind template.valign-target; + + GestureDrag { + button: 1; + propagation-phase: capture; + drag-end => $on_drag_end(); + } + + Adw.Bin { + Box container { + styles [ + "background", + "key-state-overlay", + ] + + orientation: horizontal; + spacing: 6; + + Image { + icon-name: "input-keyboard-symbolic"; + pixel-size: 16; + } + + Label tables_label { + visible: bind template.has-tables; + label: bind $tables_text(template.tables) as ; + xalign: 0.0; + } + + Label chevron_label { + visible: bind $show_chevron(template.has-tables, template.has-sequence) as ; + label: "โ€บ"; + + styles [ + "dim-label", + ] + } + + Label sequence_label { + visible: bind template.has-sequence; + label: bind $sequence_text(template.sequence) as ; + xalign: 0.0; + } + + Spinner pending_spinner { + visible: bind template.has-sequence; + spinning: bind template.has-sequence; + } + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/resize-overlay.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/resize-overlay.blp new file mode 100644 index 00000000..a05986b4 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/resize-overlay.blp @@ -0,0 +1,33 @@ +using Gtk 4.0; +using Adw 1; +// We can't inherit directly from Label because its an opaque +// type in zig-gobject. +template $GhosttyResizeOverlay: Adw.Bin { + visible: false; + can-focus: false; + can-target: false; + focusable: false; + focus-on-click: false; + duration: 750; + first-delay: 250; + overlay-halign: center; + overlay-valign: center; + // See surface.blp for why we need to wrap this. + Adw.Bin { + Label label { + styles [ + "background", + "resize-overlay", + ] + + can-focus: false; + can-target: false; + focusable: false; + focus-on-click: false; + selectable: false; + justify: center; + halign: bind template.overlay-halign; + valign: bind template.overlay-valign; + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/search-overlay.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/search-overlay.blp new file mode 100644 index 00000000..6523d414 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/search-overlay.blp @@ -0,0 +1,94 @@ +using Gtk 4.0; +using Gdk 4.0; +using Adw 1; + +template $GhosttySearchOverlay: Adw.Bin { + visible: bind template.active; + halign-target: end; + valign-target: start; + halign: bind template.halign-target; + valign: bind template.valign-target; + + GestureDrag { + button: 1; + propagation-phase: capture; + drag-end => $on_drag_end(); + } + + Adw.Bin { + Box container { + styles [ + "background", + "search-overlay", + ] + + orientation: horizontal; + spacing: 6; + + SearchEntry search_entry { + placeholder-text: _("Findโ€ฆ"); + width-chars: 20; + hexpand: true; + stop-search => $stop_search(); + search-changed => $search_changed(); + next-match => $next_match(); + previous-match => $previous_match(); + + EventControllerKey { + // We need this so we capture before the SearchEntry. + propagation-phase: capture; + key-pressed => $search_entry_key_pressed(); + } + } + + Label { + styles [ + "dim-label", + ] + + label: bind $match_label_closure(template.has-search-selected, template.search-selected, template.has-search-total, template.search-total) as ; + width-chars: 6; + xalign: 1.0; + } + + Box button_box { + orientation: horizontal; + spacing: 1; + + styles [ + "linked", + ] + + Button prev_button { + icon-name: "go-up-symbolic"; + tooltip-text: _("Previous Match"); + clicked => $next_match(); + + cursor: Gdk.Cursor { + name: "pointer"; + }; + } + + Button next_button { + icon-name: "go-down-symbolic"; + tooltip-text: _("Next Match"); + clicked => $previous_match(); + + cursor: Gdk.Cursor { + name: "pointer"; + }; + } + } + + Button close_button { + icon-name: "window-close-symbolic"; + tooltip-text: _("Close"); + clicked => $stop_search(); + + cursor: Gdk.Cursor { + name: "pointer"; + }; + } + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/surface.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/surface.blp new file mode 100644 index 00000000..794ea180 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.2/surface.blp @@ -0,0 +1,371 @@ +using Gtk 4.0; +using Adw 1; + +Adw.StatusPage error_page { + icon-name: "computer-fail-symbolic"; + title: _("Oh, no."); + description: _("Unable to acquire an OpenGL context for rendering."); + + child: LinkButton { + label: "https://ghostty.org/docs/help/gtk-opengl-context"; + uri: "https://ghostty.org/docs/help/gtk-opengl-context"; + }; +} + +Overlay terminal_page { + focusable: false; + focus-on-click: false; + + child: Box { + hexpand: true; + vexpand: true; + + GLArea gl_area { + realize => $gl_realize(); + unrealize => $gl_unrealize(); + render => $gl_render(); + resize => $gl_resize(); + hexpand: true; + vexpand: true; + focusable: true; + focus-on-click: true; + has-stencil-buffer: false; + has-depth-buffer: false; + allowed-apis: gl; + } + + PopoverMenu context_menu { + closed => $context_menu_closed(); + menu-model: context_menu_model; + flags: nested; + halign: start; + has-arrow: false; + } + + EventControllerFocus { + enter => $focus_enter(); + leave => $focus_leave(); + } + + EventControllerKey { + key-pressed => $key_pressed(); + key-released => $key_released(); + } + + EventControllerScroll { + scroll => $scroll_vertical(); + scroll-begin => $scroll_vertical_begin(); + scroll-end => $scroll_vertical_end(); + flags: vertical; + } + + EventControllerScroll { + scroll => $scroll_horizontal(); + flags: horizontal; + } + + EventControllerMotion { + motion => $mouse_motion(); + leave => $mouse_leave(); + } + + GestureClick { + pressed => $mouse_down(); + released => $mouse_up(); + button: 0; + } + }; + + [overlay] + Revealer { + reveal-child: bind template.readonly; + transition-type: crossfade; + transition-duration: 500; + // Revealers take up the full size, we need this to not capture events. + can-focus: false; + can-target: false; + focusable: false; + + Box readonly_overlay { + styles [ + "readonly_overlay", + ] + + // TODO: the tooltip doesn't actually work, but keep it here for now so + // that we can get the tooltip text translated. + has-tooltip: true; + tooltip-text: _("This terminal is in read-only mode. You can still view, select, and scroll through the content, but no input events will be sent to the running application."); + halign: end; + valign: start; + spacing: 6; + + Image { + icon-name: "changes-prevent-symbolic"; + } + + Label { + label: _("Read-only"); + } + } + } + + [overlay] + ProgressBar progress_bar_overlay { + styles [ + "osd", + ] + + visible: false; + halign: fill; + valign: start; + } + + [overlay] + // The "border" bell feature is implemented here as an overlay rather than + // just adding a border to the GLArea or other widget for two reasons. + // First, adding a border to an existing widget causes a resize of the + // widget which undesirable side effects. Second, we can make it reactive + // here in the blueprint with relatively little code. + Revealer { + reveal-child: bind $should_border_be_shown(template.config, template.bell-ringing) as ; + transition-type: crossfade; + transition-duration: 500; + // Revealers take up the full size, we need this to not capture events. + can-focus: false; + can-target: false; + focusable: false; + + Box bell_overlay { + styles [ + "bell-overlay", + ] + + halign: fill; + valign: fill; + } + } + + [overlay] + $GhosttySurfaceChildExited child_exited_overlay { + visible: bind template.child-exited; + close-request => $child_exited_close(); + } + + [overlay] + $GhosttyResizeOverlay resize_overlay {} + + [overlay] + Label url_left { + styles [ + "background", + "url-overlay", + ] + + visible: false; + halign: start; + valign: end; + label: bind template.mouse-hover-url; + + EventControllerMotion url_ec_motion { + enter => $url_mouse_enter(); + leave => $url_mouse_leave(); + } + } + + [overlay] + Label url_right { + styles [ + "background", + "url-overlay", + ] + + visible: false; + halign: end; + valign: end; + label: bind template.mouse-hover-url; + } + + [overlay] + $GhosttySearchOverlay search_overlay { + stop-search => $search_stop(); + search-changed => $search_changed(); + next-match => $search_next_match(); + previous-match => $search_previous_match(); + } + + [overlay] + $GhosttyKeyStateOverlay key_state_overlay { + tables: bind template.key-table; + sequence: bind template.key-sequence; + } + + [overlay] + // Apply unfocused-split-fill and unfocused-split-opacity to current surface + // this is only applied when a tab has more than one surface + Revealer { + reveal-child: bind $should_unfocused_split_be_shown(search_overlay.active, template.focused, template.is-split) as ; + transition-duration: 0; + // This is all necessary so that the Revealer itself doesn't override + // any input events from the other overlays. Namely, if you don't have + // these then the search overlay won't get mouse events. + can-focus: false; + can-target: false; + focusable: false; + + DrawingArea { + styles [ + "unfocused-split", + ] + } + } + + DropTarget drop_target { + drop => $drop(); + actions: copy | move; + } +} + +template $GhosttySurface: Adw.Bin { + styles [ + "surface", + ] + + notify::config => $notify_config(); + notify::error => $notify_error(); + notify::mouse-hover-url => $notify_mouse_hover_url(); + notify::mouse-hidden => $notify_mouse_hidden(); + notify::mouse-shape => $notify_mouse_shape(); + notify::vadjustment => $notify_vadjustment(); + // Some history: we used to use a Stack here and swap between the + // terminal and error pages as needed. But a Stack doesn't play nice + // with our SplitTree and Gtk.Paned usage[^1]. Replacing this with + // a manual programmatic child swap fixed this. So if you ever change + // this, be sure to test many splits! + // + // [^1]: https://github.com/ghostty-org/ghostty/issues/8533 + child: terminal_page; +} + +IMMulticontext im_context { + input-purpose: terminal; + preedit-start => $im_preedit_start(); + preedit-changed => $im_preedit_changed(); + preedit-end => $im_preedit_end(); + commit => $im_commit(); +} + +menu context_menu_model { + section { + item { + label: _("Copy"); + action: "win.copy"; + } + + item { + label: _("Paste"); + action: "win.paste"; + } + + item { + label: _("Notify on Next Command Finish"); + action: "surface.notify-on-next-command-finish"; + } + } + + section { + item { + label: _("Clear"); + action: "win.clear"; + } + + item { + label: _("Reset"); + action: "win.reset"; + } + } + + section { + submenu { + label: _("Split"); + + item { + label: _("Change Titleโ€ฆ"); + action: "surface.prompt-title"; + } + + item { + label: _("Split Up"); + action: "split-tree.new-split"; + target: "up"; + } + + item { + label: _("Split Down"); + action: "split-tree.new-split"; + target: "down"; + } + + item { + label: _("Split Left"); + action: "split-tree.new-split"; + target: "left"; + } + + item { + label: _("Split Right"); + action: "split-tree.new-split"; + target: "right"; + } + } + + submenu { + label: _("Tab"); + + item { + label: _("Change Tab Titleโ€ฆ"); + action: "tab.prompt-tab-title"; + } + + item { + label: _("New Tab"); + action: "win.new-tab"; + } + + item { + label: _("Close Tab"); + action: "tab.close"; + target: "this"; + } + } + + submenu { + label: _("Window"); + + item { + label: _("New Window"); + action: "win.new-window"; + } + + item { + label: _("Close Window"); + action: "win.close"; + } + } + } + + section { + submenu { + label: _("Config"); + + item { + label: _("Open Configuration"); + action: "app.open-config"; + } + + item { + label: _("Reload Configuration"); + action: "app.reload-config"; + } + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.3/debug-warning.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.3/debug-warning.blp new file mode 100644 index 00000000..ad7246a8 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.3/debug-warning.blp @@ -0,0 +1,9 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttyDebugWarning: Adw.Bin { + Adw.Banner { + title: _("โš ๏ธ You're running a debug build of Ghostty! Performance will be degraded."); + revealed: true; + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.3/surface-child-exited.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.3/surface-child-exited.blp new file mode 100644 index 00000000..6745f0ca --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.3/surface-child-exited.blp @@ -0,0 +1,22 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttySurfaceChildExited: Adw.Bin { + styles [ + "child-exited", + ] + + notify::data => $notify_data(); + + Adw.Bin { + Adw.Banner banner { + button-clicked => $clicked(); + revealed: true; + // Not localized on purpose because it should never be seen. + title: "This is a bug in Ghostty. Please report it."; + button-label: _("Close"); + halign: fill; + valign: end; + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.4/clipboard-confirmation-dialog.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.4/clipboard-confirmation-dialog.blp new file mode 100644 index 00000000..144e3a37 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.4/clipboard-confirmation-dialog.blp @@ -0,0 +1,96 @@ +using Gtk 4.0; +// This is unused but if we remove it we get a blueprint-compiler error. +using Adw 1; + +template $GhosttyClipboardConfirmationDialog: $GhosttyDialog { + styles [ + "clipboard-confirmation-dialog", + ] + + notify::blur => $notify_blur(); + notify::request => $notify_request(); + heading: _("Authorize Clipboard Access"); + // Not localized because this is a placeholder users never see. + body: "If you see this text, there is a bug in Ghostty. Please report it."; + + responses [ + cancel: _("Deny") suggested, + ok: _("Allow") destructive, + ] + + default-response: "cancel"; + close-response: "cancel"; + + extra-child: ListBox { + selection-mode: none; + + Overlay { + styles [ + "osd", + "clipboard-overlay", + ] + + ScrolledWindow text_view_scroll { + width-request: 500; + height-request: 200; + + TextView text_view { + styles [ + "clipboard-contents", + ] + + cursor-visible: false; + editable: false; + monospace: true; + top-margin: 8; + left-margin: 8; + bottom-margin: 8; + right-margin: 8; + buffer: bind template.clipboard-contents; + } + } + + [overlay] + Button reveal_button { + clicked => $reveal_clicked(); + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + Image { + icon-name: "view-reveal-symbolic"; + } + } + + [overlay] + Button hide_button { + clicked => $hide_clicked(); + visible: false; + halign: end; + valign: start; + margin-end: 12; + margin-top: 12; + + styles [ + "opaque", + ] + + Image { + icon-name: "view-conceal-symbolic"; + } + } + } + + Adw.SwitchRow remember_choice { + styles [ + "card", + ] + + visible: bind template.can-remember; + title: _("Remember choice for this split"); + subtitle: _("Reload configuration to show this prompt again"); + } + }; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/command-palette.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/command-palette.blp new file mode 100644 index 00000000..473fb1f0 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/command-palette.blp @@ -0,0 +1,110 @@ +using Gtk 4.0; +using Gio 2.0; +using Adw 1; + +Adw.Dialog dialog { + content-width: 700; + closed => $closed(); + + Adw.ToolbarView { + top-bar-style: flat; + + [top] + Adw.HeaderBar { + [title] + Gtk.SearchEntry search { + hexpand: true; + placeholder-text: _("Execute a commandโ€ฆ"); + stop-search => $search_stopped(); + activate => $search_activated(); + + styles [ + "command-palette-search", + ] + } + } + + Gtk.ScrolledWindow { + min-content-height: 300; + + Gtk.ListView view { + show-separators: true; + single-click-activate: true; + activate => $row_activated(); + + model: Gtk.SingleSelection model { + model: Gtk.FilterListModel { + incremental: true; + + filter: Gtk.AnyFilter { + Gtk.StringFilter { + expression: expr item as <$GhosttyCommand>.title; + search: bind search.text; + } + + Gtk.StringFilter { + expression: expr item as <$GhosttyCommand>.action-key; + search: bind search.text; + } + }; + + model: Gio.ListStore source { + item-type: typeof<$GhosttyCommand>; + }; + }; + }; + + styles [ + "rich-list", + ] + + factory: Gtk.BuilderListItemFactory { + template Gtk.ListItem { + child: Gtk.Box { + orientation: horizontal; + spacing: 10; + tooltip-text: bind template.item as <$GhosttyCommand>.description; + + Gtk.Box { + orientation: vertical; + hexpand: true; + + Gtk.Label { + ellipsize: end; + halign: start; + wrap: false; + single-line-mode: true; + + styles [ + "title", + ] + + label: bind template.item as <$GhosttyCommand>.title; + } + + Gtk.Label { + ellipsize: end; + halign: start; + wrap: false; + single-line-mode: true; + + styles [ + "subtitle", + "monospace", + ] + + label: bind template.item as <$GhosttyCommand>.action-key; + } + } + + Gtk.ShortcutLabel { + accelerator: bind template.item as <$GhosttyCommand>.action; + valign: center; + } + }; + } + }; + } + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/imgui-widget.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/imgui-widget.blp new file mode 100644 index 00000000..d5b973a7 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/imgui-widget.blp @@ -0,0 +1,51 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttyImguiWidget: Adw.Bin { + styles [ + "imgui", + ] + + Adw.Bin { + Gtk.GLArea gl_area { + auto-render: true; + // needs to be focusable so that we can receive events + focusable: true; + focus-on-click: true; + allowed-apis: gl; + realize => $realize(); + unrealize => $unrealize(); + resize => $resize(); + render => $render(); + + EventControllerFocus { + enter => $focus_enter(); + leave => $focus_leave(); + } + + EventControllerKey { + key-pressed => $key_pressed(); + key-released => $key_released(); + } + + GestureClick { + pressed => $mouse_pressed(); + released => $mouse_released(); + button: 0; + } + + EventControllerMotion { + motion => $mouse_motion(); + } + + EventControllerScroll { + scroll => $scroll(); + flags: both_axes; + } + } + } +} + +IMMulticontext im_context { + commit => $im_commit(); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-widget.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-widget.blp new file mode 100644 index 00000000..ac15ab4e --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-widget.blp @@ -0,0 +1,10 @@ +using Gtk 4.0; + +template $GhosttyInspectorWidget: $GhosttyImguiWidget { + styles [ + "inspector", + ] + + hexpand: true; + vexpand: true; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-window.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-window.blp new file mode 100644 index 00000000..a7625bc2 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/inspector-window.blp @@ -0,0 +1,38 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttyInspectorWindow: Adw.ApplicationWindow { + title: _("Ghostty: Terminal Inspector"); + icon-name: "com.mitchellh.ghostty"; + default-width: 1000; + default-height: 600; + + styles [ + "inspector", + ] + + content: Adw.ToolbarView { + [top] + Adw.HeaderBar { + title-widget: Adw.WindowTitle { + title: bind template.title; + }; + } + + Gtk.Box { + orientation: vertical; + spacing: 0; + hexpand: true; + vexpand: true; + + $GhosttyDebugWarning { + visible: bind template.debug; + } + + $GhosttyInspectorWidget inspector_widget { + notify::surface => $notify_inspector_surface(); + surface: bind template.surface; + } + } + }; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree-split.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree-split.blp new file mode 100644 index 00000000..182919f4 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree-split.blp @@ -0,0 +1,20 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttySplitTreeSplit: Adw.Bin { + styles [ + "split", + ] + + // The double-nesting is required due to a GTK bug where you can't + // bind the first child of a builder layout. If you do, you get a double + // dispose. Easiest way to see that is simply remove this and see the + // GTK critical errors (and sometimes crashes). + Adw.Bin { + Paned paned { + notify::max-position => $notify_max_position(); + notify::min-position => $notify_min_position(); + notify::position => $notify_position(); + } + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree.blp new file mode 100644 index 00000000..e8c53b60 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/split-tree.blp @@ -0,0 +1,25 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttySplitTree: Box { + notify::tree => $notify_tree(); + orientation: vertical; + + Adw.Bin tree_bin { + visible: bind template.has-surfaces; + hexpand: true; + vexpand: true; + } + + // This could be a lot more visually pleasing but in practice this doesn't + // ever happen at the time of writing this comment. A surface-less split + // tree always closes its parent. + Label { + visible: bind template.has-surfaces inverted; + // Purposely not localized currently because this shouldn't really + // ever appear. When we have a situation it does appear, we may want + // to change the styling and text so I don't want to burden localizers + // to handle this yet. + label: "No surfaces."; + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/surface-scrolled-window.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/surface-scrolled-window.blp new file mode 100644 index 00000000..75582a89 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/surface-scrolled-window.blp @@ -0,0 +1,11 @@ +using Gtk 4.0; +using Adw 1; + +template $GhostttySurfaceScrolledWindow: Adw.Bin { + notify::surface => $notify_surface(); + + Gtk.ScrolledWindow scrolled_window { + hscrollbar-policy: never; + vscrollbar-policy: bind $scrollbar_policy(template.config) as ; + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/tab.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/tab.blp new file mode 100644 index 00000000..55f2e7ef --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/tab.blp @@ -0,0 +1,18 @@ +using Gtk 4.0; + +template $GhosttyTab: Box { + styles [ + "tab", + ] + + orientation: vertical; + hexpand: true; + vexpand: true; + title: bind $computed_title(template.config, split_tree.active-surface as <$GhosttySurface>.title, split_tree.active-surface as <$GhosttySurface>.title-override, template.title-override, split_tree.is-zoomed, split_tree.active-surface as <$GhosttySurface>.bell-ringing) as ; + tooltip: bind split_tree.active-surface as <$GhosttySurface>.pwd; + + $GhosttySplitTree split_tree { + notify::active-surface => $notify_active_surface(); + notify::tree => $notify_tree(); + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/title-dialog.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/title-dialog.blp new file mode 100644 index 00000000..737a92b5 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/title-dialog.blp @@ -0,0 +1,18 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttyTitleDialog: Adw.AlertDialog { + body: _("Leave blank to restore the default title."); + + responses [ + cancel: _("Cancel"), + ok: _("OK") suggested, + ] + + default-response: "ok"; + focus-widget: entry; + + extra-child: Entry entry { + activates-default: true; + }; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/window.blp b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/window.blp new file mode 100644 index 00000000..b66a9309 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/ui/1.5/window.blp @@ -0,0 +1,323 @@ +using Gtk 4.0; +using Adw 1; + +template $GhosttyWindow: Adw.ApplicationWindow { + styles [ + "window", + ] + + close-request => $close_request(); + realize => $realize(); + notify::config => $notify_config(); + notify::fullscreened => $notify_fullscreened(); + notify::is-active => $notify_is_active(); + notify::maximized => $notify_maximized(); + notify::quick-terminal => $notify_quick_terminal(); + notify::scale-factor => $notify_scale_factor(); + default-width: 800; + default-height: 600; + // GTK4 grabs F10 input by default to focus the menubar icon. We want + // to disable this so that terminal programs can capture F10 (such as htop) + handle-menubar-accel: false; + + content: Adw.TabOverview tab_overview { + create-tab => $overview_create_tab(); + notify::open => $overview_notify_open(); + view: tab_view; + enable-new-tab: true; + // Disable the title buttons (close, maximize, minimize, ...) + // *inside* the tab overview if CSDs are disabled. + // We do spare the search button, though. + show-start-title-buttons: bind template.decorated; + show-end-title-buttons: bind template.decorated; + + Adw.ToolbarView toolbar { + top-bar-style: bind template.toolbar-style; + bottom-bar-style: bind template.toolbar-style; + + [top] + Adw.HeaderBar { + visible: bind template.headerbar-visible; + + title-widget: Adw.WindowTitle { + title: bind template.title; + // Blueprint auto-formatter won't let me split this into multiple + // lines. Let me explain myself. All parameters to a closure are used + // as notifications to recompute the value of the closure. All + // elements of a property chain are also subscribed to for changes. + // This one long, ugly line saves us from manually building up this + // massive notify chain in code. + subtitle: bind $computed_subtitle(template.config, tab_view.selected-page.child as <$GhosttyTab>.active-surface as <$GhosttySurface>.pwd) as ; + }; + + [start] + Adw.SplitButton { + clicked => $new_tab(); + icon-name: "tab-new-symbolic"; + tooltip-text: _("New Tab"); + dropdown-tooltip: _("New Split"); + menu-model: split_menu; + can-focus: false; + focus-on-click: false; + } + + [end] + Gtk.Box { + Gtk.ToggleButton { + icon-name: "view-grid-symbolic"; + tooltip-text: _("View Open Tabs"); + active: bind tab_overview.open bidirectional; + can-focus: false; + focus-on-click: false; + } + + Gtk.MenuButton { + notify::active => $notify_menu_active(); + icon-name: "open-menu-symbolic"; + menu-model: main_menu; + tooltip-text: _("Main Menu"); + can-focus: false; + } + } + } + + [top] + Adw.TabBar tab_bar { + autohide: bind template.tabs-autohide; + expand-tabs: bind template.tabs-wide; + view: tab_view; + visible: bind template.tabs-visible; + + [start] + Gtk.Box { + orientation: horizontal; + visible: bind $titlebar_style_is_tabs(template.titlebar-style) as ; + + Gtk.WindowControls { + side: start; + } + + Adw.SplitButton { + styles [ + "flat", + ] + + clicked => $new_tab(); + icon-name: "tab-new-symbolic"; + tooltip-text: _("New Tab"); + dropdown-tooltip: _("New Split"); + menu-model: split_menu; + can-focus: false; + focus-on-click: false; + } + } + + [end] + Gtk.Box { + orientation: horizontal; + visible: bind $titlebar_style_is_tabs(template.titlebar-style) as ; + + Gtk.ToggleButton { + styles [ + "flat", + ] + + icon-name: "view-grid-symbolic"; + tooltip-text: _("View Open Tabs"); + active: bind tab_overview.open bidirectional; + can-focus: false; + focus-on-click: false; + } + + Gtk.MenuButton { + styles [ + "flat", + ] + + notify::active => $notify_menu_active(); + icon-name: "open-menu-symbolic"; + menu-model: main_menu; + tooltip-text: _("Main Menu"); + can-focus: false; + } + + Gtk.WindowControls { + side: end; + } + } + } + + Box { + orientation: vertical; + + $GhosttyDebugWarning { + visible: bind template.debug; + } + + Adw.ToastOverlay toast_overlay { + Adw.TabView tab_view { + notify::n-pages => $notify_n_pages(); + notify::selected-page => $notify_selected_page(); + close-page => $close_page(); + page-attached => $page_attached(); + page-detached => $page_detached(); + create-window => $tab_create_window(); + setup-menu => $setup_tab_menu(); + menu-model: tab_context_menu; + shortcuts: none; + } + } + } + } + }; +} + +menu split_menu { + item { + label: _("Split Up"); + action: "win.split-up"; + } + + item { + label: _("Split Down"); + action: "win.split-down"; + } + + item { + label: _("Split Left"); + action: "win.split-left"; + } + + item { + label: _("Split Right"); + action: "win.split-right"; + } +} + +menu main_menu { + section { + item { + label: _("Copy"); + action: "win.copy"; + } + + item { + label: _("Paste"); + action: "win.paste"; + } + } + + section { + item { + label: _("New Window"); + action: "win.new-window"; + } + + item { + label: _("Close Window"); + action: "win.close"; + } + } + + section { + item { + label: _("Change Tab Titleโ€ฆ"); + action: "win.prompt-tab-title"; + } + + item { + label: _("New Tab"); + action: "win.new-tab"; + } + + item { + label: _("Close Tab"); + action: "win.close-tab"; + target: "this"; + } + } + + section { + submenu { + label: _("Split"); + + item { + label: _("Change Titleโ€ฆ"); + action: "win.prompt-surface-title"; + } + + item { + label: _("Split Up"); + action: "win.split-up"; + } + + item { + label: _("Split Down"); + action: "win.split-down"; + } + + item { + label: _("Split Left"); + action: "win.split-left"; + } + + item { + label: _("Split Right"); + action: "win.split-right"; + } + } + } + + section { + item { + label: _("Clear"); + action: "win.clear"; + } + + item { + label: _("Reset"); + action: "win.reset"; + } + } + + section { + item { + label: _("Command Palette"); + action: "win.toggle-command-palette"; + } + + item { + label: _("Terminal Inspector"); + action: "win.toggle-inspector"; + } + + item { + label: _("Open Configuration"); + action: "app.open-config"; + } + + item { + label: _("Reload Configuration"); + action: "app.reload-config"; + } + } + + section { + item { + label: _("About Ghostty"); + action: "win.about"; + } + + item { + label: _("Quit"); + action: "app.quit"; + } + } +} + +menu tab_context_menu { + item { + label: _("Change Tab Titleโ€ฆ"); + action: "win.prompt-context-tab-title"; + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/weak_ref.zig b/vendor/libghostty-vt/src/apprt/gtk/weak_ref.zig new file mode 100644 index 00000000..f689e45f --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/weak_ref.zig @@ -0,0 +1,44 @@ +const std = @import("std"); +const gtk = @import("gtk"); +const gobject = @import("gobject"); + +/// A lightweight wrapper around gobject.WeakRef to make it type-safe +/// to hold a single type of value. +pub fn WeakRef(comptime T: type) type { + return struct { + const Self = @This(); + + ref: gobject.WeakRef = std.mem.zeroes(gobject.WeakRef), + + pub const empty: Self = .{}; + + /// Set the weak reference to the given object. This will not + /// increase the reference count of the object. + pub fn set(self: *Self, v_: ?*T) void { + if (v_) |v| { + self.ref.set(v.as(gobject.Object)); + } else { + self.ref.set(null); + } + } + + /// Get a strong reference to the object, or null if the object + /// has been finalized. This increases the reference count by one. + pub fn get(self: *Self) ?*T { + // We can't use `as` because `as` guarantees conversion and + // that can't be statically guaranteed. + return gobject.ext.cast(T, self.ref.get() orelse return null); + } + }; +} + +test WeakRef { + const testing = std.testing; + + var ref: WeakRef(gtk.TextBuffer) = .empty; + const obj: *gtk.TextBuffer = .new(null); + ref.set(obj); + ref.get().?.unref(); // The "?" asserts non-null + obj.unref(); + try testing.expect(ref.get() == null); +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/winproto.zig b/vendor/libghostty-vt/src/apprt/gtk/winproto.zig new file mode 100644 index 00000000..d409d6c2 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/winproto.zig @@ -0,0 +1,155 @@ +const std = @import("std"); +const build_options = @import("build_options"); +const Allocator = std.mem.Allocator; + +const gdk = @import("gdk"); + +const Config = @import("../../config.zig").Config; +const input = @import("../../input.zig"); +const key = @import("key.zig"); +const ApprtWindow = @import("class/window.zig").Window; + +pub const noop = @import("winproto/noop.zig"); +pub const x11 = @import("winproto/x11.zig"); +pub const wayland = @import("winproto/wayland.zig"); + +pub const Protocol = enum { + none, + wayland, + x11, +}; + +/// App-state for the underlying windowing protocol. There should be one +/// instance of this struct per application. +pub const App = union(Protocol) { + none: noop.App, + wayland: if (build_options.wayland) wayland.App else noop.App, + x11: if (build_options.x11) x11.App else noop.App, + + pub fn init( + alloc: Allocator, + gdk_display: *gdk.Display, + app_id: [:0]const u8, + config: *const Config, + ) !App { + inline for (@typeInfo(App).@"union".fields) |field| { + if (try field.type.init( + alloc, + gdk_display, + app_id, + config, + )) |v| { + return @unionInit(App, field.name, v); + } + } + + return .{ .none = .{} }; + } + + pub fn deinit(self: *App) void { + switch (self.*) { + inline else => |*v| v.deinit(), + } + } + + pub fn eventMods( + self: *App, + device: ?*gdk.Device, + gtk_mods: gdk.ModifierType, + ) input.Mods { + return switch (self.*) { + inline else => |*v| v.eventMods(device, gtk_mods), + } orelse key.translateMods(gtk_mods); + } + + pub fn supportsQuickTerminal(self: App) bool { + return switch (self) { + inline else => |v| v.supportsQuickTerminal(), + }; + } + + /// Set up necessary support for the quick terminal that must occur + /// *before* the window-level winproto object is created. + /// + /// Only has an effect on the Wayland backend, where the gtk4-layer-shell + /// library is initialized. + pub fn initQuickTerminal(self: *App, apprt_window: *ApprtWindow) !void { + switch (self.*) { + inline else => |*v| try v.initQuickTerminal(apprt_window), + } + } +}; + +/// Per-Window state for the underlying windowing protocol. +/// +/// In Wayland, the terminology used is "Surface" and for it, this is +/// really "Surface"-specific state. But Ghostty uses the term "Surface" +/// heavily to mean something completely different, so we use "Window" here +/// to better match what it generally maps to in the Ghostty codebase. +pub const Window = union(Protocol) { + none: noop.Window, + wayland: if (build_options.wayland) wayland.Window else noop.Window, + x11: if (build_options.x11) x11.Window else noop.Window, + + pub fn init( + alloc: Allocator, + app: *App, + apprt_window: *ApprtWindow, + ) !Window { + return switch (app.*) { + inline else => |*v, tag| { + inline for (@typeInfo(Window).@"union".fields) |field| { + if (comptime std.mem.eql( + u8, + field.name, + @tagName(tag), + )) return @unionInit( + Window, + field.name, + try field.type.init( + alloc, + v, + apprt_window, + ), + ); + } + }, + }; + } + + pub fn deinit(self: *Window) void { + switch (self.*) { + inline else => |*v| v.deinit(), + } + } + + pub fn resizeEvent(self: *Window) !void { + switch (self.*) { + inline else => |*v| try v.resizeEvent(), + } + } + + pub fn syncAppearance(self: *Window) !void { + switch (self.*) { + inline else => |*v| try v.syncAppearance(), + } + } + + pub fn clientSideDecorationEnabled(self: Window) bool { + return switch (self) { + inline else => |v| v.clientSideDecorationEnabled(), + }; + } + + pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + switch (self.*) { + inline else => |*v| try v.addSubprocessEnv(env), + } + } + + pub fn setUrgent(self: *Window, urgent: bool) !void { + switch (self.*) { + inline else => |*v| try v.setUrgent(urgent), + } + } +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/winproto/BlurRegion.zig b/vendor/libghostty-vt/src/apprt/gtk/winproto/BlurRegion.zig new file mode 100644 index 00000000..f3041dae --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/winproto/BlurRegion.zig @@ -0,0 +1,187 @@ +const BlurRegion = @This(); +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const gobject = @import("gobject"); +const gdk = @import("gdk"); +const gtk = @import("gtk"); + +const Window = @import("../winproto.zig").Window; +const ApprtWindow = @import("../class/window.zig").Window; + +slices: std.ArrayList(Slice), + +/// A rectangular slice of the blur region. +// Marked `extern` since we want to be able to use this in X11 directly. +pub const Slice = extern struct { + x: Pos, + y: Pos, + width: Pos, + height: Pos, +}; + +// X11 compatibility. Ideally this should just be an `i32` like Wayland, +// but XLib sucks +const Pos = c_long; + +pub const empty: BlurRegion = .{ + .slices = .empty, +}; + +pub fn deinit(self: *BlurRegion, alloc: Allocator) void { + self.slices.deinit(alloc); + self.slices = .empty; +} + +// Calculate the blur regions for a window. +// +// Since we have rounded corners by default, we need to carve out the +// pixels on each corner to avoid the "korners bug". +// (cf. https://github.com/cutefishos/fishui/blob/41d4ba194063a3c7fff4675619b57e6ac0504f06/src/platforms/linux/blurhelper/windowblur.cpp#L134) +pub fn calcForWindow( + alloc: Allocator, + window: *ApprtWindow, + csd: bool, + to_device_coordinates: bool, +) Allocator.Error!BlurRegion { + const native = window.as(gtk.Native); + const surface = native.getSurface() orelse return .empty; + + var slices: std.ArrayList(Slice) = .empty; + errdefer slices.deinit(alloc); + + // Calculate the primary blur region + // (the one that covers most of the screen). + // It's easier to do this inside a vector since we have to scale + // everything by the scale factor anyways. + + // NOTE(pluiedev): CSDs are a f--king mistake. + // Please, GNOME, stop this nonsense of making a window ~30% bigger + // internally than how they really are just for your shadows and + // rounded corners and all that fluff. Please. I beg of you. + const x: Pos, const y: Pos = off: { + var x: f64 = 0; + var y: f64 = 0; + native.getSurfaceTransform(&x, &y); + // Slightly inset the corners if we're using CSDs + if (csd) { + x += 1; + y += 1; + } + break :off .{ @intFromFloat(x), @intFromFloat(y) }; + }; + + var width = @as(Pos, surface.getWidth()); + var height = @as(Pos, surface.getHeight()); + + // Trim off the offsets. Be careful not to get negative. + width -= x * 2; + height -= y * 2; + if (width <= 0 or height <= 0) return .empty; + + // Empirically determined. + const are_corners_rounded = rounded: { + // This cast should always succeed as all of our windows + // should be toplevel. If this fails, something very strange + // is going on. + const toplevel = gobject.ext.cast( + gdk.Toplevel, + surface, + ) orelse break :rounded false; + + const state = toplevel.getState(); + if (state.fullscreen or state.maximized or state.tiled) + break :rounded false; + + break :rounded csd; + }; + + const new_slices = try approxRoundedRect( + alloc, + x, + y, + width, + height, + // See https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/css-variables.html#window-radius + if (are_corners_rounded) 15 else 0, + ); + + if (to_device_coordinates) { + // Transform surface coordinates to device coordinates. + const sf = surface.getScaleFactor(); + for (new_slices.items) |*s| { + s.x *= sf; + s.y *= sf; + s.width *= sf; + s.height *= sf; + } + } + + return .{ .slices = new_slices }; +} + +/// Whether two sets of blur regions are equal. +pub fn eql(self: BlurRegion, other: BlurRegion) bool { + if (self.slices.items.len != other.slices.items.len) return false; + for (self.slices.items, other.slices.items) |this, that| { + if (!std.meta.eql(this, that)) return false; + } + return true; +} + +/// Approximate a rounded rectangle with many smaller rectangles. +fn approxRoundedRect( + alloc: Allocator, + x: Pos, + y: Pos, + width: Pos, + height: Pos, + radius: Pos, +) Allocator.Error!std.ArrayList(Slice) { + const r_f: f32 = @floatFromInt(radius); + + var slices: std.ArrayList(Slice) = .empty; + errdefer slices.deinit(alloc); + + // Add the central rectangle + try slices.append(alloc, .{ + .x = x, + .y = y + radius, + .width = width, + .height = height - 2 * radius, + }); + + // Add the corner rows. This is honestly quite cursed. + var row: Pos = 0; + while (row < radius) : (row += 1) { + // y distance from this row to the center corner circle + const dy = @as(f32, @floatFromInt(radius - row)) - 0.5; + + // x distance - as given by the definition of a circle + const dx = @sqrt(r_f * r_f - dy * dy); + + // How much each row should be offset, rounded to an integer + const row_x: Pos = @intFromFloat(r_f - @round(dx + 0.5)); + + // Remove the offset from both ends + const row_w = width - 2 * row_x; + + // Top slice + try slices.append(alloc, .{ + .x = x + row_x, + .y = y + row, + .width = row_w, + .height = 1, + }); + + // Bottom slice + try slices.append(alloc, .{ + .x = x + row_x, + .y = y + height - 1 - row, + .width = row_w, + .height = 1, + }); + } + + return slices; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/winproto/noop.zig b/vendor/libghostty-vt/src/apprt/gtk/winproto/noop.zig new file mode 100644 index 00000000..950ee0f3 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/winproto/noop.zig @@ -0,0 +1,73 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const gdk = @import("gdk"); + +const Config = @import("../../../config.zig").Config; +const input = @import("../../../input.zig"); +const ApprtWindow = @import("../class/window.zig").Window; + +const log = std.log.scoped(.winproto_noop); + +pub const App = struct { + pub fn init( + _: Allocator, + _: *gdk.Display, + _: [:0]const u8, + _: *const Config, + ) !?App { + return null; + } + + pub fn deinit(self: *App) void { + _ = self; + } + + pub fn eventMods( + _: *App, + _: ?*gdk.Device, + _: gdk.ModifierType, + ) ?input.Mods { + return null; + } + + pub fn supportsQuickTerminal(_: App) bool { + return false; + } + pub fn initQuickTerminal(_: *App, _: *ApprtWindow) !void {} +}; + +pub const Window = struct { + pub fn init( + _: Allocator, + _: *App, + _: *ApprtWindow, + ) !Window { + return .{}; + } + + pub fn deinit(self: *Window) void { + _ = self; + } + + pub fn updateConfigEvent( + _: *Window, + _: *const ApprtWindow.DerivedConfig, + ) !void {} + + pub fn resizeEvent(_: *Window) !void {} + + pub fn syncAppearance(_: *Window) !void {} + + /// This returns true if CSD is enabled for this window. This + /// should be the actual present state of the window, not the + /// desired state. + pub fn clientSideDecorationEnabled(self: Window) bool { + _ = self; + return true; + } + + pub fn addSubprocessEnv(_: *Window, _: *std.process.EnvMap) !void {} + + pub fn setUrgent(_: *Window, _: bool) !void {} +}; diff --git a/vendor/libghostty-vt/src/apprt/gtk/winproto/wayland.zig b/vendor/libghostty-vt/src/apprt/gtk/winproto/wayland.zig new file mode 100644 index 00000000..12c7fb8a --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/winproto/wayland.zig @@ -0,0 +1,486 @@ +//! Wayland protocol implementation for the Ghostty GTK apprt. +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const gdk = @import("gdk"); +const gdk_wayland = @import("gdk_wayland"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); +const layer_shell = @import("gtk4-layer-shell"); + +const wayland = @import("wayland"); +const wl = wayland.client.wl; +const ext = wayland.client.ext; +const kde = wayland.client.kde; +const org = wayland.client.org; +const xdg = wayland.client.xdg; + +const Config = @import("../../../config.zig").Config; +const Globals = @import("wayland/Globals.zig"); +const input = @import("../../../input.zig"); +const ApprtWindow = @import("../class/window.zig").Window; +const BlurRegion = @import("BlurRegion.zig"); + +const log = std.log.scoped(.winproto_wayland); + +/// Wayland state that contains application-wide Wayland objects (e.g. wl_display). +pub const App = struct { + display: *wl.Display, + globals: *Globals, + + pub fn init( + alloc: Allocator, + gdk_display: *gdk.Display, + app_id: [:0]const u8, + config: *const Config, + ) !?App { + _ = config; + _ = app_id; + + const gdk_wayland_display = gobject.ext.cast( + gdk_wayland.WaylandDisplay, + gdk_display, + ) orelse return null; + + const display: *wl.Display = @ptrCast(@alignCast( + gdk_wayland_display.getWlDisplay() orelse return error.NoWaylandDisplay, + )); + + const globals: *Globals = try .init(alloc, display); + errdefer globals.deinit(); + + return .{ + .display = display, + .globals = globals, + }; + } + + pub fn deinit(self: *App) void { + self.globals.deinit(); + } + + pub fn eventMods( + _: *App, + _: ?*gdk.Device, + _: gdk.ModifierType, + ) ?input.Mods { + return null; + } + + pub fn supportsQuickTerminal(self: App) bool { + _ = self; + if (!layer_shell.isSupported()) { + log.warn("your compositor does not support the wlr-layer-shell protocol; disabling quick terminal", .{}); + return false; + } + + return true; + } + + pub fn initQuickTerminal(self: *App, apprt_window: *ApprtWindow) !void { + const window = apprt_window.as(gtk.Window); + layer_shell.initForWindow(window); + + // Set target monitor based on config (null lets compositor decide) + const monitor = resolveQuickTerminalMonitor(self.globals, apprt_window); + defer if (monitor) |v| v.unref(); + layer_shell.setMonitor(window, monitor); + } +}; + +/// Per-window (wl_surface) state for the Wayland protocol. +pub const Window = struct { + apprt_window: *ApprtWindow, + + /// The Wayland surface for this window. + surface: *wl.Surface, + + /// The context from the app where we can load our Wayland interfaces. + globals: *Globals, + + /// Object that controls background effects like background blur. + bg_effect: ?*ext.BackgroundEffectSurfaceV1 = null, + + /// Object that controls the decoration mode (client/server/auto) + /// of the window. + decoration: ?*org.KdeKwinServerDecoration = null, + + /// Object that controls the slide-in/slide-out animations of the + /// quick terminal. Always null for windows other than the quick terminal. + slide: ?*org.KdeKwinSlide = null, + + /// Object that, when present, denotes that the window is currently + /// requesting attention from the user. + activation_token: ?*xdg.ActivationTokenV1 = null, + + blur_region: BlurRegion = .empty, + + pub fn init( + alloc: Allocator, + app: *App, + apprt_window: *ApprtWindow, + ) !Window { + _ = alloc; + + const gtk_native = apprt_window.as(gtk.Native); + const gdk_surface = gtk_native.getSurface() orelse return error.NotWaylandSurface; + + // This should never fail, because if we're being called at this point + // then we've already asserted that our app state is Wayland. + const gdk_wl_surface = gobject.ext.cast( + gdk_wayland.WaylandSurface, + gdk_surface, + ) orelse return error.NoWaylandSurface; + + const wl_surface: *wl.Surface = @ptrCast(@alignCast( + gdk_wl_surface.getWlSurface() orelse return error.NoWaylandSurface, + )); + + // Get our decoration object so we can control the + // CSD vs SSD status of this surface. + const deco: ?*org.KdeKwinServerDecoration = deco: { + const mgr = app.globals.get(.kde_decoration_manager) orelse + break :deco null; + + const deco: *org.KdeKwinServerDecoration = mgr.create( + wl_surface, + ) catch |err| { + log.warn("could not create decoration object={}", .{err}); + break :deco null; + }; + + break :deco deco; + }; + + const bg_effect: ?*ext.BackgroundEffectSurfaceV1 = bg: { + const mgr = app.globals.get(.ext_background_effect) orelse + break :bg null; + + const bg_effect: *ext.BackgroundEffectSurfaceV1 = mgr.getBackgroundEffect( + wl_surface, + ) catch |err| { + log.warn("could not create background effect object={}", .{err}); + break :bg null; + }; + + break :bg bg_effect; + }; + + if (apprt_window.isQuickTerminal()) { + _ = gdk.Surface.signals.enter_monitor.connect( + gdk_surface, + *ApprtWindow, + enteredMonitor, + apprt_window, + .{}, + ); + } + + return .{ + .apprt_window = apprt_window, + .surface = wl_surface, + .globals = app.globals, + .decoration = deco, + .bg_effect = bg_effect, + }; + } + + pub fn deinit(self: *Window) void { + self.blur_region.deinit(self.globals.alloc); + if (self.bg_effect) |bg| bg.destroy(); + if (self.decoration) |deco| deco.release(); + if (self.slide) |slide| slide.release(); + } + + pub fn resizeEvent(self: *Window) !void { + self.syncBlur() catch |err| { + log.err("failed to sync blur={}", .{err}); + }; + } + + pub fn syncAppearance(self: *Window) !void { + self.syncBlur() catch |err| { + log.err("failed to sync blur={}", .{err}); + }; + self.syncDecoration() catch |err| { + log.err("failed to sync decoration={}", .{err}); + }; + + if (self.apprt_window.isQuickTerminal()) { + self.syncQuickTerminal() catch |err| { + log.warn("failed to sync quick terminal appearance={}", .{err}); + }; + } + } + + pub fn clientSideDecorationEnabled(self: Window) bool { + return switch (self.getDecorationMode()) { + .Client => true, + // If we support SSDs, then we should *not* enable CSDs if we prefer SSDs. + // However, if we do not support SSDs (e.g. GNOME) then we should enable + // CSDs even if the user prefers SSDs. + .Server => if (self.globals.get(.kde_decoration_manager)) |_| false else true, + .None => false, + else => unreachable, + }; + } + + pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + _ = self; + _ = env; + } + + pub fn setUrgent(self: *Window, urgent: bool) !void { + const activation = self.globals.get(.xdg_activation) orelse return; + + // If there already is a token, destroy and unset it + if (self.activation_token) |token| token.destroy(); + + self.activation_token = if (urgent) token: { + const token = try activation.getActivationToken(); + token.setSurface(self.surface); + token.setListener(*Window, onActivationTokenEvent, self); + token.commit(); + break :token token; + } else null; + } + + /// Update the blur state of the window. + fn syncBlur(self: *Window) !void { + const compositor = self.globals.get(.compositor) orelse return; + const bg = self.bg_effect orelse return; + if (!self.globals.state.bg_effect_capabilities.blur) return; + + const config = if (self.apprt_window.getConfig()) |v| + v.get() + else + return; + const blur = config.@"background-blur"; + + if (!blur.enabled()) { + self.blur_region.deinit(self.globals.alloc); + bg.setBlurRegion(null); + return; + } + + var region: BlurRegion = try .calcForWindow( + self.globals.alloc, + self.apprt_window, + self.clientSideDecorationEnabled(), + false, + ); + errdefer region.deinit(self.globals.alloc); + + if (region.eql(self.blur_region)) { + // Region didn't change. Don't do anything. + region.deinit(self.globals.alloc); + return; + } + + const wl_region = try compositor.createRegion(); + errdefer if (wl_region) |r| r.destroy(); + for (region.slices.items) |s| wl_region.add( + @intCast(s.x), + @intCast(s.y), + @intCast(s.width), + @intCast(s.height), + ); + + bg.setBlurRegion(wl_region); + self.blur_region = region; + } + + fn syncDecoration(self: *Window) !void { + const deco = self.decoration orelse return; + + // The protocol requests uint instead of enum so we have + // to convert it. + deco.requestMode(@intCast(@intFromEnum(self.getDecorationMode()))); + } + + fn getDecorationMode(self: Window) org.KdeKwinServerDecorationManager.Mode { + return switch (self.apprt_window.getWindowDecoration()) { + .auto => self.globals.state.default_deco_mode orelse .Client, + .client => .Client, + .server => .Server, + .none => .None, + }; + } + + fn syncQuickTerminal(self: *Window) !void { + const window = self.apprt_window.as(gtk.Window); + const config = if (self.apprt_window.getConfig()) |v| + v.get() + else + return; + + layer_shell.setLayer(window, switch (config.@"gtk-quick-terminal-layer") { + .overlay => .overlay, + .top => .top, + .bottom => .bottom, + .background => .background, + }); + layer_shell.setNamespace(window, config.@"gtk-quick-terminal-namespace"); + + // Re-resolve the target monitor on every sync so that config reloads + // and primary-output changes take effect without recreating the window. + const target_monitor = resolveQuickTerminalMonitor(self.globals, self.apprt_window); + defer if (target_monitor) |v| v.unref(); + layer_shell.setMonitor(window, target_monitor); + + layer_shell.setKeyboardMode( + window, + switch (config.@"quick-terminal-keyboard-interactivity") { + .none => .none, + .@"on-demand" => on_demand: { + if (layer_shell.getProtocolVersion() < 4) { + log.warn("your compositor does not support on-demand keyboard access; falling back to exclusive access", .{}); + break :on_demand .exclusive; + } + break :on_demand .on_demand; + }, + .exclusive => .exclusive, + }, + ); + + const anchored_edge: ?layer_shell.ShellEdge = switch (config.@"quick-terminal-position") { + .left => .left, + .right => .right, + .top => .top, + .bottom => .bottom, + .center => null, + }; + + for (std.meta.tags(layer_shell.ShellEdge)) |edge| { + if (anchored_edge) |anchored| { + if (edge == anchored) { + layer_shell.setMargin(window, edge, 0); + layer_shell.setAnchor(window, edge, true); + continue; + } + } + + // Arbitrary margin - could be made customizable? + layer_shell.setMargin(window, edge, 20); + layer_shell.setAnchor(window, edge, false); + } + + if (self.slide) |slide| slide.release(); + + self.slide = if (anchored_edge) |anchored| slide: { + const mgr = self.globals.get(.kde_slide_manager) orelse break :slide null; + + const slide = mgr.create(self.surface) catch |err| { + log.warn("could not create slide object={}", .{err}); + break :slide null; + }; + + const slide_location: org.KdeKwinSlide.Location = switch (anchored) { + .top => .top, + .bottom => .bottom, + .left => .left, + .right => .right, + }; + + slide.setLocation(@intCast(@intFromEnum(slide_location))); + slide.commit(); + break :slide slide; + } else null; + } + + /// Update the size of the quick terminal based on monitor dimensions. + fn enteredMonitor( + _: *gdk.Surface, + monitor: *gdk.Monitor, + apprt_window: *ApprtWindow, + ) callconv(.c) void { + const window = apprt_window.as(gtk.Window); + const config = if (apprt_window.getConfig()) |v| v.get() else return; + + const resolved_monitor = resolveQuickTerminalMonitor( + apprt_window.winproto().wayland.globals, + apprt_window, + ); + defer if (resolved_monitor) |v| v.unref(); + + // Use the configured monitor for sizing if not in mouse mode. + const size_monitor = resolved_monitor orelse monitor; + + var monitor_size: gdk.Rectangle = undefined; + size_monitor.getGeometry(&monitor_size); + + const dims = config.@"quick-terminal-size".calculate( + config.@"quick-terminal-position", + .{ + .width = @intCast(monitor_size.f_width), + .height = @intCast(monitor_size.f_height), + }, + ); + + window.setDefaultSize(@intCast(dims.width), @intCast(dims.height)); + } + + fn onActivationTokenEvent( + token: *xdg.ActivationTokenV1, + event: xdg.ActivationTokenV1.Event, + self: *Window, + ) void { + const activation = self.globals.get(.xdg_activation) orelse return; + const current_token = self.activation_token orelse return; + + if (token.getId() != current_token.getId()) { + log.warn("received event for unknown activation token; ignoring", .{}); + return; + } + + switch (event) { + .done => |done| { + activation.activate(done.token, self.surface); + token.destroy(); + self.activation_token = null; + }, + } + } +}; + +/// Resolve the quick-terminal-screen config to a specific monitor. +/// Returns null to let the compositor decide (used for .mouse mode). +/// Caller owns the returned ref and must unref it. +fn resolveQuickTerminalMonitor( + globals: *Globals, + apprt_window: *ApprtWindow, +) ?*gdk.Monitor { + const config = if (apprt_window.getConfig()) |v| v.get() else return null; + + switch (config.@"quick-terminal-screen") { + .mouse => return null, + .main, .@"macos-menu-bar" => {}, + } + + const display = apprt_window.as(gtk.Widget).getDisplay(); + const monitors = display.getMonitors(); + + // Try to find the monitor matching the primary output name. + if (globals.state.primary_output_name) |stored_name| { + var i: u32 = 0; + while (monitors.getObject(i)) |item| : (i += 1) { + const monitor = gobject.ext.cast(gdk.Monitor, item) orelse { + item.unref(); + continue; + }; + if (monitor.getConnector()) |connector_z| { + if (std.mem.orderZ(u8, connector_z, stored_name) == .eq) { + return monitor; + } + } + monitor.unref(); + } + } + + // Fall back to the first monitor in the list. + const first = monitors.getObject(0) orelse return null; + return gobject.ext.cast(gdk.Monitor, first) orelse { + first.unref(); + return null; + }; +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/winproto/wayland/Globals.zig b/vendor/libghostty-vt/src/apprt/gtk/winproto/wayland/Globals.zig new file mode 100644 index 00000000..83052cbe --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/winproto/wayland/Globals.zig @@ -0,0 +1,252 @@ +const Globals = @This(); + +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const wayland = @import("wayland"); +const wl = wayland.client.wl; +const ext = wayland.client.ext; +const kde = wayland.client.kde; +const org = wayland.client.org; +const xdg = wayland.client.xdg; + +const log = std.log.scoped(.winproto_wayland_globals); + +alloc: Allocator, +state: State, +map: std.EnumMap(Tag, Binding), + +/// Used in the initial roundtrip to determine whether more +/// roundtrips are required to fetch the initial state. +needs_roundtrip: bool = false, + +const Binding = struct { + // All globals can be casted into a wl.Proxy object. + proxy: *wl.Proxy, + name: u32, +}; + +pub const Tag = enum { + compositor, + ext_background_effect, + kde_decoration_manager, + kde_slide_manager, + kde_output_order, + xdg_activation, + + fn Type(comptime self: Tag) type { + return switch (self) { + .compositor => wl.Compositor, + .ext_background_effect => ext.BackgroundEffectManagerV1, + .kde_decoration_manager => org.KdeKwinServerDecorationManager, + .kde_slide_manager => org.KdeKwinSlideManager, + .kde_output_order => kde.OutputOrderV1, + .xdg_activation => xdg.ActivationV1, + }; + } +}; + +pub const State = struct { + /// Connector name of the primary output (e.g., "DP-1") as reported + /// by kde_output_order_v1. The first output in each priority list + /// is the primary. + primary_output_name: ?[:0]const u8 = null, + + /// Tracks the output order event cycle. Set to true after a `done` + /// event so the next `output` event is captured as the new primary. + /// Initialized to true so the first event after binding is captured. + output_order_done: bool = true, + + default_deco_mode: ?org.KdeKwinServerDecorationManager.Mode = null, + + bg_effect_capabilities: ext.BackgroundEffectManagerV1.Capability = .{}, + + /// Reset cached state derived from kde_output_order_v1. + fn resetOutputOrder(self: *State, alloc: Allocator) void { + if (self.primary_output_name) |name| alloc.free(name); + self.primary_output_name = null; + self.output_order_done = true; + } +}; + +pub fn init(alloc: Allocator, display: *wl.Display) !*Globals { + // We need to allocate here since the listener + // expects a stable memory address. + const self = try alloc.create(Globals); + self.* = .{ + .alloc = alloc, + .state = .{}, + .map = .{}, + }; + + const registry = try display.getRegistry(); + registry.setListener(*Globals, registryListener, self); + if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed; + + // Do another roundtrip to process events emitted by globals we bound + // during registry discovery (e.g. default decoration mode, output + // order). Listeners are installed at bind time in registryListener. + if (self.needs_roundtrip) { + if (display.roundtrip() != .SUCCESS) return error.RoundtripFailed; + } + return self; +} + +pub fn deinit(self: *Globals) void { + if (self.state.primary_output_name) |name| self.alloc.free(name); + self.alloc.destroy(self); +} + +pub fn get(self: *const Globals, comptime tag: Tag) ?*tag.Type() { + const binding = self.map.get(tag) orelse return null; + return @ptrCast(binding.proxy); +} + +fn onGlobalAttached(self: *Globals, comptime tag: Tag) void { + // Install listeners immediately at bind time. This + // keeps listener setup and object lifetime in one + // place and also supports globals that appear later. + switch (tag) { + .ext_background_effect => { + const v = self.get(tag) orelse return; + v.setListener(*Globals, bgEffectListener, self); + self.needs_roundtrip = true; + }, + .kde_decoration_manager => { + const v = self.get(tag) orelse return; + v.setListener(*Globals, decoManagerListener, self); + self.needs_roundtrip = true; + }, + .kde_output_order => { + const v = self.get(tag) orelse return; + v.setListener(*Globals, outputOrderListener, self); + self.needs_roundtrip = true; + }, + else => {}, + } +} + +fn onGlobalRemoved(self: *Globals, tag: Tag) void { + switch (tag) { + .kde_output_order => self.state.resetOutputOrder(self.alloc), + else => {}, + } +} + +fn registryListener( + registry: *wl.Registry, + event: wl.Registry.Event, + self: *Globals, +) void { + switch (event) { + .global => |v| { + log.debug("found global {s}", .{v.interface}); + inline for (comptime std.meta.tags(Tag)) |tag| { + const T = tag.Type(); + if (std.mem.orderZ(u8, v.interface, T.interface.name) == .eq) { + log.debug("matched {}", .{T}); + + const new_proxy = registry.bind( + v.name, + T, + T.generated_version, + ) catch |err| { + log.warn( + "error binding interface {s} error={}", + .{ v.interface, err }, + ); + return; + }; + + // If this global was already bound, + // then we also need to destroy the old binding. + if (self.map.get(tag)) |old| { + self.onGlobalRemoved(tag); + old.proxy.destroy(); + } + + self.map.put(tag, .{ + .proxy = @ptrCast(new_proxy), + .name = v.name, + }); + self.onGlobalAttached(tag); + } + } + }, + + // This should be a rare occurrence, but in case a global + // is suddenly no longer available, we destroy and unset it + // as the protocol mandates. + .global_remove => |v| { + var it = self.map.iterator(); + while (it.next()) |kv| { + if (kv.value.name != v.name) continue; + self.onGlobalRemoved(kv.key); + kv.value.proxy.destroy(); + self.map.remove(kv.key); + } + }, + } +} + +fn bgEffectListener( + _: *ext.BackgroundEffectManagerV1, + event: ext.BackgroundEffectManagerV1.Event, + self: *Globals, +) void { + switch (event) { + .capabilities => |cap| { + self.state.bg_effect_capabilities = cap.flags; + }, + } +} + +fn decoManagerListener( + _: *org.KdeKwinServerDecorationManager, + event: org.KdeKwinServerDecorationManager.Event, + self: *Globals, +) void { + switch (event) { + .default_mode => |mode| { + self.state.default_deco_mode = @enumFromInt(mode.mode); + }, + } +} + +fn outputOrderListener( + _: *kde.OutputOrderV1, + event: kde.OutputOrderV1.Event, + self: *Globals, +) void { + switch (event) { + .output => |v| { + // Only the first output event after a `done` is the new primary. + if (!self.state.output_order_done) return; + self.state.output_order_done = false; + + const name = std.mem.sliceTo(v.output_name, 0); + if (self.state.primary_output_name) |old| self.alloc.free(old); + + if (name.len == 0) { + self.state.primary_output_name = null; + log.warn("ignoring empty primary output name from kde_output_order_v1", .{}); + } else { + self.state.primary_output_name = self.alloc.dupeZ(u8, name) catch |err| { + self.state.primary_output_name = null; + log.warn("failed to allocate primary output name: {}", .{err}); + return; + }; + log.debug("primary output: {s}", .{name}); + } + }, + .done => { + if (self.state.output_order_done) { + // No output arrived since the previous done. Treat this as + // an empty update and drop any stale cached primary. + self.state.resetOutputOrder(self.alloc); + return; + } + self.state.output_order_done = true; + }, + } +} diff --git a/vendor/libghostty-vt/src/apprt/gtk/winproto/x11.zig b/vendor/libghostty-vt/src/apprt/gtk/winproto/x11.zig new file mode 100644 index 00000000..8109959d --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/gtk/winproto/x11.zig @@ -0,0 +1,498 @@ +//! X11 window protocol implementation for the Ghostty GTK apprt. +const std = @import("std"); +const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; + +const gdk = @import("gdk"); +const gdk_x11 = @import("gdk_x11"); +const glib = @import("glib"); +const gobject = @import("gobject"); +const gtk = @import("gtk"); +const xlib = @import("xlib"); + +pub const c = @cImport({ + @cInclude("X11/Xlib.h"); + @cInclude("X11/Xatom.h"); + @cInclude("X11/XKBlib.h"); +}); + +const input = @import("../../../input.zig"); +const Config = @import("../../../config.zig").Config; +const ApprtWindow = @import("../class/window.zig").Window; +const BlurRegion = @import("BlurRegion.zig"); + +const log = std.log.scoped(.gtk_x11); + +pub const App = struct { + display: *xlib.Display, + base_event_code: c_int, + atoms: Atoms, + + pub fn init( + _: Allocator, + gdk_display: *gdk.Display, + app_id: [:0]const u8, + config: *const Config, + ) !?App { + // If the display isn't X11, then we don't need to do anything. + const gdk_x11_display = gobject.ext.cast( + gdk_x11.X11Display, + gdk_display, + ) orelse return null; + + const xlib_display = gdk_x11_display.getXdisplay(); + + const x11_program_name: [:0]const u8 = if (config.@"x11-instance-name") |pn| + pn + else if (builtin.mode == .Debug) + "ghostty-debug" + else + "ghostty"; + + // Set the X11 window class property (WM_CLASS) if we are on an X11 + // display. + // + // Note that we also set the program name here using g_set_prgname. + // This is how the instance name field for WM_CLASS is derived when + // calling gdk_x11_display_set_program_class; there does not seem to be + // a way to set it directly. It does not look like this is being set by + // our other app initialization routines currently, but since we're + // currently deriving its value from x11-instance-name effectively, I + // feel like gating it behind an X11 check is better intent. + // + // This makes the property show up like so when using xprop: + // + // WM_CLASS(STRING) = "ghostty", "com.mitchellh.ghostty" + // + // Append "-debug" on both when using the debug build. + glib.setPrgname(x11_program_name); + gdk_x11.X11Display.setProgramClass(gdk_display, app_id); + + // XKB + log.debug("Xkb.init: initializing Xkb", .{}); + log.debug("Xkb.init: running XkbQueryExtension", .{}); + var opcode: c_int = 0; + var base_event_code: c_int = 0; + var base_error_code: c_int = 0; + var major = c.XkbMajorVersion; + var minor = c.XkbMinorVersion; + if (c.XkbQueryExtension( + @ptrCast(@alignCast(xlib_display)), + &opcode, + &base_event_code, + &base_error_code, + &major, + &minor, + ) == 0) { + log.err("Fatal: error initializing Xkb extension: error executing XkbQueryExtension", .{}); + return error.XkbInitializationError; + } + + log.debug("Xkb.init: running XkbSelectEventDetails", .{}); + if (c.XkbSelectEventDetails( + @ptrCast(@alignCast(xlib_display)), + c.XkbUseCoreKbd, + c.XkbStateNotify, + c.XkbModifierStateMask, + c.XkbModifierStateMask, + ) == 0) { + log.err("Fatal: error initializing Xkb extension: error executing XkbSelectEventDetails", .{}); + return error.XkbInitializationError; + } + + return .{ + .display = xlib_display, + .base_event_code = base_event_code, + .atoms = .init(gdk_x11_display), + }; + } + + pub fn deinit(self: *App) void { + _ = self; + } + + /// Checks for an immediate pending XKB state update event, and returns the + /// keyboard state based on if it finds any. This is necessary as the + /// standard GTK X11 API (and X11 in general) does not include the current + /// key pressed in any modifier state snapshot for that event (e.g. if the + /// pressed key is a modifier, that is not necessarily reflected in the + /// modifiers). + /// + /// Returns null if there is no event. In this case, the caller should fall + /// back to the standard GDK modifier state (this likely means the key + /// event did not result in a modifier change). + pub fn eventMods( + self: App, + device: ?*gdk.Device, + gtk_mods: gdk.ModifierType, + ) ?input.Mods { + _ = device; + _ = gtk_mods; + + // Shoutout to Mozilla for figuring out a clean way to do this, this is + // paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp. + if (c.XEventsQueued( + @ptrCast(@alignCast(self.display)), + c.QueuedAfterReading, + ) == 0) return null; + + var nextEvent: c.XEvent = undefined; + _ = c.XPeekEvent(@ptrCast(@alignCast(self.display)), &nextEvent); + if (nextEvent.type != self.base_event_code) return null; + + const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent); + if (xkb_event.any.xkb_type != c.XkbStateNotify) return null; + + const xkb_state_notify_event: *c.XkbStateNotifyEvent = @ptrCast(xkb_event); + // Check the state according to XKB masks. + const lookup_mods = xkb_state_notify_event.lookup_mods; + var mods: input.Mods = .{}; + + log.debug("X11: found extra XkbStateNotify event w/lookup_mods: {b}", .{lookup_mods}); + if (lookup_mods & c.ShiftMask != 0) mods.shift = true; + if (lookup_mods & c.ControlMask != 0) mods.ctrl = true; + if (lookup_mods & c.Mod1Mask != 0) mods.alt = true; + if (lookup_mods & c.Mod4Mask != 0) mods.super = true; + if (lookup_mods & c.LockMask != 0) mods.caps_lock = true; + + return mods; + } + + pub fn supportsQuickTerminal(_: App) bool { + log.warn("quick terminal is not yet supported on X11", .{}); + return false; + } + + pub fn initQuickTerminal(_: *App, _: *ApprtWindow) !void {} +}; + +pub const Window = struct { + app: *App, + apprt_window: *ApprtWindow, + x11_surface: *gdk_x11.X11Surface, + alloc: Allocator, + + blur_region: BlurRegion = .empty, + + // Cache last applied values to avoid redundant X11 property updates. + // Redundant property updates seem to cause some visual glitches + // with some window managers: https://github.com/ghostty-org/ghostty/pull/8075 + last_applied_decoration_hints: ?MotifWMHints = null, + + pub fn init( + alloc: Allocator, + app: *App, + apprt_window: *ApprtWindow, + ) !Window { + const surface = apprt_window.as(gtk.Native).getSurface() orelse + return error.NotX11Surface; + + const x11_surface = gobject.ext.cast( + gdk_x11.X11Surface, + surface, + ) orelse return error.NotX11Surface; + + return .{ + .app = app, + .alloc = alloc, + .apprt_window = apprt_window, + .x11_surface = x11_surface, + }; + } + + pub fn deinit(self: *Window) void { + self.blur_region.deinit(self.alloc); + } + + pub fn resizeEvent(self: *Window) !void { + // The blur region must update with window resizes + self.syncBlur() catch |err| { + log.err("failed to sync blur={}", .{err}); + }; + } + + pub fn syncAppearance(self: *Window) !void { + // The user could have toggled between CSDs and SSDs, + // therefore we need to recalculate the blur region offset. + self.syncBlur() catch |err| { + log.err("failed to sync blur={}", .{err}); + }; + self.syncDecorations() catch |err| { + log.err("failed to sync decorations={}", .{err}); + }; + } + + pub fn clientSideDecorationEnabled(self: Window) bool { + return switch (self.apprt_window.getWindowDecoration()) { + .auto, .client => true, + .server, .none => false, + }; + } + + fn syncBlur(self: *Window) !void { + const config = if (self.apprt_window.getConfig()) |v| v.get() else return; + + // When blur is disabled, remove the property if it was previously set + const blur = config.@"background-blur"; + + var region: BlurRegion = if (blur.enabled()) + try .calcForWindow( + self.alloc, + self.apprt_window, + self.clientSideDecorationEnabled(), + true, + ) + else + .empty; + errdefer region.deinit(self.alloc); + + // Only update X11 properties when the blur region actually changes + if (region.eql(self.blur_region)) { + region.deinit(self.alloc); + return; + } + + if (region.slices.items.len > 0) { + log.debug("set blur={}, window xid={}, region={}", .{ + blur, + self.x11_surface.getXid(), + region, + }); + + try self.changeProperty( + BlurRegion.Slice, + self.app.atoms.kde_blur, + c.XA_CARDINAL, + ._32, + .{ .mode = .replace }, + region.slices.items, + ); + } else { + try self.deleteProperty(self.app.atoms.kde_blur); + } + + self.blur_region.deinit(self.alloc); + self.blur_region = region; + } + + fn syncDecorations(self: *Window) !void { + var hints: MotifWMHints = .{}; + + self.getWindowProperty( + MotifWMHints, + self.app.atoms.motif_wm_hints, + self.app.atoms.motif_wm_hints, + ._32, + .{}, + &hints, + ) catch |err| switch (err) { + // motif_wm_hints is already initialized, so this is fine + error.PropertyNotFound => {}, + + error.RequestFailed, + error.PropertyTypeMismatch, + error.PropertyFormatMismatch, + => return err, + }; + + hints.flags.decorations = true; + hints.decorations.all = switch (self.apprt_window.getWindowDecoration()) { + .server => true, + .auto, .client, .none => false, + }; + + // Only update decoration hints when they actually change + if (self.last_applied_decoration_hints) |last| { + if (std.meta.eql(hints, last)) return; + } + + try self.changeProperty( + MotifWMHints, + self.app.atoms.motif_wm_hints, + self.app.atoms.motif_wm_hints, + ._32, + .{ .mode = .replace }, + &.{hints}, + ); + self.last_applied_decoration_hints = hints; + } + + pub fn addSubprocessEnv(self: *Window, env: *std.process.EnvMap) !void { + var buf: [64]u8 = undefined; + const window_id = try std.fmt.bufPrint( + &buf, + "{}", + .{self.x11_surface.getXid()}, + ); + + try env.put("WINDOWID", window_id); + } + + pub fn setUrgent(self: *Window, urgent: bool) !void { + self.x11_surface.setUrgencyHint(@intFromBool(urgent)); + } + + fn getWindowProperty( + self: *Window, + comptime T: type, + name: c.Atom, + typ: c.Atom, + comptime format: PropertyFormat, + options: struct { + offset: c_long = 0, + length: c_long = std.math.maxInt(c_long), + delete: bool = false, + }, + result: *T, + ) GetWindowPropertyError!void { + // FIXME: Maybe we should switch to libxcb one day. + // Sounds like a much better idea than whatever this is + var actual_type_return: c.Atom = undefined; + var actual_format_return: c_int = undefined; + var nitems_return: c_ulong = undefined; + var bytes_after_return: c_ulong = undefined; + var prop_return: ?format.bufferType() = null; + + const code = c.XGetWindowProperty( + @ptrCast(@alignCast(self.app.display)), + self.x11_surface.getXid(), + name, + options.offset, + options.length, + @intFromBool(options.delete), + typ, + &actual_type_return, + &actual_format_return, + &nitems_return, + &bytes_after_return, + @ptrCast(&prop_return), + ); + if (code != c.Success) return error.RequestFailed; + + if (actual_type_return == c.None) return error.PropertyNotFound; + if (typ != actual_type_return) return error.PropertyTypeMismatch; + if (@intFromEnum(format) != actual_format_return) return error.PropertyFormatMismatch; + + const data_ptr: *T = @ptrCast(prop_return); + result.* = data_ptr.*; + _ = c.XFree(prop_return); + } + + fn changeProperty( + self: *Window, + comptime T: type, + name: c.Atom, + typ: c.Atom, + comptime format: PropertyFormat, + options: struct { + mode: PropertyChangeMode, + }, + values: []const T, + ) X11Error!void { + const data: format.bufferType() = @ptrCast(@constCast(values)); + // The number of "words" that each element `T` occupies. + const words_per_elem = @divExact(@sizeOf(T), @sizeOf(format.elemType())); + + const status = c.XChangeProperty( + @ptrCast(@alignCast(self.app.display)), + self.x11_surface.getXid(), + name, + typ, + @intFromEnum(format), + @intFromEnum(options.mode), + data, + @intCast(words_per_elem * values.len), + ); + + // For some godforsaken reason Xlib alternates between + // error values (0 = success) and booleans (1 = success), and they look exactly + // the same in the signature (just `int`, since Xlib is written in C89)... + if (status == 0) return error.RequestFailed; + } + + fn deleteProperty(self: *Window, name: c.Atom) X11Error!void { + const status = c.XDeleteProperty( + @ptrCast(@alignCast(self.app.display)), + self.x11_surface.getXid(), + name, + ); + if (status == 0) return error.RequestFailed; + } +}; + +const X11Error = error{ + RequestFailed, +}; + +const GetWindowPropertyError = X11Error || error{ + PropertyNotFound, + PropertyTypeMismatch, + PropertyFormatMismatch, +}; + +const Atoms = struct { + kde_blur: c.Atom, + motif_wm_hints: c.Atom, + + fn init(display: *gdk_x11.X11Display) Atoms { + return .{ + .kde_blur = gdk_x11.x11GetXatomByNameForDisplay( + display, + "_KDE_NET_WM_BLUR_BEHIND_REGION", + ), + .motif_wm_hints = gdk_x11.x11GetXatomByNameForDisplay( + display, + "_MOTIF_WM_HINTS", + ), + }; + } +}; + +const PropertyChangeMode = enum(c_int) { + replace = c.PropModeReplace, + prepend = c.PropModePrepend, + append = c.PropModeAppend, +}; + +const PropertyFormat = enum(c_int) { + _8 = 8, + _16 = 16, + _32 = 32, + + fn elemType(comptime self: PropertyFormat) type { + return switch (self) { + ._8 => c_char, + ._16 => c_int, + ._32 => c_long, + }; + } + + fn bufferType(comptime self: PropertyFormat) type { + // The buffer type has to be a multi-pointer to bytes + // *aligned to the element type* (very important, + // otherwise you'll read garbage!) + // + // I know this is really ugly. X11 is ugly. I consider it apropos. + return [*]align(@alignOf(self.elemType())) u8; + } +}; + +// See Xm/MwmUtil.h, packaged with the Motif Window Manager +const MotifWMHints = extern struct { + flags: packed struct(c_ulong) { + _pad: u1 = 0, + decorations: bool = false, + + // We don't really care about the other flags + _rest: std.meta.Int(.unsigned, @bitSizeOf(c_ulong) - 2) = 0, + } = .{}, + functions: c_ulong = 0, + decorations: packed struct(c_ulong) { + all: bool = false, + + // We don't really care about the other flags + _rest: std.meta.Int(.unsigned, @bitSizeOf(c_ulong) - 1) = 0, + } = .{}, + input_mode: c_long = 0, + status: c_ulong = 0, +}; diff --git a/vendor/libghostty-vt/src/apprt/ipc.zig b/vendor/libghostty-vt/src/apprt/ipc.zig new file mode 100644 index 00000000..b37647e0 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/ipc.zig @@ -0,0 +1,192 @@ +//! Inter-process Communication to a running Ghostty instance from a separate +//! process. +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = @import("../quirks.zig").inlineAssert; +const lib = @import("../lib/main.zig"); + +pub const Errors = error{ + /// The IPC failed. If a function returns this error, it's expected that + /// an a more specific error message will have been written to stderr (or + /// otherwise shown to the user in an appropriate way). + IPCFailed, +}; + +pub const Target = union(Key) { + /// Open up a new window in a custom instance of Ghostty. + class: [:0]const u8, + + /// Detect which instance to open a new window in. + detect, + + // Sync with: ghostty_ipc_target_tag_e + pub const Key = enum(c_int) { + class, + detect, + + test "ghostty.h Target.Key" { + try lib.checkGhosttyHEnum(Key, "GHOSTTY_IPC_TARGET_"); + } + }; + + // Sync with: ghostty_ipc_target_u + pub const CValue = extern union { + class: [*:0]const u8, + detect: void, + }; + + // Sync with: ghostty_ipc_target_s + pub const C = extern struct { + key: Key, + value: CValue, + }; + + /// Convert to ghostty_ipc_target_s. + pub fn cval(self: Target) C { + return .{ + .key = @as(Key, self), + .value = switch (self) { + .class => |class| .{ .class = class.ptr }, + .detect => .{ .detect = {} }, + }, + }; + } +}; + +pub const Action = union(enum) { + // A GUIDE TO ADDING NEW ACTIONS: + // + // 1. Add the action to the `Key` enum. The order of the enum matters + // because it maps directly to the libghostty C enum. For ABI + // compatibility, new actions should be added to the end of the enum. + // + // 2. Add the action and optional value to the Action union. + // + // 3. If the value type is not void, ensure the value is C ABI + // compatible (extern). If it is not, add a `C` decl to the value + // and a `cval` function to convert to the C ABI compatible value. + // + // 4. Update `include/ghostty.h`: add the new key, value, and union + // entry. If the value type is void then only the key needs to be + // added. Ensure the order matches exactly with the Zig code. + + /// The arguments to pass to Ghostty as the command. + new_window: NewWindow, + + pub const NewWindow = struct { + /// A list of command arguments to launch in the new window. If this is + /// `null` the command configured in the config or the user's default + /// shell should be launched. + /// + /// It is an error for this to be non-`null`, but zero length. + arguments: ?[][:0]const u8, + + pub const C = extern struct { + /// null terminated list of arguments + /// it will be null itself if there are no arguments + arguments: ?[*]?[*:0]const u8, + + pub fn deinit(self: *NewWindow.C, alloc: Allocator) void { + if (self.arguments) |arguments| alloc.free(arguments); + } + }; + + pub fn cval(self: *NewWindow, alloc: Allocator) Allocator.Error!NewWindow.C { + var result: NewWindow.C = undefined; + + if (self.arguments) |arguments| { + result.arguments = try alloc.alloc([*:0]const u8, arguments.len + 1); + + for (arguments, 0..) |argument, i| + result.arguments[i] = argument.ptr; + + // add null terminator + result.arguments[arguments.len] = null; + } else { + result.arguments = null; + } + + return result; + } + }; + + /// Sync with: ghostty_ipc_action_tag_e + pub const Key = enum(c_int) { + new_window, + + test "ghostty.h Action.Key" { + try lib.checkGhosttyHEnum(Key, "GHOSTTY_IPC_ACTION_"); + } + }; + + /// Sync with: ghostty_ipc_action_u + pub const CValue = cvalue: { + const key_fields = @typeInfo(Key).@"enum".fields; + var union_fields: [key_fields.len]std.builtin.Type.UnionField = undefined; + for (key_fields, 0..) |field, i| { + const action = @unionInit(Action, field.name, undefined); + const Type = t: { + const Type = @TypeOf(@field(action, field.name)); + // Types can provide custom types for their CValue. + if (Type != void and @hasDecl(Type, "C")) break :t Type.C; + break :t Type; + }; + + union_fields[i] = .{ + .name = field.name, + .type = Type, + .alignment = @alignOf(Type), + }; + } + + break :cvalue @Type(.{ .@"union" = .{ + .layout = .@"extern", + .tag_type = null, + .fields = &union_fields, + .decls = &.{}, + } }); + }; + + /// Sync with: ghostty_ipc_action_s + pub const C = extern struct { + key: Key, + value: CValue, + }; + + comptime { + // For ABI compatibility, we expect that this is our union size. + // At the time of writing, we don't promise ABI compatibility + // so we can change this but I want to be aware of it. + assert(@sizeOf(CValue) == switch (@sizeOf(usize)) { + 4 => 4, + 8 => 8, + else => unreachable, + }); + } + + /// Returns the value type for the given key. + pub fn Value(comptime key: Key) type { + inline for (@typeInfo(Action).@"union".fields) |field| { + const field_key = @field(Key, field.name); + if (field_key == key) return field.type; + } + + unreachable; + } + + /// Convert to ghostty_ipc_action_s. + pub fn cval(self: Action, alloc: Allocator) C { + const value: CValue = switch (self) { + inline else => |v, tag| @unionInit( + CValue, + @tagName(tag), + if (@TypeOf(v) != void and @hasDecl(@TypeOf(v), "cval")) v.cval(alloc) else v, + ), + }; + + return .{ + .key = @as(Key, self), + .value = value, + }; + } +}; diff --git a/vendor/libghostty-vt/src/apprt/none.zig b/vendor/libghostty-vt/src/apprt/none.zig new file mode 100644 index 00000000..0483d824 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/none.zig @@ -0,0 +1,19 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const internal_os = @import("../os/main.zig"); +const apprt = @import("../apprt.zig"); +pub const resourcesDir = internal_os.resourcesDir; + +pub const App = struct { + /// Always return false as there is no apprt to communicate with. + pub fn performIpc( + _: Allocator, + _: apprt.ipc.Target, + comptime action: apprt.ipc.Action.Key, + _: apprt.ipc.Action.Value(action), + ) !bool { + return false; + } +}; +pub const Surface = struct {}; diff --git a/vendor/libghostty-vt/src/apprt/runtime.zig b/vendor/libghostty-vt/src/apprt/runtime.zig new file mode 100644 index 00000000..1be50353 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/runtime.zig @@ -0,0 +1,29 @@ +const std = @import("std"); + +/// Runtime is the runtime to use for Ghostty. All runtimes do not provide +/// equivalent feature sets. +pub const Runtime = enum { + /// Will not produce an executable at all when `zig build` is called. + /// This is only useful if you're only interested in the lib only (macOS). + none, + + /// GTK4. Rich windowed application. This uses a full GObject-based + /// approach to building the application. + gtk, + + pub fn default(target: std.Target) Runtime { + return switch (target.os.tag) { + // The Linux and FreeBSD default is GTK because it is a full + // featured application. + .linux, .freebsd => .gtk, + // Otherwise, we do NONE so we don't create an exe and we create + // libghostty. On macOS, Xcode is used to build the app that links + // to libghostty. + else => .none, + }; + } +}; + +test { + _ = Runtime; +} diff --git a/vendor/libghostty-vt/src/apprt/structs.zig b/vendor/libghostty-vt/src/apprt/structs.zig new file mode 100644 index 00000000..2c37dbd5 --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/structs.zig @@ -0,0 +1,114 @@ +const build_config = @import("../build_config.zig"); + +/// ContentScale is the ratio between the current DPI and the platform's +/// default DPI. This is used to determine how much certain rendered elements +/// need to be scaled up or down. +pub const ContentScale = struct { + x: f32, + y: f32, +}; + +/// The size of the surface in pixels. +pub const SurfaceSize = struct { + width: u32, + height: u32, + + pub fn eql(self: *const SurfaceSize, other: *const SurfaceSize) bool { + return self.width == other.width and self.height == other.height; + } +}; + +/// The position of the cursor in pixels. +pub const CursorPos = struct { + x: f32, + y: f32, +}; + +/// Input Method Editor (IME) position. +pub const IMEPos = struct { + x: f64, + y: f64, + width: f64, + height: f64, +}; + +/// The clipboard type. +/// +/// If this is changed, you must also update ghostty.h +pub const Clipboard = enum(Backing) { + standard = 0, // ctrl+c/v + selection = 1, + primary = 2, + + // Our backing isn't is as small as we can in Zig, but a full + // C int if we're binding to C APIs. + const Backing = switch (build_config.app_runtime) { + .gtk => c_int, + else => u2, + }; + + /// Make this a valid gobject if we're in a GTK environment. + pub const getGObjectType = switch (build_config.app_runtime) { + .gtk => @import("gobject").ext.defineEnum( + Clipboard, + .{ .name = "GhosttyApprtClipboard" }, + ), + + .none => void, + }; +}; + +pub const ClipboardContent = struct { + mime: [:0]const u8, + data: [:0]const u8, +}; + +pub const ClipboardRequestType = enum(u8) { + paste, + osc_52_read, + osc_52_write, +}; + +/// Clipboard request. This is used to request clipboard contents and must +/// be sent as a response to a ClipboardRequest event. +pub const ClipboardRequest = union(ClipboardRequestType) { + /// A direct paste of clipboard contents. + paste: void, + + /// A request to read clipboard contents via OSC 52. + osc_52_read: Clipboard, + + /// A request to write clipboard contents via OSC 52. + osc_52_write: Clipboard, + + /// Make this a valid gobject if we're in a GTK environment. + pub const getGObjectType = switch (build_config.app_runtime) { + .gtk => @import("gobject").ext.defineBoxed( + ClipboardRequest, + .{ .name = "GhosttyClipboardRequest" }, + ), + + .none => void, + }; +}; + +/// The color scheme in use (light vs dark). +pub const ColorScheme = enum(u2) { + light = 0, + dark = 1, +}; + +/// Selection information +pub const Selection = struct { + /// Top-left point of the selection in the viewport in scaled + /// window pixels. (0,0) is the top-left of the window. + tl_x_px: f64, + tl_y_px: f64, + + /// The offset of the selection start in cells from the top-left + /// of the viewport. + /// + /// This is a strange metric but its used by macOS. + offset_start: u32, + offset_len: u32, +}; diff --git a/vendor/libghostty-vt/src/apprt/surface.zig b/vendor/libghostty-vt/src/apprt/surface.zig new file mode 100644 index 00000000..3cb0016f --- /dev/null +++ b/vendor/libghostty-vt/src/apprt/surface.zig @@ -0,0 +1,197 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const apprt = @import("../apprt.zig"); +const build_config = @import("../build_config.zig"); +const App = @import("../App.zig"); +const Surface = @import("../Surface.zig"); +const renderer = @import("../renderer.zig"); +const terminal = @import("../terminal/main.zig"); +const Config = @import("../config.zig").Config; +const MessageData = @import("../datastruct/main.zig").MessageData; + +/// The message types that can be sent to a single surface. +pub const Message = union(enum) { + /// Represents a write request. Magic number comes from the max size + /// we want this union to be. + pub const WriteReq = MessageData(u8, 255); + + /// Set the title of the surface. + /// TODO: we should change this to a "WriteReq" style structure in + /// the termio message so that we can more efficiently send strings + /// of any length + set_title: [256]u8, + + /// Report the window title back to the terminal + report_title: ReportTitleStyle, + + /// Set the mouse shape. + set_mouse_shape: terminal.MouseShape, + + /// Read the clipboard and write to the pty. + clipboard_read: apprt.Clipboard, + + /// Write the clipboard contents. + clipboard_write: struct { + clipboard_type: apprt.Clipboard, + req: WriteReq, + }, + + /// Change the configuration to the given configuration. The pointer is + /// not valid after receiving this message so any config must be used + /// and derived immediately. + change_config: *const Config, + + /// Close the surface. This will only close the current surface that + /// receives this, not the full application. + close: void, + + /// The child process running in the surface has exited. This may trigger + /// a surface close, it may not. Additional details about the child + /// command are given in the `ChildExited` struct. + child_exited: ChildExited, + + /// Show a desktop notification. + desktop_notification: struct { + /// Desktop notification title. + title: [63:0]u8, + + /// Desktop notification body. + body: [255:0]u8, + }, + + /// Health status change for the renderer. + renderer_health: renderer.Health, + + /// Tell the surface to present itself to the user. This may require raising + /// a window and switching tabs. + present_surface: void, + + /// Notifies the surface that password input has started within + /// the terminal. This should always be followed by a false value + /// unless the surface exits. + password_input: bool, + + /// A terminal color was changed using OSC sequences. + color_change: terminal.osc.color.ColoredTarget, + + /// Notifies the surface that a tick of the timer that is timing + /// out selection scrolling has occurred. "selection scrolling" + /// is when the user has clicked and dragged the mouse outside + /// the viewport of the terminal and the terminal is scrolling + /// the viewport to follow the mouse cursor. + selection_scroll_tick: bool, + + /// The terminal has reported a change in the working directory. + pwd_change: WriteReq, + + /// The terminal encountered a bell character. + ring_bell, + + /// Report the progress of an action using a GUI element + progress_report: terminal.osc.Command.ProgressReport, + + /// A command has started in the shell, start a timer. + start_command, + + /// A command has finished in the shell, stop the timer and send out + /// notifications as appropriate. The optional u8 is the exit code + /// of the command. + stop_command: ?u8, + + /// The scrollbar state changed for the surface. + scrollbar: terminal.Scrollbar, + + /// Search progress update + search_total: ?usize, + + /// Selected search index change + search_selected: ?usize, + + pub const ReportTitleStyle = enum { + csi_21_t, + + // This enum is a placeholder for future title styles. + }; + + pub const ChildExited = extern struct { + exit_code: u32, + runtime_ms: u64, + + /// Make this a valid gobject if we're in a GTK environment. + pub const getGObjectType = switch (build_config.app_runtime) { + .gtk, + => @import("gobject").ext.defineBoxed( + ChildExited, + .{ .name = "GhosttyApprtChildExited" }, + ), + + .none => void, + }; + }; +}; + +/// A surface mailbox. +pub const Mailbox = struct { + surface: *Surface, + app: App.Mailbox, + + /// Send a message to the surface. + pub fn push( + self: Mailbox, + msg: Message, + timeout: App.Mailbox.Queue.Timeout, + ) App.Mailbox.Queue.Size { + // Surface message sending is actually implemented on the app + // thread, so we have to rewrap the message with our surface + // pointer and send it to the app thread. + return self.app.push(.{ + .surface_message = .{ + .surface = self.surface, + .message = msg, + }, + }, timeout); + } +}; + +/// Context for new surface creation to determine inheritance behavior +pub const NewSurfaceContext = enum(c_int) { + window = 0, + tab = 1, + split = 2, +}; + +pub fn shouldInheritWorkingDirectory(context: NewSurfaceContext, config: *const Config) bool { + return switch (context) { + .window => config.@"window-inherit-working-directory", + .tab => config.@"tab-inherit-working-directory", + .split => config.@"split-inherit-working-directory", + }; +} + +/// Returns a new config for a surface for the given app that should be +/// used for any new surfaces. The resulting config should be deinitialized +/// after the surface is initialized. +pub fn newConfig( + app: *const App, + config: *const Config, + context: NewSurfaceContext, +) Allocator.Error!Config { + // Create a shallow clone + var copy = config.shallowClone(app.alloc); + + // Our allocator is our config's arena + const alloc = copy._arena.?.allocator(); + + // Get our previously focused surface for some inherited values. + const prev = app.focusedSurface(); + if (prev) |p| { + if (shouldInheritWorkingDirectory(context, config)) { + if (try p.pwd(alloc)) |pwd| { + copy.@"working-directory" = .{ .path = pwd }; + } + } + } + + return copy; +} diff --git a/vendor/libghostty-vt/src/benchmark/Benchmark.zig b/vendor/libghostty-vt/src/benchmark/Benchmark.zig new file mode 100644 index 00000000..41c3695d --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/Benchmark.zig @@ -0,0 +1,178 @@ +//! A single benchmark case. +const Benchmark = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const macos = @import("macos"); +const build_config = @import("../build_config.zig"); + +ptr: *anyopaque, +vtable: VTable, + +/// Create a new benchmark from a pointer and a vtable. +/// +/// This usually is only called by benchmark implementations, not +/// benchmark users. +pub fn init( + pointer: anytype, + vtable: VTable, +) Benchmark { + const Ptr = @TypeOf(pointer); + assert(@typeInfo(Ptr) == .pointer); // Must be a pointer + assert(@typeInfo(Ptr).pointer.size == .one); // Must be a single-item pointer + assert(@typeInfo(@typeInfo(Ptr).pointer.child) == .@"struct"); // Must point to a struct + return .{ .ptr = pointer, .vtable = vtable }; +} + +/// Run the benchmark. +pub fn run( + self: Benchmark, + mode: RunMode, +) Error!RunResult { + // Run our setup function if it exists. We do this first because + // we don't want this part of our benchmark and we want to fail fast. + if (self.vtable.setupFn) |func| try func(self.ptr); + defer if (self.vtable.teardownFn) |func| func(self.ptr); + + // Our result accumulator. This will be returned at the end of the run. + var result: RunResult = .{}; + + // If we're on macOS, we setup signposts so its easier to find + // the results in Instruments. There's a lot of nasty comptime stuff + // here but its just to ensure this does nothing on other platforms. + const signpost_name = "ghostty"; + const signpost: if (builtin.target.os.tag.isDarwin()) struct { + log: *macos.os.Log, + id: macos.os.signpost.Id, + } else void = if (builtin.target.os.tag.isDarwin()) darwin: { + macos.os.signpost.init(); + const log = macos.os.Log.create( + build_config.bundle_id, + macos.os.signpost.Category.points_of_interest, + ); + const id = macos.os.signpost.Id.forPointer(log, self.ptr); + macos.os.signpost.intervalBegin(log, id, signpost_name); + break :darwin .{ .log = log, .id = id }; + } else {}; + defer if (comptime builtin.target.os.tag.isDarwin()) { + macos.os.signpost.intervalEnd( + signpost.log, + signpost.id, + signpost_name, + ); + signpost.log.release(); + }; + + const start = std.time.Instant.now() catch return error.BenchmarkFailed; + while (true) { + // Run our step function. If it fails, we return the error. + try self.vtable.stepFn(self.ptr); + result.iterations += 1; + + // Get our current monotonic time and check our exit conditions. + const now = std.time.Instant.now() catch return error.BenchmarkFailed; + const exit = switch (mode) { + .once => true, + .duration => |ns| now.since(start) >= ns, + }; + + if (exit) { + result.duration = now.since(start); + return result; + } + } + + // We exit within the loop body. + unreachable; +} + +/// The type of benchmark run. This is used to determine how the benchmark +/// is executed. +pub const RunMode = union(enum) { + /// Run the benchmark exactly once. + once, + + /// Run the benchmark for a fixed duration in nanoseconds. This + /// will not interrupt a running step so if the granularity of the + /// duration is too low, benchmark results may be inaccurate. + duration: u64, +}; + +/// The result of a benchmark run. +pub const RunResult = struct { + /// The total iterations that step was executed. For "once" run + /// modes this will always be 1. + iterations: u32 = 0, + + /// The total time taken for the run. For "duration" run modes + /// this will be relatively close to the requested duration. + /// The units are nanoseconds. + duration: u64 = 0, +}; + +/// The possible errors that can occur during various stages of the +/// benchmark. Right now its just "failure" which ends the benchmark. +pub const Error = error{BenchmarkFailed}; + +/// The vtable that must be provided to invoke the real implementation. +pub const VTable = struct { + /// A single step to execute the benchmark. This should do the work + /// that is under test. This may be called multiple times if we're + /// testing throughput. + stepFn: *const fn (ptr: *anyopaque) Error!void, + + /// Setup and teardown functions. These are called once before + /// the first step and once after the last step. They are not part + /// of the benchmark results (unless you're benchmarking the full + /// binary). + setupFn: ?*const fn (ptr: *anyopaque) Error!void = null, + teardownFn: ?*const fn (ptr: *anyopaque) void = null, +}; + +test Benchmark { + // This test fails on FreeBSD and Windows so skip: + // + // /home/runner/work/ghostty/ghostty/src/benchmark/Benchmark.zig:165:5: 0x3cd2de1 in decltest.Benchmark (ghostty-test) + // try testing.expect(result.duration > 0); + // ^ + switch (builtin.os.tag) { + .freebsd, + .windows, + => return error.SkipZigTest, + else => {}, + } + + const testing = std.testing; + const Simple = struct { + const Self = @This(); + + setup_i: usize = 0, + step_i: usize = 0, + + pub fn benchmark(self: *Self) Benchmark { + return .init(self, .{ + .stepFn = step, + .setupFn = setup, + }); + } + + fn setup(ptr: *anyopaque) Error!void { + const self: *Self = @ptrCast(@alignCast(ptr)); + self.setup_i += 1; + } + + fn step(ptr: *anyopaque) Error!void { + const self: *Self = @ptrCast(@alignCast(ptr)); + self.step_i += 1; + } + }; + + var s: Simple = .{}; + const b = s.benchmark(); + const result = try b.run(.once); + try testing.expectEqual(1, s.setup_i); + try testing.expectEqual(1, s.step_i); + try testing.expectEqual(1, result.iterations); + try testing.expect(result.duration > 0); +} diff --git a/vendor/libghostty-vt/src/benchmark/CApi.zig b/vendor/libghostty-vt/src/benchmark/CApi.zig new file mode 100644 index 00000000..3bef8b26 --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/CApi.zig @@ -0,0 +1,34 @@ +const std = @import("std"); +const cli = @import("cli.zig"); +const state = &@import("../global.zig").state; + +const log = std.log.scoped(.benchmark); + +/// Run the Ghostty benchmark CLI with the given action and arguments. +export fn ghostty_benchmark_cli( + action_name_: [*:0]const u8, + args: [*:0]const u8, +) bool { + const action_name = std.mem.sliceTo(action_name_, 0); + const action: cli.Action = std.meta.stringToEnum( + cli.Action, + action_name, + ) orelse { + log.warn("unknown action={s}", .{action_name}); + return false; + }; + + cli.mainAction( + state.alloc, + action, + .{ .string = std.mem.sliceTo(args, 0) }, + ) catch |err| { + log.warn("failed to run action={s} err={}", .{ + @tagName(action), + err, + }); + return false; + }; + + return true; +} diff --git a/vendor/libghostty-vt/src/benchmark/CodepointWidth.zig b/vendor/libghostty-vt/src/benchmark/CodepointWidth.zig new file mode 100644 index 00000000..30d3f91e --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/CodepointWidth.zig @@ -0,0 +1,207 @@ +//! This benchmark tests the throughput of codepoint width calculation. +//! This is a common operation in terminal character printing and the +//! motivating factor to write this benchmark was discovering that our +//! codepoint width function was 30% of the runtime of every character +//! print. +const CodepointWidth = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Benchmark = @import("Benchmark.zig"); +const options = @import("options.zig"); +const UTF8Decoder = @import("../terminal/UTF8Decoder.zig"); +const simd = @import("../simd/main.zig"); +const table = @import("../unicode/main.zig").table; + +const log = std.log.scoped(.@"terminal-stream-bench"); + +opts: Options, + +/// The file, opened in the setup function. +data_f: ?std.fs.File = null, + +pub const Options = struct { + /// The type of codepoint width calculation to use. + mode: Mode = .noop, + + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + data: ?[]const u8 = null, +}; + +pub const Mode = enum { + /// The baseline mode copies the data from the fd into a buffer. This + /// is used to show the minimal overhead of reading the fd into memory + /// and establishes a baseline for the other modes. + noop, + + /// libc wcwidth + wcwidth, + + /// Our SIMD implementation. + simd, + + /// Test our lookup table implementation. + table, +}; + +/// Create a new terminal stream handler for the given arguments. +pub fn create( + alloc: Allocator, + opts: Options, +) !*CodepointWidth { + const ptr = try alloc.create(CodepointWidth); + errdefer alloc.destroy(ptr); + ptr.* = .{ .opts = opts }; + return ptr; +} + +pub fn destroy(self: *CodepointWidth, alloc: Allocator) void { + alloc.destroy(self); +} + +pub fn benchmark(self: *CodepointWidth) Benchmark { + return .init(self, .{ + .stepFn = switch (self.opts.mode) { + .noop => stepNoop, + .wcwidth => stepWcwidth, + .table => stepTable, + .simd => stepSimd, + }, + .setupFn = setup, + .teardownFn = teardown, + }); +} + +fn setup(ptr: *anyopaque) Benchmark.Error!void { + const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); + + // Open our data file to prepare for reading. We can do more + // validation here eventually. + assert(self.data_f == null); + self.data_f = options.dataFile(self.opts.data) catch |err| { + log.warn("error opening data file err={}", .{err}); + return error.BenchmarkFailed; + }; +} + +fn teardown(ptr: *anyopaque) void { + const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); + if (self.data_f) |f| { + f.close(); + self.data_f = null; + } +} + +fn stepNoop(ptr: *anyopaque) Benchmark.Error!void { + _ = ptr; +} + +extern "c" fn wcwidth(c: u32) c_int; + +fn stepWcwidth(ptr: *anyopaque) Benchmark.Error!void { + if (comptime builtin.os.tag == .windows) { + log.warn("wcwidth is not available on Windows", .{}); + return; + } + + const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var d: UTF8Decoder = .{}; + var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + + for (buf[0..n]) |c| { + const cp_, const consumed = d.next(c); + assert(consumed); + if (cp_) |cp| { + std.mem.doNotOptimizeAway(wcwidth(cp)); + } + } + } +} + +fn stepTable(ptr: *anyopaque) Benchmark.Error!void { + const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var d: UTF8Decoder = .{}; + var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + + for (buf[0..n]) |c| { + const cp_, const consumed = d.next(c); + assert(consumed); + if (cp_) |cp| { + // This is the same trick we do in terminal.zig so we + // keep it here. + std.mem.doNotOptimizeAway(if (cp <= 0xFF) + 1 + else + table.get(@intCast(cp)).width); + } + } + } +} + +fn stepSimd(ptr: *anyopaque) Benchmark.Error!void { + const self: *CodepointWidth = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var d: UTF8Decoder = .{}; + var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + + for (buf[0..n]) |c| { + const cp_, const consumed = d.next(c); + assert(consumed); + if (cp_) |cp| { + std.mem.doNotOptimizeAway(simd.codepointWidth(cp)); + } + } + } +} + +test CodepointWidth { + const testing = std.testing; + const alloc = testing.allocator; + + const impl: *CodepointWidth = try .create(alloc, .{}); + defer impl.destroy(alloc); + + const bench = impl.benchmark(); + _ = try bench.run(.once); +} diff --git a/vendor/libghostty-vt/src/benchmark/GraphemeBreak.zig b/vendor/libghostty-vt/src/benchmark/GraphemeBreak.zig new file mode 100644 index 00000000..8278c5c2 --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/GraphemeBreak.zig @@ -0,0 +1,152 @@ +//! This benchmark tests the throughput of grapheme break calculation. +//! This is a common operation in terminal character printing for terminals +//! that support grapheme clustering. +const GraphemeBreak = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Benchmark = @import("Benchmark.zig"); +const options = @import("options.zig"); +const UTF8Decoder = @import("../terminal/UTF8Decoder.zig"); +const unicode = @import("../unicode/main.zig"); +const uucode = @import("uucode"); + +const log = std.log.scoped(.@"terminal-stream-bench"); + +opts: Options, + +/// The file, opened in the setup function. +data_f: ?std.fs.File = null, + +pub const Options = struct { + /// The type of codepoint width calculation to use. + mode: Mode = .table, + + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + data: ?[]const u8 = null, +}; + +pub const Mode = enum { + /// The baseline mode copies the data from the fd into a buffer. This + /// is used to show the minimal overhead of reading the fd into memory + /// and establishes a baseline for the other modes. + noop, + + /// Ghostty's table-based approach. + table, +}; + +/// Create a new terminal stream handler for the given arguments. +pub fn create( + alloc: Allocator, + opts: Options, +) !*GraphemeBreak { + const ptr = try alloc.create(GraphemeBreak); + errdefer alloc.destroy(ptr); + ptr.* = .{ .opts = opts }; + return ptr; +} + +pub fn destroy(self: *GraphemeBreak, alloc: Allocator) void { + alloc.destroy(self); +} + +pub fn benchmark(self: *GraphemeBreak) Benchmark { + return .init(self, .{ + .stepFn = switch (self.opts.mode) { + .noop => stepNoop, + .table => stepTable, + }, + .setupFn = setup, + .teardownFn = teardown, + }); +} + +fn setup(ptr: *anyopaque) Benchmark.Error!void { + const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); + + // Open our data file to prepare for reading. We can do more + // validation here eventually. + assert(self.data_f == null); + self.data_f = options.dataFile(self.opts.data) catch |err| { + log.warn("error opening data file err={}", .{err}); + return error.BenchmarkFailed; + }; +} + +fn teardown(ptr: *anyopaque) void { + const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); + if (self.data_f) |f| { + f.close(); + self.data_f = null; + } +} + +fn stepNoop(ptr: *anyopaque) Benchmark.Error!void { + const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var d: UTF8Decoder = .{}; + var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + + for (buf[0..n]) |c| { + _ = d.next(c); + } + } +} + +fn stepTable(ptr: *anyopaque) Benchmark.Error!void { + const self: *GraphemeBreak = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var d: UTF8Decoder = .{}; + var state: uucode.grapheme.BreakState = .default; + var cp1: u21 = 0; + var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + + for (buf[0..n]) |c| { + const cp_, const consumed = d.next(c); + assert(consumed); + if (cp_) |cp2| { + std.mem.doNotOptimizeAway(unicode.graphemeBreak(cp1, @intCast(cp2), &state)); + cp1 = cp2; + } + } + } +} + +test GraphemeBreak { + const testing = std.testing; + const alloc = testing.allocator; + + const impl: *GraphemeBreak = try .create(alloc, .{}); + defer impl.destroy(alloc); + + const bench = impl.benchmark(); + _ = try bench.run(.once); +} diff --git a/vendor/libghostty-vt/src/benchmark/IsSymbol.zig b/vendor/libghostty-vt/src/benchmark/IsSymbol.zig new file mode 100644 index 00000000..4fbffd1e --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/IsSymbol.zig @@ -0,0 +1,151 @@ +//! This benchmark tests the throughput of grapheme break calculation. +//! This is a common operation in terminal character printing for terminals +//! that support grapheme clustering. +const IsSymbol = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Benchmark = @import("Benchmark.zig"); +const options = @import("options.zig"); +const UTF8Decoder = @import("../terminal/UTF8Decoder.zig"); +const uucode = @import("uucode"); +const symbols_table = @import("../unicode/symbols_table.zig").table; + +const log = std.log.scoped(.@"is-symbol-bench"); + +opts: Options, + +/// The file, opened in the setup function. +data_f: ?std.fs.File = null, + +pub const Options = struct { + /// Which test to run. + mode: Mode = .uucode, + + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + data: ?[]const u8 = null, +}; + +pub const Mode = enum { + /// uucode implementation + uucode, + + /// Ghostty's table-based approach. + table, +}; + +/// Create a new terminal stream handler for the given arguments. +pub fn create( + alloc: Allocator, + opts: Options, +) !*IsSymbol { + const ptr = try alloc.create(IsSymbol); + errdefer alloc.destroy(ptr); + ptr.* = .{ .opts = opts }; + return ptr; +} + +pub fn destroy(self: *IsSymbol, alloc: Allocator) void { + alloc.destroy(self); +} + +pub fn benchmark(self: *IsSymbol) Benchmark { + return .init(self, .{ + .stepFn = switch (self.opts.mode) { + .uucode => stepUucode, + .table => stepTable, + }, + .setupFn = setup, + .teardownFn = teardown, + }); +} + +fn setup(ptr: *anyopaque) Benchmark.Error!void { + const self: *IsSymbol = @ptrCast(@alignCast(ptr)); + + // Open our data file to prepare for reading. We can do more + // validation here eventually. + assert(self.data_f == null); + self.data_f = options.dataFile(self.opts.data) catch |err| { + log.warn("error opening data file err={}", .{err}); + return error.BenchmarkFailed; + }; +} + +fn teardown(ptr: *anyopaque) void { + const self: *IsSymbol = @ptrCast(@alignCast(ptr)); + if (self.data_f) |f| { + f.close(); + self.data_f = null; + } +} + +fn stepUucode(ptr: *anyopaque) Benchmark.Error!void { + const self: *IsSymbol = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var d: UTF8Decoder = .{}; + var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + + for (buf[0..n]) |c| { + const cp_, const consumed = d.next(c); + assert(consumed); + if (cp_) |cp| { + std.mem.doNotOptimizeAway(uucode.get(.is_symbol, cp)); + } + } + } +} + +fn stepTable(ptr: *anyopaque) Benchmark.Error!void { + const self: *IsSymbol = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var d: UTF8Decoder = .{}; + var buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + + for (buf[0..n]) |c| { + const cp_, const consumed = d.next(c); + assert(consumed); + if (cp_) |cp| { + std.mem.doNotOptimizeAway(symbols_table.get(cp)); + } + } + } +} + +test IsSymbol { + const testing = std.testing; + const alloc = testing.allocator; + + const impl: *IsSymbol = try .create(alloc, .{}); + defer impl.destroy(alloc); + + const bench = impl.benchmark(); + _ = try bench.run(.once); +} diff --git a/vendor/libghostty-vt/src/benchmark/OscParser.zig b/vendor/libghostty-vt/src/benchmark/OscParser.zig new file mode 100644 index 00000000..d4b416de --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/OscParser.zig @@ -0,0 +1,118 @@ +//! This benchmark tests the throughput of the OSC parser. +const OscParser = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Benchmark = @import("Benchmark.zig"); +const options = @import("options.zig"); +const Parser = @import("../terminal/osc.zig").Parser; +const log = std.log.scoped(.@"osc-parser-bench"); + +opts: Options, + +/// The file, opened in the setup function. +data_f: ?std.fs.File = null, + +parser: Parser, + +pub const Options = struct { + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + data: ?[]const u8 = null, +}; + +/// Create a new terminal stream handler for the given arguments. +pub fn create( + alloc: Allocator, + opts: Options, +) !*OscParser { + const ptr = try alloc.create(OscParser); + errdefer alloc.destroy(ptr); + ptr.* = .{ + .opts = opts, + .data_f = null, + .parser = .init(alloc), + }; + return ptr; +} + +pub fn destroy(self: *OscParser, alloc: Allocator) void { + self.parser.deinit(); + alloc.destroy(self); +} + +pub fn benchmark(self: *OscParser) Benchmark { + return .init(self, .{ + .stepFn = step, + .setupFn = setup, + .teardownFn = teardown, + }); +} + +fn setup(ptr: *anyopaque) Benchmark.Error!void { + const self: *OscParser = @ptrCast(@alignCast(ptr)); + + // Open our data file to prepare for reading. We can do more + // validation here eventually. + assert(self.data_f == null); + self.data_f = options.dataFile(self.opts.data) catch |err| { + log.warn("error opening data file err={}", .{err}); + return error.BenchmarkFailed; + }; + self.parser.reset(); +} + +fn teardown(ptr: *anyopaque) void { + const self: *OscParser = @ptrCast(@alignCast(ptr)); + if (self.data_f) |f| { + f.close(); + self.data_f = null; + } +} + +fn step(ptr: *anyopaque) Benchmark.Error!void { + const self: *OscParser = @ptrCast(@alignCast(ptr)); + + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var r = f.reader(&read_buf); + + var osc_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + while (true) { + r.interface.fill(@bitSizeOf(usize) / 8) catch |err| switch (err) { + error.EndOfStream => return, + error.ReadFailed => return error.BenchmarkFailed, + }; + const len = r.interface.takeInt(usize, .little) catch |err| switch (err) { + error.EndOfStream => return, + error.ReadFailed => return error.BenchmarkFailed, + }; + + if (len > osc_buf.len) return error.BenchmarkFailed; + + r.interface.readSliceAll(osc_buf[0..len]) catch |err| switch (err) { + error.EndOfStream => return, + error.ReadFailed => return error.BenchmarkFailed, + }; + + for (osc_buf[0..len]) |c| @call(.always_inline, Parser.next, .{ &self.parser, c }); + std.mem.doNotOptimizeAway(self.parser.end(std.ascii.control_code.bel)); + self.parser.reset(); + } +} + +test OscParser { + const testing = std.testing; + const alloc = testing.allocator; + + const impl: *OscParser = try .create(alloc, .{}); + defer impl.destroy(alloc); + + const bench = impl.benchmark(); + _ = try bench.run(.once); +} diff --git a/vendor/libghostty-vt/src/benchmark/ScreenClone.zig b/vendor/libghostty-vt/src/benchmark/ScreenClone.zig new file mode 100644 index 00000000..108eaa0c --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/ScreenClone.zig @@ -0,0 +1,193 @@ +//! This benchmark tests the performance of the Screen.clone +//! function. This is useful because it is one of the primary lock +//! holders that impact IO performance when the renderer is active. +//! We do this very frequently. +const ScreenClone = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const terminalpkg = @import("../terminal/main.zig"); +const Benchmark = @import("Benchmark.zig"); +const options = @import("options.zig"); +const Terminal = terminalpkg.Terminal; + +const log = std.log.scoped(.@"terminal-stream-bench"); + +opts: Options, +terminal: Terminal, + +pub const Options = struct { + /// The type of codepoint width calculation to use. + mode: Mode = .clone, + + /// The size of the terminal. This affects benchmarking when + /// dealing with soft line wrapping and the memory impact + /// of page sizes. + @"terminal-rows": u16 = 80, + @"terminal-cols": u16 = 120, + + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + /// + /// This will be used to initialize the terminal screen state before + /// cloning. This data can switch to alt screen if it wants. The time + /// to read this is not part of the benchmark. + data: ?[]const u8 = null, +}; + +pub const Mode = enum { + /// The baseline mode copies the screen by value. + noop, + + /// Full clone + clone, + + /// RenderState rather than a screen clone. + render, +}; + +pub fn create( + alloc: Allocator, + opts: Options, +) !*ScreenClone { + const ptr = try alloc.create(ScreenClone); + errdefer alloc.destroy(ptr); + + ptr.* = .{ + .opts = opts, + .terminal = try .init(alloc, .{ + .rows = opts.@"terminal-rows", + .cols = opts.@"terminal-cols", + }), + }; + + return ptr; +} + +pub fn destroy(self: *ScreenClone, alloc: Allocator) void { + self.terminal.deinit(alloc); + alloc.destroy(self); +} + +pub fn benchmark(self: *ScreenClone) Benchmark { + return .init(self, .{ + .stepFn = switch (self.opts.mode) { + .noop => stepNoop, + .clone => stepClone, + .render => stepRender, + }, + .setupFn = setup, + .teardownFn = teardown, + }); +} + +fn setup(ptr: *anyopaque) Benchmark.Error!void { + const self: *ScreenClone = @ptrCast(@alignCast(ptr)); + + // Always reset our terminal state + self.terminal.fullReset(); + + // Force a style on every single row, which + var s = self.terminal.vtStream(); + defer s.deinit(); + s.nextSlice("\x1b[48;2;20;40;60m"); + for (0..self.terminal.rows - 1) |_| s.nextSlice("hello\r\n"); + s.nextSlice("hello"); + + // Setup our terminal state + const data_f: std.fs.File = (options.dataFile( + self.opts.data, + ) catch |err| { + log.warn("error opening data file err={}", .{err}); + return error.BenchmarkFailed; + }) orelse return; + + var stream = self.terminal.vtStream(); + defer stream.deinit(); + + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = data_f.reader(&read_buf); + const r = &f_reader.interface; + + var buf: [4096]u8 = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + stream.nextSlice(buf[0..n]); + } +} + +fn teardown(ptr: *anyopaque) void { + const self: *ScreenClone = @ptrCast(@alignCast(ptr)); + _ = self; +} + +fn stepNoop(ptr: *anyopaque) Benchmark.Error!void { + const self: *ScreenClone = @ptrCast(@alignCast(ptr)); + + // We loop because its so fast that a single benchmark run doesn't + // properly capture our speeds. + for (0..1000) |_| { + const s: terminalpkg.Screen = self.terminal.screens.active.*; + std.mem.doNotOptimizeAway(s); + } +} + +fn stepClone(ptr: *anyopaque) Benchmark.Error!void { + const self: *ScreenClone = @ptrCast(@alignCast(ptr)); + + // We loop because its so fast that a single benchmark run doesn't + // properly capture our speeds. + for (0..1000) |_| { + const s: *terminalpkg.Screen = self.terminal.screens.active; + const copy = s.clone( + s.alloc, + .{ .viewport = .{} }, + null, + ) catch |err| { + log.warn("error cloning screen err={}", .{err}); + return error.BenchmarkFailed; + }; + std.mem.doNotOptimizeAway(copy); + + // Note: we purposely do not free memory because we don't want + // to benchmark that. We'll free when the benchmark exits. + } +} + +fn stepRender(ptr: *anyopaque) Benchmark.Error!void { + const self: *ScreenClone = @ptrCast(@alignCast(ptr)); + + // We do this once out of the loop because a significant slowdown + // on the first run is allocation. After that first run, even with + // a full rebuild, it is much faster. Let's ignore that first run + // slowdown. + const alloc = self.terminal.screens.active.alloc; + var state: terminalpkg.RenderState = .empty; + state.update(alloc, &self.terminal) catch |err| { + log.warn("error cloning screen err={}", .{err}); + return error.BenchmarkFailed; + }; + + // We loop because its so fast that a single benchmark run doesn't + // properly capture our speeds. + for (0..1000) |_| { + // Forces a full rebuild because it thinks our screen changed + state.screen = .alternate; + state.update(alloc, &self.terminal) catch |err| { + log.warn("error cloning screen err={}", .{err}); + return error.BenchmarkFailed; + }; + std.mem.doNotOptimizeAway(state); + + // Note: we purposely do not free memory because we don't want + // to benchmark that. We'll free when the benchmark exits. + } +} diff --git a/vendor/libghostty-vt/src/benchmark/TerminalParser.zig b/vendor/libghostty-vt/src/benchmark/TerminalParser.zig new file mode 100644 index 00000000..e0008176 --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/TerminalParser.zig @@ -0,0 +1,108 @@ +//! This benchmark tests the throughput of the terminal escape code parser. +const TerminalParser = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const terminalpkg = @import("../terminal/main.zig"); +const Benchmark = @import("Benchmark.zig"); +const options = @import("options.zig"); + +const log = std.log.scoped(.@"terminal-stream-bench"); + +opts: Options, + +/// The file, opened in the setup function. +data_f: ?std.fs.File = null, + +pub const Options = struct { + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + data: ?[]const u8 = null, +}; + +pub fn create( + alloc: Allocator, + opts: Options, +) !*TerminalParser { + const ptr = try alloc.create(TerminalParser); + errdefer alloc.destroy(ptr); + ptr.* = .{ .opts = opts }; + return ptr; +} + +pub fn destroy(self: *TerminalParser, alloc: Allocator) void { + alloc.destroy(self); +} + +pub fn benchmark(self: *TerminalParser) Benchmark { + return .init(self, .{ + .stepFn = step, + .setupFn = setup, + .teardownFn = teardown, + }); +} + +fn setup(ptr: *anyopaque) Benchmark.Error!void { + const self: *TerminalParser = @ptrCast(@alignCast(ptr)); + + // Open our data file to prepare for reading. We can do more + // validation here eventually. + assert(self.data_f == null); + self.data_f = options.dataFile(self.opts.data) catch |err| { + log.warn("error opening data file err={}", .{err}); + return error.BenchmarkFailed; + }; +} + +fn teardown(ptr: *anyopaque) void { + const self: *TerminalParser = @ptrCast(@alignCast(ptr)); + if (self.data_f) |f| { + f.close(); + self.data_f = null; + } +} + +fn step(ptr: *anyopaque) Benchmark.Error!void { + const self: *TerminalParser = @ptrCast(@alignCast(ptr)); + + // Get our buffered reader so we're not predominantly + // waiting on file IO. It'd be better to move this fully into + // memory. If we're IO bound though that should show up on + // the benchmark results and... I know writing this that we + // aren't currently IO bound. + const f = self.data_f orelse return; + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + var r = &f_reader.interface; + + var p: terminalpkg.Parser = .init(); + + var buf: [4096]u8 = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + for (buf[0..n]) |c| { + const actions = p.next(c); + //std.log.warn("actions={any}", .{actions}); + _ = actions; + } + } +} + +test TerminalParser { + const testing = std.testing; + const alloc = testing.allocator; + + const impl: *TerminalParser = try .create(alloc, .{}); + defer impl.destroy(alloc); + + const bench = impl.benchmark(); + _ = try bench.run(.once); +} diff --git a/vendor/libghostty-vt/src/benchmark/TerminalStream.zig b/vendor/libghostty-vt/src/benchmark/TerminalStream.zig new file mode 100644 index 00000000..1cac656e --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/TerminalStream.zig @@ -0,0 +1,161 @@ +//! This benchmark tests the performance of the terminal stream +//! handler from input to terminal state update. This is useful to +//! test general throughput of VT parsing and handling. +//! +//! Note that the handler used for this benchmark isn't the full +//! terminal handler, since that requires a significant amount of +//! state. This is a simplified version that only handles specific +//! terminal operations like printing characters. We should expand +//! this to include more operations to improve the accuracy of the +//! benchmark. +//! +//! It is a fairly broad benchmark that can be used to determine +//! if we need to optimize something more specific (e.g. the parser). +const TerminalStream = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const terminalpkg = @import("../terminal/main.zig"); +const Benchmark = @import("Benchmark.zig"); +const options = @import("options.zig"); +const Terminal = terminalpkg.Terminal; +const Stream = terminalpkg.Stream(*Handler); + +const log = std.log.scoped(.@"terminal-stream-bench"); + +opts: Options, +terminal: Terminal, +handler: Handler, +stream: Stream, + +/// The file, opened in the setup function. +data_f: ?std.fs.File = null, + +pub const Options = struct { + /// The size of the terminal. This affects benchmarking when + /// dealing with soft line wrapping and the memory impact + /// of page sizes. + @"terminal-rows": u16 = 80, + @"terminal-cols": u16 = 120, + + /// The data to read as a filepath. If this is "-" then + /// we will read stdin. If this is unset, then we will + /// do nothing (benchmark is a noop). It'd be more unixy to + /// use stdin by default but I find that a hanging CLI command + /// with no interaction is a bit annoying. + data: ?[]const u8 = null, +}; + +/// Create a new terminal stream handler for the given arguments. +pub fn create( + alloc: Allocator, + opts: Options, +) !*TerminalStream { + const ptr = try alloc.create(TerminalStream); + errdefer alloc.destroy(ptr); + + ptr.* = .{ + .opts = opts, + .terminal = try .init(alloc, .{ + .rows = opts.@"terminal-rows", + .cols = opts.@"terminal-cols", + }), + .handler = .{ .t = &ptr.terminal }, + .stream = .init(&ptr.handler), + }; + + return ptr; +} + +pub fn destroy(self: *TerminalStream, alloc: Allocator) void { + self.terminal.deinit(alloc); + alloc.destroy(self); +} + +pub fn benchmark(self: *TerminalStream) Benchmark { + return .init(self, .{ + .stepFn = step, + .setupFn = setup, + .teardownFn = teardown, + }); +} + +fn setup(ptr: *anyopaque) Benchmark.Error!void { + const self: *TerminalStream = @ptrCast(@alignCast(ptr)); + + // Always reset our terminal state + self.terminal.fullReset(); + + // Open our data file to prepare for reading. We can do more + // validation here eventually. + assert(self.data_f == null); + self.data_f = options.dataFile(self.opts.data) catch |err| { + log.warn("error opening data file err={}", .{err}); + return error.BenchmarkFailed; + }; +} + +fn teardown(ptr: *anyopaque) void { + const self: *TerminalStream = @ptrCast(@alignCast(ptr)); + if (self.data_f) |f| { + f.close(); + self.data_f = null; + } +} + +fn step(ptr: *anyopaque) Benchmark.Error!void { + const self: *TerminalStream = @ptrCast(@alignCast(ptr)); + + // Get our buffered reader so we're not predominantly + // waiting on file IO. It'd be better to move this fully into + // memory. If we're IO bound though that should show up on + // the benchmark results and... I know writing this that we + // aren't currently IO bound. + const f = self.data_f orelse return; + + var read_buf: [4096]u8 align(std.atomic.cache_line) = undefined; + var f_reader = f.reader(&read_buf); + const r = &f_reader.interface; + + var buf: [4096]u8 = undefined; + while (true) { + const n = r.readSliceShort(&buf) catch { + log.warn("error reading data file err={?}", .{f_reader.err}); + return error.BenchmarkFailed; + }; + if (n == 0) break; // EOF reached + self.stream.nextSlice(buf[0..n]); + } +} + +/// Implements the handler interface for the terminal.Stream. +/// We should expand this to include more operations to make +/// our benchmark more realistic. +const Handler = struct { + t: *Terminal, + + pub fn vt( + self: *Handler, + comptime action: Stream.Action.Tag, + value: Stream.Action.Value(action), + ) void { + switch (action) { + .print => self.t.print(value.cp) catch |err| { + log.warn("error processing benchmark print err={}", .{err}); + }, + else => {}, + } + } +}; + +test TerminalStream { + const testing = std.testing; + const alloc = testing.allocator; + + const impl: *TerminalStream = try .create(alloc, .{}); + defer impl.destroy(alloc); + + const bench = impl.benchmark(); + _ = try bench.run(.once); +} diff --git a/vendor/libghostty-vt/src/benchmark/cli.zig b/vendor/libghostty-vt/src/benchmark/cli.zig new file mode 100644 index 00000000..13f07077 --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/cli.zig @@ -0,0 +1,100 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const cli = @import("../cli.zig"); + +/// The available actions for the CLI. This is the list of available +/// benchmarks. View docs for each individual one in the predictably +/// named files. +pub const Action = enum { + @"codepoint-width", + @"grapheme-break", + @"screen-clone", + @"terminal-parser", + @"terminal-stream", + @"is-symbol", + @"osc-parser", + + /// Returns the struct associated with the action. The struct + /// should have a few decls: + /// + /// - `const Options`: The CLI options for the action. + /// - `fn create`: Create a new instance of the action from options. + /// - `fn benchmark`: Returns a `Benchmark` instance for the action. + /// + /// See TerminalStream for an example. + pub fn Struct(comptime action: Action) type { + return switch (action) { + .@"screen-clone" => @import("ScreenClone.zig"), + .@"terminal-stream" => @import("TerminalStream.zig"), + .@"codepoint-width" => @import("CodepointWidth.zig"), + .@"grapheme-break" => @import("GraphemeBreak.zig"), + .@"terminal-parser" => @import("TerminalParser.zig"), + .@"is-symbol" => @import("IsSymbol.zig"), + .@"osc-parser" => @import("OscParser.zig"), + }; + } +}; + +/// An entrypoint for the benchmark CLI. +pub fn main() !void { + const alloc = std.heap.c_allocator; + const action_ = try cli.action.detectArgs(Action, alloc); + const action = action_ orelse return error.NoAction; + try mainAction(alloc, action, .cli); +} + +/// Arguments that can be passed to the benchmark. +pub const Args = union(enum) { + /// The arguments passed to the CLI via argc/argv. + cli, + + /// Simple string arguments, parsed via std.process.ArgIteratorGeneral. + string: []const u8, +}; + +pub fn mainAction( + alloc: Allocator, + action: Action, + args: Args, +) !void { + switch (action) { + inline else => |comptime_action| { + const BenchmarkImpl = Action.Struct(comptime_action); + try mainActionImpl(BenchmarkImpl, alloc, args); + }, + } +} + +fn mainActionImpl( + comptime BenchmarkImpl: type, + alloc: Allocator, + args: Args, +) !void { + // First, parse our CLI options. + const Options = BenchmarkImpl.Options; + var opts: Options = .{}; + defer if (@hasDecl(Options, "deinit")) opts.deinit(); + switch (args) { + .cli => { + var iter = try cli.args.argsIterator(alloc); + defer iter.deinit(); + try cli.args.parse(Options, alloc, &opts, &iter); + }, + .string => |str| { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + str, + ); + defer iter.deinit(); + try cli.args.parse(Options, alloc, &opts, &iter); + }, + } + + // Create our implementation + const impl = try BenchmarkImpl.create(alloc, opts); + defer impl.destroy(alloc); + + // Initialize our benchmark + const b = impl.benchmark(); + _ = try b.run(.once); +} diff --git a/vendor/libghostty-vt/src/benchmark/main.zig b/vendor/libghostty-vt/src/benchmark/main.zig new file mode 100644 index 00000000..5673044f --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/main.zig @@ -0,0 +1,13 @@ +pub const cli = @import("cli.zig"); +pub const Benchmark = @import("Benchmark.zig"); +pub const CApi = @import("CApi.zig"); +pub const TerminalStream = @import("TerminalStream.zig"); +pub const CodepointWidth = @import("CodepointWidth.zig"); +pub const GraphemeBreak = @import("GraphemeBreak.zig"); +pub const ScreenClone = @import("ScreenClone.zig"); +pub const TerminalParser = @import("TerminalParser.zig"); +pub const IsSymbol = @import("IsSymbol.zig"); + +test { + @import("std").testing.refAllDecls(@This()); +} diff --git a/vendor/libghostty-vt/src/benchmark/options.zig b/vendor/libghostty-vt/src/benchmark/options.zig new file mode 100644 index 00000000..049e80f4 --- /dev/null +++ b/vendor/libghostty-vt/src/benchmark/options.zig @@ -0,0 +1,20 @@ +//! This file contains helpers for CLI options. + +const std = @import("std"); + +/// Returns the data file for the given path in a way that is consistent +/// across our CLI. If the path is not set then no file is returned. +/// If the path is "-", then we will return stdin. If the path is +/// a file then we will open and return the handle. +pub fn dataFile(path_: ?[]const u8) !?std.fs.File { + const path = path_ orelse return null; + + // Stdin + if (std.mem.eql(u8, path, "-")) return .stdin(); + + // Normal file + const file = try std.fs.cwd().openFile(path, .{}); + errdefer file.close(); + + return file; +} diff --git a/vendor/libghostty-vt/src/build/Config.zig b/vendor/libghostty-vt/src/build/Config.zig new file mode 100644 index 00000000..88968aab --- /dev/null +++ b/vendor/libghostty-vt/src/build/Config.zig @@ -0,0 +1,666 @@ +/// Build configuration. This is the configuration that is populated +/// during `zig build` to control the rest of the build process. +const Config = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); + +const ApprtRuntime = @import("../apprt/runtime.zig").Runtime; +const FontBackend = @import("../font/backend.zig").Backend; +const RendererBackend = @import("../renderer/backend.zig").Backend; +const TerminalBuildOptions = @import("../terminal/build_options.zig").Options; +const XCFrameworkTarget = @import("xcframework.zig").Target; +const WasmTarget = @import("../os/wasm/target.zig").Target; +const expandPath = @import("../os/path.zig").expand; + +const gtk = @import("gtk.zig"); +const GitVersion = @import("GitVersion.zig"); + +/// Standard build configuration options. +optimize: std.builtin.OptimizeMode, +target: std.Build.ResolvedTarget, +xcframework_target: XCFrameworkTarget = .universal, +wasm_target: WasmTarget, + +/// Comptime interfaces +app_runtime: ApprtRuntime = .none, +renderer: RendererBackend = .opengl, +font_backend: FontBackend = .freetype, + +/// Feature flags +x11: bool = false, +wayland: bool = false, +sentry: bool = true, +simd: bool = true, +i18n: bool = true, +wasm_shared: bool = true, + +/// Ghostty exe properties +exe_entrypoint: ExeEntrypoint = .ghostty, +version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 }, + +/// Binary properties +pie: bool = false, +strip: bool = false, +patch_rpath: ?[]const u8 = null, + +/// Artifacts +flatpak: bool = false, +snap: bool = false, +emit_bench: bool = false, +emit_docs: bool = false, +emit_exe: bool = false, +emit_helpgen: bool = false, +emit_lib_vt: bool = false, +emit_macos_app: bool = false, +emit_terminfo: bool = false, +emit_termcap: bool = false, +emit_test_exe: bool = false, +emit_themes: bool = false, +emit_xcframework: bool = false, +emit_webdata: bool = false, +emit_unicode_table_gen: bool = false, + +/// True when Ghostty is being built as a dependency of another project +/// rather than as the root project. +is_dep: bool = false, + +/// Environmental properties +env: std.process.EnvMap, + +pub fn init(b: *std.Build, appVersion: []const u8) !Config { + // Setup our standard Zig target and optimize options, i.e. + // `-Doptimize` and `-Dtarget`. + const optimize = b.standardOptimizeOption(.{}); + const target = target: { + var result = b.standardTargetOptions(.{}); + + // If we're building for macOS and we're on macOS, we need to + // use a generic target to workaround compilation issues. + if (result.result.os.tag == .macos and + builtin.target.os.tag.isDarwin()) + { + result = genericMacOSTarget(b, result.query.cpu_arch); + } + + // On Windows, default to the MSVC ABI so that produced COFF + // objects (including compiler_rt) are compatible with the MSVC + // linker. Zig defaults to the GNU ABI which produces objects + // with invalid COMDAT sections that MSVC rejects (LNK1143). + // Only override when no explicit ABI was requested. + if (result.result.os.tag == .windows and + result.query.abi == null) + { + var query = result.query; + query.abi = .msvc; + result = b.resolveTargetQuery(query); + } + + // If we have no minimum OS version, we set the default based on + // our tag. Not all tags have a minimum so this may be null. + if (result.query.os_version_min == null) { + result.query.os_version_min = osVersionMin(result.result.os.tag); + } + + break :target result; + }; + + // Detect if Ghostty is a dependency of another project. + // dep_prefix is non-empty when this build is running as a dependency. + const is_dep = b.dep_prefix.len > 0; + + // This is set to true when we're building a system package. For now + // this is trivially detected using the "system_package_mode" bool + // but we may want to make this more sophisticated in the future. + const system_package = b.graph.system_package_mode; + + // This specifies our target wasm runtime. For now only one semi-usable + // one exists so this is hardcoded. + const wasm_target: WasmTarget = .browser; + + // Determine whether GTK supports X11 and Wayland. This is always safe + // to run even on non-Linux platforms because any failures result in + // defaults. + const gtk_targets = gtk.targets(b); + + // We use env vars throughout the build so we grab them immediately here. + var env = try std.process.getEnvMap(b.allocator); + errdefer env.deinit(); + + var config: Config = .{ + .optimize = optimize, + .target = target, + .wasm_target = wasm_target, + .is_dep = is_dep, + .env = env, + }; + + //--------------------------------------------------------------- + // Target-specific properties + config.xcframework_target = b.option( + XCFrameworkTarget, + "xcframework-target", + "The target for the xcframework.", + ) orelse .universal; + + //--------------------------------------------------------------- + // Comptime Interfaces + config.font_backend = b.option( + FontBackend, + "font-backend", + "The font backend to use for discovery and rasterization.", + ) orelse FontBackend.default(target.result, wasm_target); + + config.app_runtime = b.option( + ApprtRuntime, + "app-runtime", + "The app runtime to use. Not all values supported on all platforms.", + ) orelse ApprtRuntime.default(target.result); + + config.renderer = b.option( + RendererBackend, + "renderer", + "The app runtime to use. Not all values supported on all platforms.", + ) orelse RendererBackend.default(target.result, wasm_target); + + //--------------------------------------------------------------- + // Feature Flags + + config.flatpak = b.option( + bool, + "flatpak", + "Build for Flatpak (integrates with Flatpak APIs). Only has an effect targeting Linux.", + ) orelse false; + + config.snap = b.option( + bool, + "snap", + "Build for Snap (do specific Snap operations). Only has an effect targeting Linux.", + ) orelse false; + + config.sentry = b.option( + bool, + "sentry", + "Build with Sentry crash reporting. Default for macOS is true, false for any other system.", + ) orelse sentry: { + switch (target.result.os.tag) { + .macos, .ios => break :sentry true, + + // Note its false for linux because the crash reports on Linux + // don't have much useful information. + else => break :sentry false, + } + }; + + config.simd = b.option( + bool, + "simd", + "Build with SIMD-accelerated code paths. Results in significant performance improvements.", + ) orelse simd: { + // We can't build our SIMD dependencies for Wasm. Note that we may + // still use SIMD features in the Wasm-builds. + if (target.result.cpu.arch.isWasm()) break :simd false; + + break :simd true; + }; + + config.wayland = b.option( + bool, + "gtk-wayland", + "Enables linking against Wayland libraries when using the GTK rendering backend.", + ) orelse gtk_targets.wayland; + + config.x11 = b.option( + bool, + "gtk-x11", + "Enables linking against X11 libraries when using the GTK rendering backend.", + ) orelse gtk_targets.x11; + + config.i18n = b.option( + bool, + "i18n", + "Enables gettext-based internationalization. Enabled by default only for macOS, and other Unix-like systems like Linux and FreeBSD when using glibc.", + ) orelse switch (target.result.os.tag) { + .macos, .ios => true, + .linux, .freebsd => target.result.isGnuLibC(), + else => false, + }; + + //--------------------------------------------------------------- + // Ghostty Exe Properties + + const version_string = b.option( + []const u8, + "version-string", + "A specific version string to use for the build. " ++ + "If not specified, git will be used. This must be a semantic version.", + ); + + config.version = if (version_string) |v| + // If an explicit version is given, we always use it. + try std.SemanticVersion.parse(v) + else version: { + const app_version = try std.SemanticVersion.parse(appVersion); + + // Is ghostty a dependency? If so, skip git detection. + if (is_dep) break :version .{ + .major = app_version.major, + .minor = app_version.minor, + .patch = app_version.patch, + }; + + // If no explicit version is given, we try to detect it from git. + const vsn = GitVersion.detect(b) catch |err| switch (err) { + // If Git isn't available we just make an unknown dev version. + error.GitNotFound, + error.GitNotRepository, + => break :version .{ + .major = app_version.major, + .minor = app_version.minor, + .patch = app_version.patch, + .pre = "dev", + .build = "0000000", + }, + + else => return err, + }; + if (vsn.tag) |tag| { + // Tip releases behave just like any other pre-release so we skip. + if (!std.mem.eql(u8, tag, "tip")) { + const expected = b.fmt("v{d}.{d}.{d}", .{ + app_version.major, + app_version.minor, + app_version.patch, + }); + + if (!std.mem.eql(u8, tag, expected)) { + @panic("tagged releases must be in vX.Y.Z format matching build.zig"); + } + + break :version .{ + .major = app_version.major, + .minor = app_version.minor, + .patch = app_version.patch, + }; + } + } + + break :version .{ + .major = app_version.major, + .minor = app_version.minor, + .patch = app_version.patch, + .pre = vsn.branch, + .build = vsn.short_hash, + }; + }; + + //--------------------------------------------------------------- + // Binary Properties + + // On NixOS, the built binary from `zig build` needs to patch the rpath + // into the built binary for it to be portable across the NixOS system + // it was built for. We default this to true if we can detect we're in + // a Nix shell and have LD_LIBRARY_PATH set. + config.patch_rpath = b.option( + []const u8, + "patch-rpath", + "Inject the LD_LIBRARY_PATH as the rpath in the built binary. " ++ + "This defaults to LD_LIBRARY_PATH if we're in a Nix shell environment on NixOS.", + ) orelse patch_rpath: { + // We only do the patching if we're targeting our own CPU and its Linux. + if (!(target.result.os.tag == .linux) or !target.query.isNativeCpu()) break :patch_rpath null; + + // If we're in a nix shell we default to doing this. + // Note: we purposely never deinit envmap because we leak the strings + if (env.get("IN_NIX_SHELL") == null) break :patch_rpath null; + break :patch_rpath env.get("LD_LIBRARY_PATH"); + }; + + config.pie = b.option( + bool, + "pie", + "Build a Position Independent Executable. Default true for system packages.", + ) orelse system_package; + + config.strip = b.option( + bool, + "strip", + "Strip the final executable. Default true for fast and small releases", + ) orelse switch (optimize) { + .Debug => false, + .ReleaseSafe => false, + .ReleaseFast, .ReleaseSmall => true, + }; + + //--------------------------------------------------------------- + // Artifacts to Emit + + config.emit_lib_vt = b.option( + bool, + "emit-lib-vt", + "Set defaults for a libghostty-vt-only build (disables xcframework, macOS app, and docs).", + ) orelse false; + + config.emit_exe = b.option( + bool, + "emit-exe", + "Build and install main executables with 'build'", + ) orelse !config.emit_lib_vt; + + config.emit_test_exe = b.option( + bool, + "emit-test-exe", + "Build and install test executables with 'build'", + ) orelse false; + + config.emit_unicode_table_gen = b.option( + bool, + "emit-unicode-table-gen", + "Build and install executables that generate unicode tables with 'build'", + ) orelse false; + + config.emit_bench = b.option( + bool, + "emit-bench", + "Build and install the benchmark executables.", + ) orelse false; + + config.emit_helpgen = b.option( + bool, + "emit-helpgen", + "Build and install the helpgen executable.", + ) orelse false; + + config.emit_docs = b.option( + bool, + "emit-docs", + "Build and install auto-generated documentation (requires pandoc)", + ) orelse emit_docs: { + // If we are emitting any other artifacts then we default to false. + if (config.emit_bench or + config.emit_test_exe or + config.emit_helpgen or + config.emit_lib_vt) break :emit_docs false; + + // We always emit docs in system package mode. + if (system_package) break :emit_docs true; + + // We only default to true if we can find pandoc. + const path = expandPath(b.allocator, "pandoc") catch + break :emit_docs false; + defer if (path) |p| b.allocator.free(p); + break :emit_docs path != null; + }; + + config.emit_terminfo = b.option( + bool, + "emit-terminfo", + "Install Ghostty terminfo source file", + ) orelse switch (target.result.os.tag) { + .windows => true, + else => switch (optimize) { + .Debug => true, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, + }, + }; + + config.emit_termcap = b.option( + bool, + "emit-termcap", + "Install Ghostty termcap file", + ) orelse switch (optimize) { + .Debug => true, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, + }; + + config.emit_themes = b.option( + bool, + "emit-themes", + "Install bundled iTerm2-Color-Schemes Ghostty themes", + ) orelse true; + + config.emit_webdata = b.option( + bool, + "emit-webdata", + "Build the website data for the website.", + ) orelse false; + + config.emit_xcframework = b.option( + bool, + "emit-xcframework", + "Build and install the xcframework for the macOS library.", + ) orelse !config.emit_lib_vt and + builtin.target.os.tag.isDarwin() and + target.result.os.tag == .macos and + config.app_runtime == .none and + (!config.emit_bench and + !config.emit_test_exe and + !config.emit_helpgen); + + config.emit_macos_app = b.option( + bool, + "emit-macos-app", + "Build and install the macOS app bundle.", + ) orelse !config.emit_lib_vt and config.emit_xcframework; + + //--------------------------------------------------------------- + // System Packages + + // These are all our dependencies that can be used with system + // packages if they exist. We set them up here so that we can set + // their defaults early. The first call configures the integration and + // subsequent calls just return the configured value. This lets them + // show up properly in `--help`. + + { + // These dependencies we want to default false if we're on macOS. + // On macOS we don't want to use system libraries because we + // generally want a fat binary. This can be overridden with the + // `-fsys` flag. + for (&[_][]const u8{ + "freetype", + "harfbuzz", + "fontconfig", + "libpng", + "zlib", + "oniguruma", + }) |dep| { + _ = b.systemIntegrationOption( + dep, + .{ + // If we're not on darwin we want to use whatever the + // default is via the system package mode + .default = if (target.result.os.tag.isDarwin()) false else null, + }, + ); + } + + // These default to false because they're rarely available as + // system packages so we usually want to statically link them. + for (&[_][]const u8{ + "glslang", + "spirv-cross", + "simdutf", + }) |dep| { + _ = b.systemIntegrationOption(dep, .{ .default = false }); + } + + // These are dynamic libraries we default to true, preferring + // to use system packages over building and installing libs + // as they require additional ldconfig of library paths or + // patching the rpath of the program to discover the dynamic library + // at runtime + for (&[_][]const u8{"gtk4-layer-shell"}) |dep| { + _ = b.systemIntegrationOption(dep, .{ .default = true }); + } + } + + return config; +} + +/// Configure the build options with our values. +pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void { + // We need to break these down individual because addOption doesn't + // support all types. + step.addOption(bool, "flatpak", self.flatpak); + step.addOption(bool, "snap", self.snap); + step.addOption(bool, "x11", self.x11); + step.addOption(bool, "wayland", self.wayland); + step.addOption(bool, "sentry", self.sentry); + step.addOption(bool, "simd", self.simd); + step.addOption(bool, "i18n", self.i18n); + step.addOption(ApprtRuntime, "app_runtime", self.app_runtime); + step.addOption(FontBackend, "font_backend", self.font_backend); + step.addOption(RendererBackend, "renderer", self.renderer); + step.addOption(ExeEntrypoint, "exe_entrypoint", self.exe_entrypoint); + step.addOption(WasmTarget, "wasm_target", self.wasm_target); + step.addOption(bool, "wasm_shared", self.wasm_shared); + + // Our version. We also add the string version so we don't need + // to do any allocations at runtime. This has to be long enough to + // accommodate realistic large branch names for dev versions. + var buf: [1024]u8 = undefined; + step.addOption(std.SemanticVersion, "app_version", self.version); + step.addOption([:0]const u8, "app_version_string", try std.fmt.bufPrintZ( + &buf, + "{f}", + .{self.version}, + )); + step.addOption( + ReleaseChannel, + "release_channel", + channel: { + const pre = self.version.pre orelse break :channel .stable; + if (pre.len == 0) break :channel .stable; + break :channel .tip; + }, + ); +} + +/// Returns the build options for the terminal module. This assumes a +/// Ghostty executable being built. Callers should modify this as needed. +pub fn terminalOptions(self: *const Config) TerminalBuildOptions { + return .{ + .artifact = .ghostty, + .simd = self.simd, + .oniguruma = true, + .c_abi = false, + .version = self.version, + .slow_runtime_safety = switch (self.optimize) { + .Debug => true, + .ReleaseSafe, + .ReleaseSmall, + .ReleaseFast, + => false, + }, + }; +} + +/// Returns a baseline CPU target retaining all the other CPU configs. +pub fn baselineTarget(self: *const Config) std.Build.ResolvedTarget { + // Set our cpu model as baseline. There may need to be other modifications + // we need to make such as resetting CPU features but for now this works. + var q = self.target.query; + q.cpu_model = .baseline; + + // Same logic as build.resolveTargetQuery but we don't need to + // handle the native case. + return .{ + .query = q, + .result = std.zig.system.resolveTargetQuery(q) catch + @panic("unable to resolve baseline query"), + }; +} + +/// Rehydrate our Config from the comptime options. Note that not all +/// options are available at comptime, so look closely at this implementation +/// to see what is and isn't available. +pub fn fromOptions() Config { + const options = @import("build_options"); + return .{ + // Unused at runtime. + .optimize = undefined, + .target = undefined, + .env = undefined, + + .version = options.app_version, + .flatpak = options.flatpak, + .app_runtime = std.meta.stringToEnum(ApprtRuntime, @tagName(options.app_runtime)).?, + .font_backend = std.meta.stringToEnum(FontBackend, @tagName(options.font_backend)).?, + .renderer = std.meta.stringToEnum(RendererBackend, @tagName(options.renderer)).?, + .snap = options.snap, + .exe_entrypoint = std.meta.stringToEnum(ExeEntrypoint, @tagName(options.exe_entrypoint)).?, + .wasm_target = std.meta.stringToEnum(WasmTarget, @tagName(options.wasm_target)).?, + .wasm_shared = options.wasm_shared, + .i18n = options.i18n, + }; +} + +/// Returns the minimum OS version for the given OS tag. This shouldn't +/// be used generally, it should only be used for Darwin-based OS currently. +pub fn osVersionMin(tag: std.Target.Os.Tag) ?std.Target.Query.OsVersion { + return switch (tag) { + // We support back to the earliest officially supported version + // of macOS by Apple. EOL versions are not supported. + .macos => .{ .semver = .{ + .major = 13, + .minor = 0, + .patch = 0, + } }, + + // iOS 17 picked arbitrarily + .ios => .{ .semver = .{ + .major = 17, + .minor = 0, + .patch = 0, + } }, + + // This should never happen currently. If we add a new target then + // we should add a new case here. + else => null, + }; +} + +// Returns a ResolvedTarget for a mac with a `target.result.cpu.model.name` of `generic`. +// `b.standardTargetOptions()` returns a more specific cpu like `apple_a15`. +// +// This is used to workaround compilation issues on macOS. +// (see for example https://github.com/mitchellh/ghostty/issues/1640). +pub fn genericMacOSTarget( + b: *std.Build, + arch: ?std.Target.Cpu.Arch, +) std.Build.ResolvedTarget { + return b.resolveTargetQuery(.{ + .cpu_arch = arch orelse builtin.target.cpu.arch, + .os_tag = .macos, + .os_version_min = osVersionMin(.macos), + }); +} + +/// The possible entrypoints for the exe artifact. This has no effect on +/// other artifact types (i.e. lib, wasm_module). +/// +/// The whole existence of this enum is to workaround the fact that Zig +/// doesn't allow the main function to be in a file in a subdirctory +/// from the "root" of the module, and I don't want to pollute our root +/// directory with a bunch of individual zig files for each entrypoint. +/// +/// Therefore, main.zig uses this to switch between the different entrypoints. +pub const ExeEntrypoint = enum { + ghostty, + helpgen, + mdgen_ghostty_1, + mdgen_ghostty_5, + webgen_config, + webgen_actions, + webgen_commands, +}; + +/// The release channel for the build. +pub const ReleaseChannel = enum { + /// Unstable builds on every commit. + tip, + + /// Stable tagged releases. + stable, +}; diff --git a/vendor/libghostty-vt/src/build/GhosttyBench.zig b/vendor/libghostty-vt/src/build/GhosttyBench.zig new file mode 100644 index 00000000..27dda880 --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyBench.zig @@ -0,0 +1,55 @@ +//! GhosttyBench generates all the Ghostty benchmark helper binaries. +const GhosttyBench = @This(); + +const std = @import("std"); +const SharedDeps = @import("SharedDeps.zig"); + +steps: []*std.Build.Step.Compile, + +pub fn init( + b: *std.Build, + deps: *const SharedDeps, +) !GhosttyBench { + var steps: std.ArrayList(*std.Build.Step.Compile) = .empty; + errdefer steps.deinit(b.allocator); + + // Our synthetic data generator + { + const exe = b.addExecutable(.{ + .name = "ghostty-gen", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main_gen.zig"), + .target = deps.config.target, + // We always want our datagen to be fast because it + // takes awhile to run. + .optimize = .ReleaseFast, + }), + }); + exe.linkLibC(); + _ = try deps.add(exe); + try steps.append(b.allocator, exe); + } + + // Our benchmarking application. + { + const exe = b.addExecutable(.{ + .name = "ghostty-bench", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main_bench.zig"), + .target = deps.config.target, + // We always want our benchmarks to be in release mode. + .optimize = .ReleaseFast, + }), + }); + exe.linkLibC(); + _ = try deps.add(exe); + try steps.append(b.allocator, exe); + } + + return .{ .steps = steps.items }; +} + +pub fn install(self: *const GhosttyBench) void { + const b = self.steps[0].step.owner; + for (self.steps) |step| b.installArtifact(step); +} diff --git a/vendor/libghostty-vt/src/build/GhosttyDist.zig b/vendor/libghostty-vt/src/build/GhosttyDist.zig new file mode 100644 index 00000000..448047f4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyDist.zig @@ -0,0 +1,257 @@ +const GhosttyDist = @This(); + +const std = @import("std"); +const Config = @import("Config.zig"); +const SharedDeps = @import("SharedDeps.zig"); +const GhosttyFrameData = @import("GhosttyFrameData.zig"); + +/// The final source tarball. +archive: std.Build.LazyPath, + +/// The step to install the tarball. +install_step: *std.Build.Step, + +/// The step to depend on +archive_step: *std.Build.Step, + +/// The step to depend on for checking the dist +check_step: *std.Build.Step, + +pub fn init(b: *std.Build, cfg: *const Config) !GhosttyDist { + // The name prefix used for all paths in the archive. + const name = if (cfg.emit_lib_vt) "libghostty-vt" else "ghostty"; + + // Get the resources we're going to inject into the source tarball. + // lib-vt doesn't need GTK resources or frame data. + const alloc = b.allocator; + var resources: std.ArrayListUnmanaged(Resource) = .empty; + if (!cfg.emit_lib_vt) { + { + const gtk = SharedDeps.gtkNgDistResources(b); + try resources.append(alloc, gtk.resources_c); + try resources.append(alloc, gtk.resources_h); + } + { + const framedata = GhosttyFrameData.distResources(b); + try resources.append(alloc, framedata.framedata); + } + } + + // git archive to create the final tarball. "git archive" is the + // easiest way I can find to create a tarball that ignores stuff + // from gitignore and also supports adding files as well as removing + // dist-only files (the "export-ignore" git attribute). + const git_archive = b.addSystemCommand(&.{ + "git", + "archive", + "--format=tgz", + }); + + // embed the Ghostty version in the tarball + { + const version = b.addWriteFiles().add("VERSION", b.fmt("{f}", .{cfg.version})); + // --add-file uses the most recent --prefix to determine the path + // in the archive to copy the file (the directory only). + git_archive.addArg(b.fmt("--prefix={s}-{f}/", .{ + name, cfg.version, + })); + git_archive.addPrefixedFileArg("--add-file=", version); + } + + // Add all of our resources into the tarball. + for (resources.items) |resource| { + // Our dist path basename may not match our generated file basename, + // and git archive requires this. To be safe, we copy the file once + // to ensure the basename matches and then use that as the final + // generated file. + const copied = b.addWriteFiles().addCopyFile( + resource.generated, + std.fs.path.basename(resource.dist), + ); + + // --add-file uses the most recent --prefix to determine the path + // in the archive to copy the file (the directory only). + git_archive.addArg(b.fmt("--prefix={s}-{f}/{s}/", .{ + name, cfg.version, + std.fs.path.dirname(resource.dist).?, + })); + git_archive.addPrefixedFileArg("--add-file=", copied); + } + + // Add our output + git_archive.addArgs(&.{ + // This is important. Standard source tarballs extract into + // a directory named `project-version`. This is expected by + // standard tooling such as debhelper and rpmbuild. + b.fmt("--prefix={s}-{f}/", .{ name, cfg.version }), + "-o", + }); + const output = git_archive.addOutputFileArg(b.fmt( + "{s}-{f}.tar.gz", + .{ name, cfg.version }, + )); + git_archive.addArg("HEAD"); + + // When building for lib-vt only, exclude large directories that + // are not needed to build libghostty-vt. This significantly reduces + // the size of the resulting archive. + if (cfg.emit_lib_vt) { + for (lib_vt_excludes) |exclude| { + git_archive.addArg(b.fmt(":(exclude){s}", .{exclude})); + } + } + + // The install step to put the dist into the build directory. + const install = b.addInstallFile( + output, + b.fmt("dist/{s}-{f}.tar.gz", .{ name, cfg.version }), + ); + + // The check step to ensure the archive works. + const check = b.addSystemCommand(&.{ "tar", "xvzf" }); + check.addFileArg(output); + check.addArg("-C"); + + // This is the root Ghostty source dir of the extracted source tarball. + // i.e. this is way `build.zig` is. + const extract_dir = check + .addOutputDirectoryArg(name) + .path(b, b.fmt("{s}-{f}", .{ name, cfg.version })); + + // Check that tests pass within the extracted directory. This isn't + // a fully hermetic test because we're sharing the Zig cache. In + // the future we could add an option to use a totally new cache but + // in the interest of speed we don't do that for now and hope other + // CI catches any issues. + const check_test = step: { + // For lib-vt, we run the lib-vt tests instead of the full test suite. + const check_cmd = if (cfg.emit_lib_vt) + &[_][]const u8{ "zig", "build", "test-lib-vt", "-Demit-lib-vt=true" } + else + &[_][]const u8{ "zig", "build", "test" }; + const step = b.addSystemCommand(check_cmd); + step.setCwd(extract_dir); + + // Must be set so that Zig knows that this command doesn't + // have side effects and is being run for its exit code check. + // Zig will cache depending on its extract dir. + step.expectExitCode(0); + + // Capture stderr so it doesn't spew into the parent build. + // On the flip side, if the test fails we won't know why so + // that sucks but we should have already ran tests at this point. + // NOTE(mitchellh): temporarily disabled to diagnose heisenbug + //_ = step.captureStdErr(); + + break :step step; + }; + + // Check that all our dist resources are at the proper path. + for (resources.items) |resource| { + const path = extract_dir.path(b, resource.dist); + const check_path = b.addCheckFile(path, .{}); + check_test.step.dependOn(&check_path.step); + } + + // For lib-vt, also verify the CMake build works from the tarball. + if (cfg.emit_lib_vt) { + const cmake_build_dir = extract_dir.path(b, "cmake-build"); + const cmake_configure = b.addSystemCommand(&.{ "cmake", "-B" }); + cmake_configure.addDirectoryArg(cmake_build_dir); + cmake_configure.setCwd(extract_dir); + cmake_configure.expectExitCode(0); + cmake_configure.step.dependOn(&check.step); + + const cmake_build = b.addSystemCommand(&.{ "cmake", "--build" }); + cmake_build.addDirectoryArg(cmake_build_dir); + cmake_build.expectExitCode(0); + cmake_build.step.dependOn(&cmake_configure.step); + + check_test.step.dependOn(&cmake_build.step); + } + + return .{ + .archive = output, + .install_step = &install.step, + .archive_step = &git_archive.step, + .check_step = &check_test.step, + }; +} + +/// Paths to exclude from the dist archive when building for lib-vt only. +/// These are large files and directories that are not needed to build or +/// test libghostty-vt, specified as git pathspec exclude patterns. +const lib_vt_excludes = &[_][]const u8{ + // App and platform resources + "images", + "macos", + "dist/doxygen", + "dist/linux", + "dist/macos", + "dist/windows", + "flatpak", + "snap", + "po", + "example", + + // Test corpus (lib-vt tests use embedded testdata within src/terminal/) + "test", + + // Large binary assets + "src/font/res", + "src/crash/testdata", + "pkg/wuffs/src/too_big.jpg", + "pkg/wuffs/src/too_big.png", + "pkg/breakpad/vendor", + + // Vendored libraries not used by lib-vt + "vendor", +}; + +/// A dist resource is a resource that is built and distributed as part +/// of the source tarball with Ghostty. These aren't committed to the Git +/// repository but are built as part of the `zig build dist` command. +/// The purpose is to limit the number of build-time dependencies required +/// for downstream users and packagers. +pub const Resource = struct { + /// The relative path in the source tree where the resource will be + /// if it was pre-built. These are not checksummed or anything because the + /// assumption is that the source tarball itself is checksummed and signed. + dist: []const u8, + + /// The path to the generated resource in the build system. By depending + /// on this you'll force it to regenerate. This does NOT point to the + /// "path" above. + generated: std.Build.LazyPath, + + /// Returns the path to use for this resource. + pub fn path(self: *const Resource, b: *std.Build) std.Build.LazyPath { + // If the dist path exists at build compile time then we use it. + if (self.exists(b)) { + return b.path(self.dist); + } + + // Otherwise we use the generated path. + return self.generated; + } + + /// Returns true if the dist path exists at build time. + pub fn exists(self: *const Resource, b: *std.Build) bool { + if (b.build_root.handle.access(self.dist, .{})) { + // If we have a ".git" directory then we're a git checkout + // and we never want to use the dist path. This shouldn't happen + // so show a warning to the user. + if (b.build_root.handle.access(".git", .{})) { + std.log.warn( + "dist resource '{s}' should not be in a git checkout", + .{self.dist}, + ); + return false; + } else |_| {} + + return true; + } else |_| { + return false; + } + } +}; diff --git a/vendor/libghostty-vt/src/build/GhosttyDocs.zig b/vendor/libghostty-vt/src/build/GhosttyDocs.zig new file mode 100644 index 00000000..cd75fc06 --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyDocs.zig @@ -0,0 +1,124 @@ +//! GhosttyDocs generates all the on-disk documentation that Ghostty is +//! installed with (man pages, html, markdown, etc.) +const GhosttyDocs = @This(); + +const std = @import("std"); +const Config = @import("Config.zig"); +const SharedDeps = @import("SharedDeps.zig"); + +steps: []*std.Build.Step, + +pub fn init( + b: *std.Build, + deps: *const SharedDeps, +) !GhosttyDocs { + var steps: std.ArrayList(*std.Build.Step) = .empty; + errdefer steps.deinit(b.allocator); + + const manpages = [_]struct { + name: []const u8, + section: []const u8, + }{ + .{ .name = "ghostty", .section = "1" }, + .{ .name = "ghostty", .section = "5" }, + }; + + inline for (manpages) |manpage| { + const generate_markdown = b.addExecutable(.{ + .name = "mdgen_" ++ manpage.name ++ "_" ++ manpage.section, + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = b.graph.host, + .strip = false, + .omit_frame_pointer = false, + .unwind_tables = .sync, + }), + }); + deps.help_strings.addImport(generate_markdown); + + const gen_config = config: { + var copy = deps.config.*; + copy.exe_entrypoint = @field( + Config.ExeEntrypoint, + "mdgen_" ++ manpage.name ++ "_" ++ manpage.section, + ); + break :config copy; + }; + + const generate_markdown_options = b.addOptions(); + try gen_config.addOptions(generate_markdown_options); + generate_markdown.root_module.addOptions("build_options", generate_markdown_options); + + const generate_markdown_step = b.addRunArtifact(generate_markdown); + const markdown_output = generate_markdown_step.captureStdOut(); + + try steps.append(b.allocator, &b.addInstallFile( + markdown_output, + "share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".md", + ).step); + + const generate_html = b.addSystemCommand(&.{"pandoc"}); + generate_html.addArgs(&.{ + "--standalone", + "--from", + "markdown", + "--to", + "html", + }); + generate_html.addFileArg(markdown_output); + + try steps.append(b.allocator, &b.addInstallFile( + generate_html.captureStdOut(), + "share/ghostty/doc/" ++ manpage.name ++ "." ++ manpage.section ++ ".html", + ).step); + + const generate_manpage = b.addSystemCommand(&.{"pandoc"}); + generate_manpage.addArgs(&.{ + "--standalone", + "--from", + "markdown", + "--to", + "man", + }); + generate_manpage.addFileArg(markdown_output); + + try steps.append(b.allocator, &b.addInstallFile( + generate_manpage.captureStdOut(), + "share/man/man" ++ manpage.section ++ "/" ++ manpage.name ++ "." ++ manpage.section, + ).step); + } + + return .{ .steps = steps.items }; +} + +pub fn install(self: *const GhosttyDocs) void { + const b = self.steps[0].owner; + self.addStepDependencies(b.getInstallStep()); +} + +pub fn addStepDependencies( + self: *const GhosttyDocs, + other_step: *std.Build.Step, +) void { + for (self.steps) |step| other_step.dependOn(step); +} + +/// Installs some dummy files to satisfy the folder structure of docs +/// without actually generating any documentation. This is useful +/// when the `emit-docs` option is not set to true, but we still +/// need the rough directory structure to exist, such as for the macOS +/// app. +pub fn installDummy(self: *const GhosttyDocs, step: *std.Build.Step) void { + _ = self; + + const b = step.owner; + var wf = b.addWriteFiles(); + const path = "share/man/.placeholder"; + step.dependOn(&b.addInstallFile( + wf.add( + path, + "emit-docs not true so no man pages", + ), + path, + ).step); +} diff --git a/vendor/libghostty-vt/src/build/GhosttyExe.zig b/vendor/libghostty-vt/src/build/GhosttyExe.zig new file mode 100644 index 00000000..caa564bf --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyExe.zig @@ -0,0 +1,127 @@ +const Ghostty = @This(); + +const std = @import("std"); +const Config = @import("Config.zig"); +const SharedDeps = @import("SharedDeps.zig"); + +/// The primary Ghostty executable. +exe: *std.Build.Step.Compile, + +/// The install step for the executable. +install_step: *std.Build.Step.InstallArtifact, + +pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty { + const exe: *std.Build.Step.Compile = b.addExecutable(.{ + .name = "ghostty", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = cfg.target, + .optimize = cfg.optimize, + .strip = cfg.strip, + .omit_frame_pointer = cfg.strip, + .unwind_tables = if (cfg.strip) .none else .sync, + }), + // Crashes on x86_64 self-hosted on 0.15.1 + .use_llvm = true, + }); + const install_step = b.addInstallArtifact(exe, .{}); + + // Set PIE if requested + if (cfg.pie) exe.pie = true; + + // Add the shared dependencies. When building only lib-vt we skip + // heavy deps so cross-compilation doesn't pull in GTK, etc. + if (!cfg.emit_lib_vt) _ = try deps.add(exe); + + // Check for possible issues + try checkNixShell(exe, cfg); + + // Patch our rpath if that option is specified. + if (cfg.patch_rpath) |rpath| { + if (rpath.len > 0) { + const run = std.Build.Step.Run.create(b, "patchelf rpath"); + run.addArgs(&.{ "patchelf", "--set-rpath", rpath }); + run.addArtifactArg(exe); + install_step.step.dependOn(&run.step); + } + } + + // OS-specific + switch (cfg.target.result.os.tag) { + .windows => { + exe.subsystem = .Windows; + exe.addWin32ResourceFile(.{ + .file = b.path("dist/windows/ghostty.rc"), + }); + }, + + else => {}, + } + + return .{ + .exe = exe, + .install_step = install_step, + }; +} + +/// Add the ghostty exe to the install target. +pub fn install(self: *const Ghostty) void { + const b = self.install_step.step.owner; + b.getInstallStep().dependOn(&self.install_step.step); +} + +/// If we're in NixOS but not in the shell environment then we issue +/// a warning because the rpath may not be setup properly. This doesn't modify +/// our build in any way but addresses a common build-from-source issue +/// for a subset of users. +fn checkNixShell(exe: *std.Build.Step.Compile, cfg: *const Config) !void { + // Non-Linux doesn't have rpath issues. + if (cfg.target.result.os.tag != .linux) return; + + // When cross-compiling, we don't need to worry about matching our + // Nix shell rpath since the resulting binary will be run on a + // separate system. + if (!cfg.target.query.isNativeCpu()) return; + if (!cfg.target.query.isNativeOs()) return; + + // Verify we're in NixOS + std.fs.accessAbsolute("/etc/NIXOS", .{}) catch return; + + // If we're in a nix shell, not a problem + if (cfg.env.get("IN_NIX_SHELL") != null) return; + + try exe.step.addError( + "\x1b[" ++ color_map.get("yellow").? ++ + "\x1b[" ++ color_map.get("d").? ++ + \\Detected building on and for NixOS outside of the Nix shell environment. + \\ + \\The resulting ghostty binary will likely fail on launch because it is + \\unable to dynamically load the windowing libs (X11, Wayland, etc.). + \\We highly recommend running only within the Nix build environment + \\and the resulting binary will be portable across your system. + \\ + \\To run in the Nix build environment, use the following command. + \\Append any additional options like (`-Doptimize` flags). The resulting + \\binary will be in zig-out as usual. + \\ + \\ nix develop -c zig build + \\ + ++ + "\x1b[0m", + .{}, + ); +} + +/// ANSI escape codes for colored log output +const color_map = std.StaticStringMap([]const u8).initComptime(.{ + &.{ "black", "30m" }, + &.{ "blue", "34m" }, + &.{ "b", "1m" }, + &.{ "d", "2m" }, + &.{ "cyan", "36m" }, + &.{ "green", "32m" }, + &.{ "magenta", "35m" }, + &.{ "red", "31m" }, + &.{ "white", "37m" }, + &.{ "yellow", "33m" }, +}); diff --git a/vendor/libghostty-vt/src/build/GhosttyFrameData.zig b/vendor/libghostty-vt/src/build/GhosttyFrameData.zig new file mode 100644 index 00000000..8469759f --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyFrameData.zig @@ -0,0 +1,75 @@ +//! GhosttyFrameData generates a compressed file and zig module which contains (and exposes) the +//! Ghostty animation frames for use in `ghostty +boo` +const GhosttyFrameData = @This(); + +const std = @import("std"); +const DistResource = @import("GhosttyDist.zig").Resource; + +/// The output path for the compressed framedata zig file +output: std.Build.LazyPath, + +pub fn init(b: *std.Build) !GhosttyFrameData { + const dist = distResources(b); + + // Generate the Zig source file that embeds the compressed data + const wf = b.addWriteFiles(); + _ = wf.addCopyFile(dist.framedata.path(b), "framedata.compressed"); + const zig_file = wf.add("framedata.zig", + \\//! This file is auto-generated. Do not edit. + \\ + \\pub const compressed = @embedFile("framedata.compressed"); + \\ + ); + + return .{ .output = zig_file }; +} + +/// Add the "framedata" import. +pub fn addImport(self: *const GhosttyFrameData, step: *std.Build.Step.Compile) void { + self.output.addStepDependencies(&step.step); + step.root_module.addAnonymousImport("framedata", .{ + .root_source_file = self.output, + }); +} + +/// Creates the framedata resources that can be prebuilt for our dist build. +pub fn distResources(b: *std.Build) struct { + framedata: DistResource, +} { + const exe = b.addExecutable(.{ + .name = "framegen", + .root_module = b.createModule(.{ + .target = b.graph.host, + }), + }); + exe.addCSourceFile(.{ + .file = b.path("src/build/framegen/main.c"), + .flags = &.{}, + }); + exe.linkLibC(); + + if (b.systemIntegrationOption("zlib", .{})) { + exe.linkSystemLibrary2("zlib", .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, + }); + } else { + if (b.lazyDependency("zlib", .{ + .target = b.graph.host, + .optimize = .ReleaseFast, + })) |zlib_dep| { + exe.linkLibrary(zlib_dep.artifact("z")); + } + } + + const run = b.addRunArtifact(exe); + run.addDirectoryArg(b.path("src/build/framegen/frames")); + const compressed_file = run.addOutputFileArg("framedata.compressed"); + + return .{ + .framedata = .{ + .dist = "src/build/framegen/framedata.compressed", + .generated = compressed_file, + }, + }; +} diff --git a/vendor/libghostty-vt/src/build/GhosttyI18n.zig b/vendor/libghostty-vt/src/build/GhosttyI18n.zig new file mode 100644 index 00000000..0874676c --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyI18n.zig @@ -0,0 +1,193 @@ +const GhosttyI18n = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const Config = @import("Config.zig"); +const gresource = @import("../apprt/gtk/build/gresource.zig"); +const locales = @import("../os/i18n_locales.zig").locales; + +const domain = "com.mitchellh.ghostty"; + +owner: *std.Build, +steps: []*std.Build.Step, + +/// This step updates the translation files on disk that should be +/// committed to the repo. +update_step: *std.Build.Step, + +pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n { + _ = cfg; + + var steps: std.ArrayList(*std.Build.Step) = .empty; + defer steps.deinit(b.allocator); + + inline for (locales) |locale| { + // There is no encoding suffix in the LC_MESSAGES path on FreeBSD, + // so we need to remove it from `locale` to have a correct destination string. + // (/usr/local/share/locale/en_AU/LC_MESSAGES) + const target_locale = comptime if (builtin.target.os.tag == .freebsd) + std.mem.trimRight(u8, locale, ".UTF-8") + else + locale; + + const msgfmt = b.addSystemCommand(&.{ "msgfmt", "-o", "-" }); + msgfmt.addFileArg(b.path("po/" ++ locale ++ ".po")); + + try steps.append(b.allocator, &b.addInstallFile( + msgfmt.captureStdOut(), + std.fmt.comptimePrint( + "share/locale/{s}/LC_MESSAGES/{s}.mo", + .{ target_locale, domain }, + ), + ).step); + } + + return .{ + .owner = b, + .update_step = try createUpdateStep(b), + .steps = try steps.toOwnedSlice(b.allocator), + }; +} + +pub fn install(self: *const GhosttyI18n) void { + self.addStepDependencies(self.owner.getInstallStep()); +} + +pub fn addStepDependencies( + self: *const GhosttyI18n, + other_step: *std.Build.Step, +) void { + for (self.steps) |step| other_step.dependOn(step); +} + +fn createUpdateStep(b: *std.Build) !*std.Build.Step { + const xgettext = b.addSystemCommand(&.{ + "xgettext", + "--language=C", // Silence the "unknown extension" errors + "--from-code=UTF-8", + "--keyword=_", + "--keyword=C_:1c,2", + }); + + // Collect to intermediate .pot file + xgettext.addArg("-o"); + const gtk_pot = xgettext.addOutputFileArg("gtk.pot"); + + // Not cacheable due to the gresource files + xgettext.has_side_effects = true; + + inline for (gresource.blueprints) |blp| { + const path = std.fmt.comptimePrint( + "src/apprt/gtk/ui/{[major]}.{[minor]}/{[name]s}.blp", + blp, + ); + // The arguments to xgettext must be the relative path in the build root + // or the resulting files will contain the absolute path. This will cause + // a lot of churn because not everyone has the Ghostty code checked out in + // exactly the same location. + xgettext.addArg(path); + // Mark the file as an input so that the Zig build system caching will work. + xgettext.addFileInput(b.path(path)); + } + + { + // Iterate over all of the files underneath `src/apprt/gtk`. We store + // them in an array so that they can be sorted into a determininistic + // order. That will minimize code churn as directory walking is not + // guaranteed to happen in any particular order. + + var gtk_files: std.ArrayListUnmanaged([]const u8) = .empty; + defer { + for (gtk_files.items) |item| b.allocator.free(item); + gtk_files.deinit(b.allocator); + } + + var gtk_dir = try b.build_root.handle.openDir( + "src/apprt/gtk", + .{ .iterate = true }, + ); + defer gtk_dir.close(); + + var walk = try gtk_dir.walk(b.allocator); + defer walk.deinit(); + while (try walk.next()) |src| { + switch (src.kind) { + .file => if (!std.mem.endsWith( + u8, + src.basename, + ".zig", + )) continue, + + else => continue, + } + + try gtk_files.append(b.allocator, try b.allocator.dupe(u8, src.path)); + } + + std.mem.sort( + []const u8, + gtk_files.items, + {}, + struct { + fn lt(_: void, lhs: []const u8, rhs: []const u8) bool { + return std.mem.order(u8, lhs, rhs) == .lt; + } + }.lt, + ); + + for (gtk_files.items) |item| { + const path = b.pathJoin(&.{ "src/apprt/gtk", item }); + // The arguments to xgettext must be the relative path in the build root + // or the resulting files will contain the absolute path. This will + // cause a lot of churn because not everyone has the Ghostty code + // checked out in exactly the same location. + xgettext.addArg(path); + // Mark the file as an input so that the Zig build system caching will work. + xgettext.addFileInput(b.path(path)); + } + } + + // Add support for localizing our `nautilus` integration + const xgettext_py = b.addSystemCommand(&.{ + "xgettext", + "--language=Python", + "--from-code=UTF-8", + }); + + // Collect to intermediate .pot file + xgettext_py.addArg("-o"); + const py_pot = xgettext_py.addOutputFileArg("py.pot"); + + const nautilus_script_path = "dist/linux/ghostty_nautilus.py"; + xgettext_py.addArg(nautilus_script_path); + xgettext_py.addFileInput(b.path(nautilus_script_path)); + + // Merge pot files + const xgettext_merge = b.addSystemCommand(&.{ + "xgettext", + "--add-comments=Translators", + "--package-name=" ++ domain, + "--msgid-bugs-address=m@mitchellh.com", + "--copyright-holder=\"Mitchell Hashimoto, Ghostty contributors\"", + "-o", + "-", + }); + // py_pot needs to be first on merge order because of `xgettext` behavior around + // charset when merging the two `.pot` files + xgettext_merge.addFileArg(py_pot); + xgettext_merge.addFileArg(gtk_pot); + const usf = b.addUpdateSourceFiles(); + usf.addCopyFileToSource( + xgettext_merge.captureStdOut(), + "po/" ++ domain ++ ".pot", + ); + + inline for (locales) |locale| { + const msgmerge = b.addSystemCommand(&.{ "msgmerge", "--quiet", "--no-fuzzy-matching" }); + msgmerge.addFileArg(b.path("po/" ++ locale ++ ".po")); + msgmerge.addFileArg(xgettext_merge.captureStdOut()); + usf.addCopyFileToSource(msgmerge.captureStdOut(), "po/" ++ locale ++ ".po"); + } + + return &usf.step; +} diff --git a/vendor/libghostty-vt/src/build/GhosttyLib.zig b/vendor/libghostty-vt/src/build/GhosttyLib.zig new file mode 100644 index 00000000..4e15fbbf --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyLib.zig @@ -0,0 +1,199 @@ +const GhosttyLib = @This(); + +const std = @import("std"); +const RunStep = std.Build.Step.Run; +const Config = @import("Config.zig"); +const SharedDeps = @import("SharedDeps.zig"); +const LibtoolStep = @import("LibtoolStep.zig"); +const LipoStep = @import("LipoStep.zig"); + +/// The step that generates the file. +step: *std.Build.Step, + +/// The final static library file +output: std.Build.LazyPath, +dsym: ?std.Build.LazyPath, + +pub fn initStatic( + b: *std.Build, + deps: *const SharedDeps, +) !GhosttyLib { + const lib = b.addLibrary(.{ + .name = "ghostty", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main_c.zig"), + .target = deps.config.target, + .optimize = deps.config.optimize, + .strip = deps.config.strip, + .omit_frame_pointer = deps.config.strip, + .unwind_tables = if (deps.config.strip) .none else .sync, + }), + + // Fails on self-hosted x86_64 on macOS + .use_llvm = true, + }); + lib.linkLibC(); + + // These must be bundled since we're compiling into a static lib. + // Otherwise, you get undefined symbol errors. + lib.bundle_compiler_rt = true; + lib.bundle_ubsan_rt = true; + + if (deps.config.target.result.os.tag == .windows) { + // Zig's ubsan emits /exclude-symbols linker directives that + // are incompatible with the MSVC linker (LNK4229). + lib.bundle_ubsan_rt = false; + } + + // Add our dependencies. Get the list of all static deps so we can + // build a combined archive if necessary. + var lib_list = try deps.add(lib); + try lib_list.append(b.allocator, lib.getEmittedBin()); + + if (!deps.config.target.result.os.tag.isDarwin()) return .{ + .step = &lib.step, + .output = lib.getEmittedBin(), + .dsym = null, + }; + + // Create a static lib that contains all our dependencies. + const libtool = LibtoolStep.create(b, .{ + .name = "ghostty", + .out_name = "libghostty-fat.a", + .sources = lib_list.items, + }); + libtool.step.dependOn(&lib.step); + + return .{ + .step = libtool.step, + .output = libtool.output, + + // Static libraries cannot have dSYMs because they aren't linked. + .dsym = null, + }; +} + +pub fn initShared( + b: *std.Build, + deps: *const SharedDeps, +) !GhosttyLib { + const lib = b.addLibrary(.{ + .name = "ghostty", + .linkage = .dynamic, + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main_c.zig"), + .target = deps.config.target, + .optimize = deps.config.optimize, + .strip = deps.config.strip, + .omit_frame_pointer = deps.config.strip, + .unwind_tables = if (deps.config.strip) .none else .sync, + }), + + // Fails on self-hosted x86_64 + .use_llvm = true, + }); + _ = try deps.add(lib); + + // On Windows with MSVC, building a DLL requires the full CRT library + // chain. linkLibC() (called via deps.add) provides msvcrt.lib, but + // that references symbols in vcruntime.lib and ucrt.lib. Zig's library + // search paths include the MSVC lib dir and the Windows SDK 'um' dir, + // but not the SDK 'ucrt' dir where ucrt.lib lives. + if (deps.config.target.result.os.tag == .windows and + deps.config.target.result.abi == .msvc) + { + // The CRT initialization code in msvcrt.lib calls __vcrt_initialize + // and __acrt_initialize, which are in the static CRT libraries. + lib.linkSystemLibrary("libvcruntime"); + + // ucrt.lib is in the Windows SDK 'ucrt' dir. Detect the SDK + // installation and add the UCRT library path. + const arch = deps.config.target.result.cpu.arch; + const sdk = std.zig.WindowsSdk.find(b.allocator, arch) catch null; + if (sdk) |s| { + if (s.windows10sdk) |w10| { + const arch_str: []const u8 = switch (arch) { + .x86_64 => "x64", + .x86 => "x86", + .aarch64 => "arm64", + else => "x64", + }; + const ucrt_lib_path = std.fmt.allocPrint( + b.allocator, + "{s}\\Lib\\{s}\\ucrt\\{s}", + .{ w10.path, w10.version, arch_str }, + ) catch null; + + if (ucrt_lib_path) |path| { + lib.addLibraryPath(.{ .cwd_relative = path }); + } + } + } + lib.linkSystemLibrary("libucrt"); + } + + // Get our debug symbols + const dsymutil: ?std.Build.LazyPath = dsymutil: { + if (!deps.config.target.result.os.tag.isDarwin()) { + break :dsymutil null; + } + + const dsymutil = RunStep.create(b, "dsymutil"); + dsymutil.addArgs(&.{"dsymutil"}); + dsymutil.addFileArg(lib.getEmittedBin()); + dsymutil.addArgs(&.{"-o"}); + const output = dsymutil.addOutputFileArg("libghostty.dSYM"); + break :dsymutil output; + }; + + return .{ + .step = &lib.step, + .output = lib.getEmittedBin(), + .dsym = dsymutil, + }; +} + +pub fn initMacOSUniversal( + b: *std.Build, + original_deps: *const SharedDeps, +) !GhosttyLib { + const aarch64 = try initStatic(b, &try original_deps.retarget( + b, + Config.genericMacOSTarget(b, .aarch64), + )); + const x86_64 = try initStatic(b, &try original_deps.retarget( + b, + Config.genericMacOSTarget(b, .x86_64), + )); + + const universal = LipoStep.create(b, .{ + .name = "ghostty", + .out_name = "libghostty.a", + .input_a = aarch64.output, + .input_b = x86_64.output, + }); + + return .{ + .step = universal.step, + .output = universal.output, + + // You can't run dsymutil on a universal binary, you have to + // do it on the individual binaries. + .dsym = null, + }; +} + +pub fn install(self: *const GhosttyLib, name: []const u8) void { + const b = self.step.owner; + const lib_install = b.addInstallLibFile(self.output, name); + b.getInstallStep().dependOn(&lib_install.step); +} + +pub fn installHeader(self: *const GhosttyLib) void { + const b = self.step.owner; + const header_install = b.addInstallHeaderFile( + b.path("include/ghostty.h"), + "ghostty.h", + ); + b.getInstallStep().dependOn(&header_install.step); +} diff --git a/vendor/libghostty-vt/src/build/GhosttyLibVt.zig b/vendor/libghostty-vt/src/build/GhosttyLibVt.zig new file mode 100644 index 00000000..3e1be477 --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyLibVt.zig @@ -0,0 +1,310 @@ +const GhosttyLibVt = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const RunStep = std.Build.Step.Run; +const GhosttyZig = @import("GhosttyZig.zig"); +const LibtoolStep = @import("LibtoolStep.zig"); +const SharedDeps = @import("SharedDeps.zig"); + +/// The step that generates the file. +step: *std.Build.Step, + +/// The install step for the library output. +artifact: *std.Build.Step, + +/// The kind of library +kind: Kind, + +/// The final library file +output: std.Build.LazyPath, +dsym: ?std.Build.LazyPath, +pkg_config: ?std.Build.LazyPath, + +/// The kind of library being built. This is similar to LinkMode but +/// also includes wasm which is an executable, not a library. +const Kind = enum { + wasm, + shared, + static, +}; + +pub fn initWasm( + b: *std.Build, + zig: *const GhosttyZig, +) !GhosttyLibVt { + const target = zig.vt.resolved_target.?; + assert(target.result.cpu.arch.isWasm()); + + const exe = b.addExecutable(.{ + .name = "ghostty-vt", + .root_module = zig.vt_c, + .version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 }, + }); + + // Allow exported symbols to actually be exported. + exe.rdynamic = true; + + // Export the indirect function table so that embedders (e.g. JS in + // a browser) can insert callback entries for terminal effects. + exe.export_table = true; + + // There is no entrypoint for this wasm module. + exe.entry = .disabled; + + // Zig's WASM linker doesn't support --growable-table, so the table + // is emitted with max == min and can't be grown from JS. Run a + // small Zig build tool that patches the binary's table section to + // remove the max limit. + const patch_run = patch: { + const patcher = b.addExecutable(.{ + .name = "wasm_patch_growable_table", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/build/wasm_patch_growable_table.zig"), + .target = b.graph.host, + }), + }); + break :patch b.addRunArtifact(patcher); + }; + patch_run.addFileArg(exe.getEmittedBin()); + const output = patch_run.addOutputFileArg("ghostty-vt.wasm"); + const artifact_install = b.addInstallFileWithDir( + output, + .bin, + "ghostty-vt.wasm", + ); + + return .{ + .step = &patch_run.step, + .artifact = &artifact_install.step, + .kind = .wasm, + .output = output, + .dsym = null, + .pkg_config = null, + }; +} + +pub fn initStatic( + b: *std.Build, + zig: *const GhosttyZig, +) !GhosttyLibVt { + return initLib(b, zig, .static); +} + +pub fn initShared( + b: *std.Build, + zig: *const GhosttyZig, +) !GhosttyLibVt { + return initLib(b, zig, .dynamic); +} + +fn initLib( + b: *std.Build, + zig: *const GhosttyZig, + linkage: std.builtin.LinkMode, +) !GhosttyLibVt { + const kind: Kind = switch (linkage) { + .static => .static, + .dynamic => .shared, + }; + const target = zig.vt.resolved_target.?; + const lib = b.addLibrary(.{ + .name = if (kind == .static) "ghostty-vt-static" else "ghostty-vt", + .linkage = linkage, + .root_module = zig.vt_c, + .version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 }, + }); + lib.installHeadersDirectory( + b.path("include/ghostty"), + "ghostty", + .{ .include_extensions = &.{".h"} }, + ); + + if (kind == .static) { + // These must be bundled since we're compiling into a static lib. + // Otherwise, you get undefined symbol errors. This could cause + // problems if you're linking multiple static Zig libraries but + // we'll cross that bridge when we get to it. + lib.bundle_compiler_rt = true; + lib.bundle_ubsan_rt = true; + + // Enable PIC so the static library can be linked into PIE + // executables, which is the default on most Linux distributions. + lib.root_module.pic = true; + } + + if (target.result.os.tag == .windows) { + // Zig's ubsan emits /exclude-symbols linker directives that + // are incompatible with the MSVC linker (LNK4229). + lib.bundle_ubsan_rt = false; + } + + if (lib.rootModuleTarget().abi.isAndroid()) { + // Support 16kb page sizes, required for Android 15+. + lib.link_z_max_page_size = 16384; // 16kb + + try @import("android_ndk").addPaths(b, lib); + } + + if (lib.rootModuleTarget().os.tag.isDarwin()) { + // Self-hosted x86_64 doesn't work for darwin. It may not work + // for other platforms too but definitely darwin. + lib.use_llvm = true; + + // This is required for codesign and dynamic linking to work. + lib.headerpad_max_install_names = true; + + // If we're not cross compiling then we try to find the Apple + // SDK using standard Apple tooling. + if (builtin.os.tag.isDarwin()) try @import("apple_sdk").addPaths(b, lib); + } + + // Get our debug symbols (only for shared libs; static libs aren't linked) + const dsymutil: ?std.Build.LazyPath = dsymutil: { + if (kind != .shared) break :dsymutil null; + if (!target.result.os.tag.isDarwin()) break :dsymutil null; + + const dsymutil = RunStep.create(b, "dsymutil"); + dsymutil.addArgs(&.{"dsymutil"}); + dsymutil.addFileArg(lib.getEmittedBin()); + dsymutil.addArgs(&.{"-o"}); + const output = dsymutil.addOutputFileArg("libghostty-vt.dSYM"); + break :dsymutil output; + }; + + // pkg-config + const pc: std.Build.LazyPath = pc: { + const wf = b.addWriteFiles(); + break :pc wf.add("libghostty-vt.pc", b.fmt( + \\prefix={s} + \\includedir=${{prefix}}/include + \\libdir=${{prefix}}/lib + \\ + \\Name: libghostty-vt + \\URL: https://github.com/ghostty-org/ghostty + \\Description: Ghostty VT library + \\Version: 0.1.0 + \\Cflags: -I${{includedir}} + \\Libs: -L${{libdir}} -lghostty-vt + \\Libs.private: {s} + \\Requires.private: {s} + , .{ b.install_prefix, libsPrivate(zig), requiresPrivate(b) })); + }; + + // For static libraries with vendored SIMD dependencies, combine + // all archives into a single fat archive so consumers only need + // to link one file. Skip on Windows where ar/libtool aren't available. + if (kind == .static and + zig.simd_libs.items.len > 0 and + target.result.os.tag != .windows) + { + var sources: SharedDeps.LazyPathList = .empty; + try sources.append(b.allocator, lib.getEmittedBin()); + try sources.appendSlice(b.allocator, zig.simd_libs.items); + + const combined = combineArchives(b, target, sources.items); + combined.step.dependOn(&lib.step); + + return .{ + .step = combined.step, + .artifact = &b.addInstallArtifact(lib, .{}).step, + .kind = kind, + .output = combined.output, + .dsym = dsymutil, + .pkg_config = pc, + }; + } + + return .{ + .step = &lib.step, + .artifact = &b.addInstallArtifact(lib, .{}).step, + .kind = kind, + .output = lib.getEmittedBin(), + .dsym = dsymutil, + .pkg_config = pc, + }; +} + +/// Combine multiple static archives into a single fat archive. +/// Uses libtool on Darwin and ar MRI scripts on other platforms. +fn combineArchives( + b: *std.Build, + target: std.Build.ResolvedTarget, + sources: []const std.Build.LazyPath, +) struct { step: *std.Build.Step, output: std.Build.LazyPath } { + if (target.result.os.tag.isDarwin()) { + const libtool = LibtoolStep.create(b, .{ + .name = "ghostty-vt", + .out_name = "libghostty-vt.a", + .sources = @constCast(sources), + }); + return .{ .step = libtool.step, .output = libtool.output }; + } + + // On non-Darwin, use an MRI script with ar -M to combine archives + // directly without extracting. This avoids issues with ar x + // producing full-path member names and read-only permissions. + const run = RunStep.create(b, "combine-archives ghostty-vt"); + run.addArgs(&.{ + "/bin/sh", "-c", + \\set -e + \\out="$1"; shift + \\script="CREATE $out" + \\for a in "$@"; do + \\ script="$script + \\ADDLIB $a" + \\done + \\script="$script + \\SAVE + \\END" + \\echo "$script" | ar -M + , + "_", + }); + const output = run.addOutputFileArg("libghostty-vt.a"); + for (sources) |source| run.addFileArg(source); + + return .{ .step = &run.step, .output = output }; +} + +/// Returns the Libs.private value for the pkg-config file. +/// This includes the C++ standard library needed by SIMD code. +/// +/// Zig compiles C++ code with LLVM's libc++ (not GNU libstdc++), +/// so consumers linking the static library need a libc++-compatible +/// toolchain: `zig cc`, `clang`, or GCC with `-lc++` installed. +fn libsPrivate( + zig: *const GhosttyZig, +) []const u8 { + return if (zig.vt_c.link_libcpp orelse false) "-lc++" else ""; +} + +/// Returns the Requires.private value for the pkg-config file. +/// When SIMD dependencies are provided by the system (via +/// -Dsystem-integration), we reference their pkg-config names so +/// that downstream consumers pick them up transitively. +fn requiresPrivate(b: *std.Build) []const u8 { + const system_simdutf = b.systemIntegrationOption("simdutf", .{}); + const system_highway = b.systemIntegrationOption("highway", .{ .default = false }); + + if (system_simdutf and system_highway) return "simdutf, libhwy"; + if (system_simdutf) return "simdutf"; + if (system_highway) return "libhwy"; + return ""; +} + +pub fn install( + self: *const GhosttyLibVt, + step: *std.Build.Step, +) void { + const b = step.owner; + step.dependOn(self.artifact); + if (self.pkg_config) |pkg_config| { + step.dependOn(&b.addInstallFileWithDir( + pkg_config, + .prefix, + "share/pkgconfig/libghostty-vt.pc", + ).step); + } +} diff --git a/vendor/libghostty-vt/src/build/GhosttyResources.zig b/vendor/libghostty-vt/src/build/GhosttyResources.zig new file mode 100644 index 00000000..6f857655 --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyResources.zig @@ -0,0 +1,442 @@ +const GhosttyResources = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const Config = @import("Config.zig"); +const RunStep = std.Build.Step.Run; +const SharedDeps = @import("SharedDeps.zig"); + +steps: []*std.Build.Step, + +pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !GhosttyResources { + var steps: std.ArrayList(*std.Build.Step) = .empty; + errdefer steps.deinit(b.allocator); + + // This is the exe used to generate some build data. + const build_data_exe = b.addExecutable(.{ + .name = "ghostty-build-data", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main_build_data.zig"), + .target = b.graph.host, + .strip = false, + .omit_frame_pointer = false, + .unwind_tables = .sync, + }), + }); + build_data_exe.linkLibC(); + + deps.help_strings.addImport(build_data_exe); + + // Terminfo + terminfo: { + const os_tag = cfg.target.result.os.tag; + const terminfo_share_dir = if (os_tag == .freebsd) + "site-terminfo" + else + "terminfo"; + + // Encode our terminfo + const run = b.addRunArtifact(build_data_exe); + run.addArg("+terminfo"); + const wf = b.addWriteFiles(); + const source = wf.addCopyFile(run.captureStdOut(), "ghostty.terminfo"); + + if (cfg.emit_terminfo) { + const source_install = b.addInstallFile( + source, + if (os_tag == .freebsd) + "share/site-terminfo/ghostty.terminfo" + else + "share/terminfo/ghostty.terminfo", + ); + + try steps.append(b.allocator, &source_install.step); + } + + // Windows doesn't have the binaries below. + if (os_tag == .windows) break :terminfo; + + // Convert to termcap source format if thats helpful to people and + // install it. The resulting value here is the termcap source in case + // that is used for other commands. + if (cfg.emit_termcap) { + const run_step = RunStep.create(b, "infotocap"); + run_step.addArg("infotocap"); + run_step.addFileArg(source); + const out_source = run_step.captureStdOut(); + _ = run_step.captureStdErr(); // so we don't see stderr + + const cap_install = b.addInstallFile( + out_source, + if (os_tag == .freebsd) + "share/site-terminfo/ghostty.termcap" + else + "share/terminfo/ghostty.termcap", + ); + + try steps.append(b.allocator, &cap_install.step); + } + + // Compile the terminfo source into a terminfo database + { + const run_step = RunStep.create(b, "tic"); + run_step.addArgs(&.{ "tic", "-x", "-o" }); + const path = run_step.addOutputFileArg(terminfo_share_dir); + + run_step.addFileArg(source); + _ = run_step.captureStdErr(); // so we don't see stderr + + // Ensure that `share/terminfo` is a directory, otherwise the `cp + // -R` will create a file named `share/terminfo` + const mkdir_step = RunStep.create(b, "make share/terminfo directory"); + switch (cfg.target.result.os.tag) { + // windows mkdir shouldn't need "-p" + .windows => mkdir_step.addArgs(&.{"mkdir"}), + else => mkdir_step.addArgs(&.{ "mkdir", "-p" }), + } + + mkdir_step.addArg(b.fmt( + "{s}/share/{s}", + .{ b.install_path, terminfo_share_dir }, + )); + + try steps.append(b.allocator, &mkdir_step.step); + + // Use cp -R instead of Step.InstallDir because we need to preserve + // symlinks in the terminfo database. Zig's InstallDir step doesn't + // handle symlinks correctly yet. + const copy_step = RunStep.create(b, "copy terminfo db"); + copy_step.addArgs(&.{ "cp", "-R" }); + copy_step.addFileArg(path); + copy_step.addArg(b.fmt("{s}/share", .{b.install_path})); + copy_step.step.dependOn(&mkdir_step.step); + try steps.append(b.allocator, ©_step.step); + } + } + + // Shell-integration + { + const install_step = b.addInstallDirectory(.{ + .source_dir = b.path("src/shell-integration"), + .install_dir = .{ .custom = "share" }, + .install_subdir = b.pathJoin(&.{ "ghostty", "shell-integration" }), + .exclude_extensions = &.{".md"}, + }); + try steps.append(b.allocator, &install_step.step); + } + + // Themes + if (cfg.emit_themes) { + if (b.lazyDependency("iterm2_themes", .{})) |upstream| { + const install_step = b.addInstallDirectory(.{ + .source_dir = upstream.path(""), + .install_dir = .{ .custom = "share" }, + .install_subdir = b.pathJoin(&.{ "ghostty", "themes" }), + .exclude_extensions = &.{".md"}, + }); + try steps.append(b.allocator, &install_step.step); + } + } + + // Fish shell completions + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+fish"); + const wf = b.addWriteFiles(); + _ = wf.addCopyFile(run.captureStdOut(), "ghostty.fish"); + + const install_step = b.addInstallDirectory(.{ + .source_dir = wf.getDirectory(), + .install_dir = .prefix, + .install_subdir = "share/fish/vendor_completions.d", + }); + try steps.append(b.allocator, &install_step.step); + } + + // zsh shell completions + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+zsh"); + const wf = b.addWriteFiles(); + _ = wf.addCopyFile(run.captureStdOut(), "_ghostty"); + + const install_step = b.addInstallDirectory(.{ + .source_dir = wf.getDirectory(), + .install_dir = .prefix, + .install_subdir = "share/zsh/site-functions", + }); + try steps.append(b.allocator, &install_step.step); + } + + // bash shell completions + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+bash"); + const wf = b.addWriteFiles(); + _ = wf.addCopyFile(run.captureStdOut(), "ghostty.bash"); + + const install_step = b.addInstallDirectory(.{ + .source_dir = wf.getDirectory(), + .install_dir = .prefix, + .install_subdir = "share/bash-completion/completions", + }); + try steps.append(b.allocator, &install_step.step); + } + + // Vim and Neovim plugin + { + const wf = b.addWriteFiles(); + + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+vim-syntax"); + _ = wf.addCopyFile(run.captureStdOut(), "syntax/ghostty.vim"); + } + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+vim-ftdetect"); + _ = wf.addCopyFile(run.captureStdOut(), "ftdetect/ghostty.vim"); + } + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+vim-ftplugin"); + _ = wf.addCopyFile(run.captureStdOut(), "ftplugin/ghostty.vim"); + } + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+vim-compiler"); + _ = wf.addCopyFile(run.captureStdOut(), "compiler/ghostty.vim"); + } + + const vim_step = b.addInstallDirectory(.{ + .source_dir = wf.getDirectory(), + .install_dir = .prefix, + .install_subdir = "share/vim/vimfiles", + }); + try steps.append(b.allocator, &vim_step.step); + + const neovim_step = b.addInstallDirectory(.{ + .source_dir = wf.getDirectory(), + .install_dir = .prefix, + .install_subdir = "share/nvim/site", + }); + try steps.append(b.allocator, &neovim_step.step); + } + + // Sublime syntax highlighting for bat cli tool + // NOTE: The current implementation requires symlinking the generated + // 'ghostty.sublime-syntax' file from zig-out to the '~.config/bat/syntaxes' + // directory. The syntax then needs to be mapped to the correct language in + // the config file within the '~.config/bat' directory + // (ex: --map-syntax "/Users/user/.config/ghostty/config.ghostty:Ghostty Config"). + { + const run = b.addRunArtifact(build_data_exe); + run.addArg("+sublime"); + const wf = b.addWriteFiles(); + _ = wf.addCopyFile(run.captureStdOut(), "ghostty.sublime-syntax"); + + const install_step = b.addInstallDirectory(.{ + .source_dir = wf.getDirectory(), + .install_dir = .prefix, + .install_subdir = "share/bat/syntaxes", + }); + try steps.append(b.allocator, &install_step.step); + } + + // App (Linux) + if (cfg.target.result.os.tag == .linux) try addLinuxAppResources( + b, + cfg, + &steps, + ); + + return .{ .steps = steps.items }; +} + +/// Add the resource files needed to make Ghostty a proper +/// Linux desktop application (for various desktop environments). +fn addLinuxAppResources( + b: *std.Build, + cfg: *const Config, + steps: *std.ArrayList(*std.Build.Step), +) !void { + assert(cfg.target.result.os.tag == .linux); + + // Background: + // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html + + const name = b.fmt("Ghostty{s}", .{ + switch (cfg.optimize) { + .Debug, .ReleaseSafe => " (Debug)", + .ReleaseFast, .ReleaseSmall => "", + }, + }); + + const app_id = b.fmt("com.mitchellh.ghostty{s}", .{ + switch (cfg.optimize) { + .Debug, .ReleaseSafe => "-debug", + .ReleaseFast, .ReleaseSmall => "", + }, + }); + + const exe_abs_path = b.fmt( + "{s}/bin/ghostty", + .{b.install_prefix}, + ); + + // The templates that we will process. The templates are in + // cmake format and will be processed and saved to the + // second element of the tuple. + const Template = struct { std.Build.LazyPath, []const u8 }; + const templates: []const Template = templates: { + var ts: std.ArrayList(Template) = .empty; + defer ts.deinit(b.allocator); + + // Desktop file so that we have an icon and other metadata + try ts.append(b.allocator, .{ + b.path("dist/linux/app.desktop.in"), + b.fmt("share/applications/{s}.desktop", .{app_id}), + }); + + // Service for DBus activation. + try ts.append(b.allocator, .{ + if (cfg.flatpak) + b.path("dist/linux/dbus.service.flatpak.in") + else + b.path("dist/linux/dbus.service.in"), + b.fmt("share/dbus-1/services/{s}.service", .{app_id}), + }); + + // `systemd` user service. This is kind of nasty but `systemd` looks for + // user services in different paths depending on if we are installed + // as a system package or not (lib vs. share) so we have to handle that + // here. We might be able to get away with always installing to both + // because it only ever searches in one... but I don't want to do that + // hack until we have to. + // + // The XDG Freedesktop Portal has a major undocumented requirement + // for programs that are launched/controlled by `systemd` to interact + // with the Portal. The unit must be named `app-.service`. The + // Portal uses the `systemd` unit name figure out what the program's + // application ID is and it will only look at unit names that begin with + // `app-`. I can find no place that this is documented other than by + // inspecting the code or the issue and PR that introduced this feature. + // See the following code: + // + // https://github.com/flatpak/xdg-desktop-portal/blob/7d4d48cf079147c8887da17ec6c3954acd5a285c/src/xdp-utils.c#L152-L220 + if (!cfg.flatpak) try ts.append(b.allocator, .{ + b.path("dist/linux/systemd.service.in"), + b.fmt( + "{s}/systemd/user/app-{s}.service", + .{ + if (b.graph.system_package_mode) "lib" else "share", + app_id, + }, + ), + }); + + // AppStream metainfo so that application has rich metadata + // within app stores + try ts.append(b.allocator, .{ + b.path("dist/linux/com.mitchellh.ghostty.metainfo.xml.in"), + b.fmt("share/metainfo/{s}.metainfo.xml", .{app_id}), + }); + + break :templates try ts.toOwnedSlice(b.allocator); + }; + + // Process all our templates + for (templates) |template| { + const tpl = b.addConfigHeader(.{ + .style = .{ .cmake = template[0] }, + }, .{ + .NAME = name, + .APPID = app_id, + .GHOSTTY = exe_abs_path, + }); + + // Template output has a single header line we want to remove. + // We use `tail` to do it since its part of the POSIX standard. + const tail = b.addSystemCommand(&.{ "tail", "-n", "+2" }); + tail.setStdIn(.{ .lazy_path = tpl.getOutput() }); + + const copy = b.addInstallFile( + tail.captureStdOut(), + template[1], + ); + + try steps.append(b.allocator, ©.step); + } + + // Right click menu action for Plasma desktop + try steps.append(b.allocator, &b.addInstallFile( + b.path("dist/linux/ghostty_dolphin.desktop"), + "share/kio/servicemenus/com.mitchellh.ghostty.desktop", + ).step); + + // Right click menu action for Nautilus. Note that this _must_ be named + // `ghostty.py`. Using the full app id causes problems (see #5468). + try steps.append(b.allocator, &b.addInstallFile( + b.path("dist/linux/ghostty_nautilus.py"), + "share/nautilus-python/extensions/ghostty.py", + ).step); + + // Various icons that our application can use, including the icon + // that will be used for the desktop. + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/16.png"), + "share/icons/hicolor/16x16/apps/com.mitchellh.ghostty.png", + ).step); + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/32.png"), + "share/icons/hicolor/32x32/apps/com.mitchellh.ghostty.png", + ).step); + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/128.png"), + "share/icons/hicolor/128x128/apps/com.mitchellh.ghostty.png", + ).step); + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/256.png"), + "share/icons/hicolor/256x256/apps/com.mitchellh.ghostty.png", + ).step); + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/512.png"), + "share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png", + ).step); + // Flatpaks only support icons up to 512x512. + if (!cfg.flatpak) { + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/1024.png"), + "share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png", + ).step); + } + + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/32.png"), + "share/icons/hicolor/16x16@2/apps/com.mitchellh.ghostty.png", + ).step); + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/64.png"), + "share/icons/hicolor/32x32@2/apps/com.mitchellh.ghostty.png", + ).step); + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/256.png"), + "share/icons/hicolor/128x128@2/apps/com.mitchellh.ghostty.png", + ).step); + try steps.append(b.allocator, &b.addInstallFile( + b.path("images/gnome/512.png"), + "share/icons/hicolor/256x256@2/apps/com.mitchellh.ghostty.png", + ).step); +} + +pub fn install(self: *const GhosttyResources) void { + const b = self.steps[0].owner; + self.addStepDependencies(b.getInstallStep()); +} + +pub fn addStepDependencies( + self: *const GhosttyResources, + other_step: *std.Build.Step, +) void { + for (self.steps) |step| other_step.dependOn(step); +} diff --git a/vendor/libghostty-vt/src/build/GhosttyWebdata.zig b/vendor/libghostty-vt/src/build/GhosttyWebdata.zig new file mode 100644 index 00000000..e29b20c2 --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyWebdata.zig @@ -0,0 +1,119 @@ +//! GhosttyWebdata generates all the Ghostty website data that is +//! merged with the website for things like config references. +const GhosttyWebdata = @This(); + +const std = @import("std"); +const SharedDeps = @import("SharedDeps.zig"); + +steps: []*std.Build.Step, + +pub fn init( + b: *std.Build, + deps: *const SharedDeps, +) !GhosttyWebdata { + var steps: std.ArrayList(*std.Build.Step) = .empty; + errdefer steps.deinit(b.allocator); + + { + const webgen_config = b.addExecutable(.{ + .name = "webgen_config", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = b.graph.host, + .strip = false, + .omit_frame_pointer = false, + .unwind_tables = .sync, + }), + }); + deps.help_strings.addImport(webgen_config); + + { + const buildconfig = config: { + var copy = deps.config.*; + copy.exe_entrypoint = .webgen_config; + break :config copy; + }; + + const options = b.addOptions(); + try buildconfig.addOptions(options); + webgen_config.root_module.addOptions("build_options", options); + } + + const webgen_config_step = b.addRunArtifact(webgen_config); + const webgen_config_out = webgen_config_step.captureStdOut(); + + try steps.append(b.allocator, &b.addInstallFile( + webgen_config_out, + "share/ghostty/webdata/config.mdx", + ).step); + } + + { + const webgen_actions = b.addExecutable(.{ + .name = "webgen_actions", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = b.graph.host, + }), + }); + deps.help_strings.addImport(webgen_actions); + + { + const buildconfig = config: { + var copy = deps.config.*; + copy.exe_entrypoint = .webgen_actions; + break :config copy; + }; + + const options = b.addOptions(); + try buildconfig.addOptions(options); + webgen_actions.root_module.addOptions("build_options", options); + } + + const webgen_actions_step = b.addRunArtifact(webgen_actions); + const webgen_actions_out = webgen_actions_step.captureStdOut(); + + try steps.append(b.allocator, &b.addInstallFile( + webgen_actions_out, + "share/ghostty/webdata/actions.mdx", + ).step); + } + + { + const webgen_commands = b.addExecutable(.{ + .name = "webgen_commands", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = b.graph.host, + }), + }); + deps.help_strings.addImport(webgen_commands); + + { + const buildconfig = config: { + var copy = deps.config.*; + copy.exe_entrypoint = .webgen_commands; + break :config copy; + }; + + const options = b.addOptions(); + try buildconfig.addOptions(options); + webgen_commands.root_module.addOptions("build_options", options); + } + + const webgen_commands_step = b.addRunArtifact(webgen_commands); + const webgen_commands_out = webgen_commands_step.captureStdOut(); + + try steps.append(b.allocator, &b.addInstallFile( + webgen_commands_out, + "share/ghostty/webdata/commands.mdx", + ).step); + } + + return .{ .steps = steps.items }; +} + +pub fn install(self: *const GhosttyWebdata) void { + const b = self.steps[0].owner; + for (self.steps) |step| b.getInstallStep().dependOn(step); +} diff --git a/vendor/libghostty-vt/src/build/GhosttyXCFramework.zig b/vendor/libghostty-vt/src/build/GhosttyXCFramework.zig new file mode 100644 index 00000000..3afeb907 --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyXCFramework.zig @@ -0,0 +1,104 @@ +const GhosttyXCFramework = @This(); + +const std = @import("std"); +const Config = @import("Config.zig"); +const SharedDeps = @import("SharedDeps.zig"); +const GhosttyLib = @import("GhosttyLib.zig"); +const XCFrameworkStep = @import("XCFrameworkStep.zig"); +const Target = @import("xcframework.zig").Target; + +xcframework: *XCFrameworkStep, +target: Target, + +pub fn init( + b: *std.Build, + deps: *const SharedDeps, + target: Target, +) !GhosttyXCFramework { + // Universal macOS build + const macos_universal = try GhosttyLib.initMacOSUniversal(b, deps); + + // Native macOS build + const macos_native = try GhosttyLib.initStatic(b, &try deps.retarget( + b, + Config.genericMacOSTarget(b, null), + )); + + // iOS + const ios = try GhosttyLib.initStatic(b, &try deps.retarget( + b, + b.resolveTargetQuery(.{ + .cpu_arch = .aarch64, + .os_tag = .ios, + .os_version_min = Config.osVersionMin(.ios), + .abi = null, + }), + )); + + // iOS Simulator + const ios_sim = try GhosttyLib.initStatic(b, &try deps.retarget( + b, + b.resolveTargetQuery(.{ + .cpu_arch = .aarch64, + .os_tag = .ios, + .os_version_min = Config.osVersionMin(.ios), + .abi = .simulator, + + // We force the Apple CPU model because the simulator + // doesn't support the generic CPU model as of Zig 0.14 due + // to missing "altnzcv" instructions, which is false. This + // surely can't be right but we can fix this if/when we get + // back to running simulator builds. + .cpu_model = .{ .explicit = &std.Target.aarch64.cpu.apple_a17 }, + }), + )); + + // The xcframework wraps our ghostty library so that we can link + // it to the final app built with Swift. + const xcframework = XCFrameworkStep.create(b, .{ + .name = "GhosttyKit", + .out_path = "macos/GhosttyKit.xcframework", + .libraries = switch (target) { + .universal => &.{ + .{ + .library = macos_universal.output, + .headers = b.path("include"), + .dsym = macos_universal.dsym, + }, + .{ + .library = ios.output, + .headers = b.path("include"), + .dsym = ios.dsym, + }, + .{ + .library = ios_sim.output, + .headers = b.path("include"), + .dsym = ios_sim.dsym, + }, + }, + + .native => &.{.{ + .library = macos_native.output, + .headers = b.path("include"), + .dsym = macos_native.dsym, + }}, + }, + }); + + return .{ + .xcframework = xcframework, + .target = target, + }; +} + +pub fn install(self: *const GhosttyXCFramework) void { + const b = self.xcframework.step.owner; + self.addStepDependencies(b.getInstallStep()); +} + +pub fn addStepDependencies( + self: *const GhosttyXCFramework, + other_step: *std.Build.Step, +) void { + other_step.dependOn(self.xcframework.step); +} diff --git a/vendor/libghostty-vt/src/build/GhosttyXcodebuild.zig b/vendor/libghostty-vt/src/build/GhosttyXcodebuild.zig new file mode 100644 index 00000000..81af994c --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyXcodebuild.zig @@ -0,0 +1,203 @@ +const Ghostty = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); +const RunStep = std.Build.Step.Run; +const Config = @import("Config.zig"); +const Docs = @import("GhosttyDocs.zig"); +const I18n = @import("GhosttyI18n.zig"); +const Resources = @import("GhosttyResources.zig"); +const XCFramework = @import("GhosttyXCFramework.zig"); + +build: *std.Build.Step.Run, +open: *std.Build.Step.Run, +copy: *std.Build.Step.Run, +xctest: *std.Build.Step.Run, + +pub const Deps = struct { + xcframework: *const XCFramework, + docs: *const Docs, + i18n: ?*const I18n, + resources: *const Resources, +}; + +pub fn init( + b: *std.Build, + config: *const Config, + deps: Deps, +) !Ghostty { + const xc_config = switch (config.optimize) { + .Debug => "Debug", + .ReleaseSafe, + .ReleaseSmall, + .ReleaseFast, + => "ReleaseLocal", + }; + + const xc_arch: ?[]const u8 = switch (deps.xcframework.target) { + // Universal is our default target, so we don't have to + // add anything. + .universal => null, + + // Native we need to override the architecture in the Xcode + // project with the -arch flag. + .native => switch (builtin.cpu.arch) { + .aarch64 => "arm64", + .x86_64 => "x86_64", + else => @panic("unsupported macOS arch"), + }, + }; + + const env = try std.process.getEnvMap(b.allocator); + const app_path = b.fmt("macos/build/{s}/Ghostty.app", .{xc_config}); + + // Our step to build the Ghostty macOS app. + const build = build: { + // External environment variables can mess up xcodebuild, so + // we create a new empty environment. + const env_map = try b.allocator.create(std.process.EnvMap); + env_map.* = .init(b.allocator); + if (env.get("PATH")) |v| try env_map.put("PATH", v); + + const step = RunStep.create(b, "xcodebuild"); + step.has_side_effects = true; + step.cwd = b.path("macos"); + step.env_map = env_map; + step.addArgs(&.{ + "xcodebuild", + "-target", + "Ghostty", + "-configuration", + xc_config, + }); + + // If we have a specific architecture, we need to pass it + // to xcodebuild. + if (xc_arch) |arch| step.addArgs(&.{ "-arch", arch }); + + // We need the xcframework + deps.xcframework.addStepDependencies(&step.step); + + // We also need all these resources because the xcode project + // references them via symlinks. + deps.resources.addStepDependencies(&step.step); + if (deps.i18n) |v| v.addStepDependencies(&step.step); + deps.docs.installDummy(&step.step); + + // Expect success + step.expectExitCode(0); + + break :build step; + }; + + const xctest = xctest: { + const env_map = try b.allocator.create(std.process.EnvMap); + env_map.* = .init(b.allocator); + if (env.get("PATH")) |v| try env_map.put("PATH", v); + + const step = RunStep.create(b, "xcodebuild test"); + step.has_side_effects = true; + step.cwd = b.path("macos"); + step.env_map = env_map; + step.addArgs(&.{ + "xcodebuild", + "test", + "-scheme", + "Ghostty", + "-skip-testing", + "GhosttyUITests", + }); + if (xc_arch) |arch| step.addArgs(&.{ "-arch", arch }); + + // We need the xcframework + deps.xcframework.addStepDependencies(&step.step); + + // We also need all these resources because the xcode project + // references them via symlinks. + deps.resources.addStepDependencies(&step.step); + if (deps.i18n) |v| v.addStepDependencies(&step.step); + deps.docs.installDummy(&step.step); + + // Expect success + step.expectExitCode(0); + + break :xctest step; + }; + + // Our step to open the resulting Ghostty app. + const open = open: { + const disable_save_state = RunStep.create(b, "disable save state"); + disable_save_state.has_side_effects = true; + disable_save_state.addArgs(&.{ + "/usr/libexec/PlistBuddy", + "-c", + // We'll have to change this to `Set` if we ever put this + // into our Info.plist. + "Add :NSQuitAlwaysKeepsWindows bool false", + b.fmt("{s}/Contents/Info.plist", .{app_path}), + }); + disable_save_state.expectExitCode(0); + disable_save_state.step.dependOn(&build.step); + + const open = RunStep.create(b, "run Ghostty app"); + open.has_side_effects = true; + open.cwd = b.path(""); + open.addArgs(&.{b.fmt( + "{s}/Contents/MacOS/ghostty", + .{app_path}, + )}); + + // Open depends on the app + open.step.dependOn(&build.step); + open.step.dependOn(&disable_save_state.step); + + // This overrides our default behavior and forces logs to show + // up on stderr (in addition to the centralized macOS log). + open.setEnvironmentVariable("GHOSTTY_LOG", "stderr,macos"); + + // Configure how we're launching + open.setEnvironmentVariable("GHOSTTY_MAC_LAUNCH_SOURCE", "zig_run"); + + if (b.args) |args| { + open.addArgs(args); + } + + break :open open; + }; + + // Our step to copy the app bundle to the install path. + // We have to use `cp -R` because there are symlinks in the + // bundle. + const copy = copy: { + const step = RunStep.create(b, "copy app bundle"); + step.addArgs(&.{ "cp", "-R" }); + step.addFileArg(b.path(app_path)); + step.addArg(b.fmt("{s}", .{b.install_path})); + step.step.dependOn(&build.step); + break :copy step; + }; + + return .{ + .build = build, + .open = open, + .copy = copy, + .xctest = xctest, + }; +} + +pub fn install(self: *const Ghostty) void { + const b = self.copy.step.owner; + b.getInstallStep().dependOn(&self.copy.step); +} + +pub fn installXcframework(self: *const Ghostty) void { + const b = self.build.step.owner; + b.getInstallStep().dependOn(&self.build.step); +} + +pub fn addTestStepDependencies( + self: *const Ghostty, + other_step: *std.Build.Step, +) void { + other_step.dependOn(&self.xctest.step); +} diff --git a/vendor/libghostty-vt/src/build/GhosttyZig.zig b/vendor/libghostty-vt/src/build/GhosttyZig.zig new file mode 100644 index 00000000..4901180d --- /dev/null +++ b/vendor/libghostty-vt/src/build/GhosttyZig.zig @@ -0,0 +1,102 @@ +//! GhosttyZig generates the Zig modules that Ghostty exports +//! for downstream usage. +const GhosttyZig = @This(); + +const std = @import("std"); +const Config = @import("Config.zig"); +const SharedDeps = @import("SharedDeps.zig"); +const TerminalBuildOptions = @import("../terminal/build_options.zig").Options; + +/// The `_c`-suffixed modules are built with the C ABI enabled. +vt: *std.Build.Module, +vt_c: *std.Build.Module, + +/// Static library paths for vendored SIMD dependencies. Populated +/// only when the dependencies are built from source (not provided +/// by the system via -Dsystem-integration). Used to produce a +/// combined static archive for downstream consumers. +simd_libs: SharedDeps.LazyPathList, + +pub fn init( + b: *std.Build, + cfg: *const Config, + deps: *const SharedDeps, +) !GhosttyZig { + // Terminal module build options + var vt_options = cfg.terminalOptions(); + vt_options.artifact = .lib; + // We presently don't allow Oniguruma in our Zig module at all. + // We should expose this as a build option in the future so we can + // conditionally do this. + vt_options.oniguruma = false; + + var simd_libs: SharedDeps.LazyPathList = .empty; + + return .{ + .vt = try initVt( + "ghostty-vt", + b, + cfg, + deps, + vt_options, + null, + ), + + .vt_c = try initVt( + "ghostty-vt-c", + b, + cfg, + deps, + options: { + var dup = vt_options; + dup.c_abi = true; + break :options dup; + }, + &simd_libs, + ), + + .simd_libs = simd_libs, + }; +} + +fn initVt( + name: []const u8, + b: *std.Build, + cfg: *const Config, + deps: *const SharedDeps, + vt_options: TerminalBuildOptions, + simd_libs: ?*SharedDeps.LazyPathList, +) !*std.Build.Module { + // General build options + const general_options = b.addOptions(); + try cfg.addOptions(general_options); + + const vt = b.addModule(name, .{ + .root_source_file = b.path("src/lib_vt.zig"), + .target = cfg.target, + .optimize = cfg.optimize, + + // SIMD require libc/libcpp (both) but otherwise we don't care. + // On MSVC, we must not use linkLibCpp because Zig passes + // -nostdinc++ and adds its bundled libc++/libc++abi headers + // which conflict with MSVC's C++ runtime. The MSVC SDK dirs + // added via link_libc contain both C and C++ headers. + .link_libc = if (cfg.simd) true else null, + .link_libcpp = if (cfg.simd and cfg.target.result.abi != .msvc) true else null, + }); + vt.addOptions("build_options", general_options); + vt_options.add(b, vt); + + // We always need unicode tables + deps.unicode_tables.addModuleImport(vt); + + // We need uucode for grapheme break support + deps.addUucode(b, vt, cfg.target, cfg.optimize); + + // If SIMD is enabled, add all our SIMD dependencies. + if (cfg.simd) { + try SharedDeps.addSimd(b, vt, simd_libs); + } + + return vt; +} diff --git a/vendor/libghostty-vt/src/build/GitVersion.zig b/vendor/libghostty-vt/src/build/GitVersion.zig new file mode 100644 index 00000000..8b368d2c --- /dev/null +++ b/vendor/libghostty-vt/src/build/GitVersion.zig @@ -0,0 +1,83 @@ +const Version = @This(); + +const std = @import("std"); + +/// The short hash (7 characters) of the latest commit. +short_hash: []const u8, + +/// True if there was a diff at build time. +changes: bool, + +/// The tag -- if any -- that this commit is a part of. +tag: ?[]const u8, + +/// The branch that was checked out at the time of the build. +branch: []const u8, + +/// Initialize the version and detect it from the Git environment. This +/// allocates using the build allocator and doesn't free. +pub fn detect(b: *std.Build) !Version { + // Execute a bunch of git commands to determine the automatic version. + var code: u8 = 0; + const branch: []const u8 = b: { + const tmp: []u8 = b.runAllowFail( + &[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "rev-parse", "--abbrev-ref", "HEAD" }, + &code, + .Ignore, + ) catch |err| switch (err) { + error.FileNotFound => return error.GitNotFound, + error.ExitCodeFailure => return error.GitNotRepository, + else => return err, + }; + // Replace any '/' with '-' as including slashes will mess up building + // the dist tarball - the tarball uses the branch as part of the + // name and including slashes means that the tarball will end up in + // subdirectories instead of where it's supposed to be. + std.mem.replaceScalar(u8, tmp, '/', '-'); + break :b tmp; + }; + + const short_hash = short_hash: { + const output = b.runAllowFail( + &[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "-c", "log.showSignature=false", "log", "--pretty=format:%h", "-n", "1" }, + &code, + .Ignore, + ) catch |err| switch (err) { + error.FileNotFound => return error.GitNotFound, + else => return err, + }; + + break :short_hash std.mem.trimRight(u8, output, "\r\n "); + }; + + const tag = b.runAllowFail( + &[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "describe", "--exact-match", "--tags" }, + &code, + .Ignore, + ) catch |err| switch (err) { + error.FileNotFound => return error.GitNotFound, + error.ExitCodeFailure => "", // expected + else => return err, + }; + + _ = b.runAllowFail(&[_][]const u8{ + "git", + "-C", + b.build_root.path orelse ".", + "diff", + "--quiet", + "--exit-code", + }, &code, .Ignore) catch |err| switch (err) { + error.FileNotFound => return error.GitNotFound, + error.ExitCodeFailure => {}, // expected + else => return err, + }; + const changes = code != 0; + + return .{ + .short_hash = short_hash, + .changes = changes, + .tag = if (tag.len > 0) std.mem.trimRight(u8, tag, "\r\n ") else null, + .branch = std.mem.trimRight(u8, branch, "\r\n "), + }; +} diff --git a/vendor/libghostty-vt/src/build/HelpStrings.zig b/vendor/libghostty-vt/src/build/HelpStrings.zig new file mode 100644 index 00000000..96505cab --- /dev/null +++ b/vendor/libghostty-vt/src/build/HelpStrings.zig @@ -0,0 +1,56 @@ +const HelpStrings = @This(); + +const std = @import("std"); +const Config = @import("Config.zig"); + +/// The "helpgen" exe. +exe: *std.Build.Step.Compile, + +/// The output path for the help strings. +output: std.Build.LazyPath, + +pub fn init(b: *std.Build, cfg: *const Config) !HelpStrings { + const exe = b.addExecutable(.{ + .name = "helpgen", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/helpgen.zig"), + .target = b.graph.host, + .strip = false, + .omit_frame_pointer = false, + .unwind_tables = .sync, + }), + }); + + const help_config = config: { + var copy = cfg.*; + copy.exe_entrypoint = .helpgen; + break :config copy; + }; + const options = b.addOptions(); + try help_config.addOptions(options); + exe.root_module.addOptions("build_options", options); + + const help_run = b.addRunArtifact(exe); + + // Generated Zig files have to end with .zig + const wf = b.addWriteFiles(); + const output = wf.addCopyFile(help_run.captureStdOut(), "helpgen.zig"); + + return .{ + .exe = exe, + .output = output, + }; +} + +/// Add the "help_strings" import. +pub fn addImport(self: *const HelpStrings, step: *std.Build.Step.Compile) void { + self.output.addStepDependencies(&step.step); + step.root_module.addAnonymousImport("help_strings", .{ + .root_source_file = self.output, + }); +} + +/// Install the help exe +pub fn install(self: *const HelpStrings) void { + self.exe.step.owner.installArtifact(self.exe); +} diff --git a/vendor/libghostty-vt/src/build/LibtoolStep.zig b/vendor/libghostty-vt/src/build/LibtoolStep.zig new file mode 100644 index 00000000..856a33aa --- /dev/null +++ b/vendor/libghostty-vt/src/build/LibtoolStep.zig @@ -0,0 +1,78 @@ +//! A zig builder step that runs "libtool" against a list of libraries +//! in order to create a single combined static library. +const LibtoolStep = @This(); + +const std = @import("std"); +const Step = std.Build.Step; +const RunStep = std.Build.Step.Run; +const LazyPath = std.Build.LazyPath; + +pub const Options = struct { + /// The name of this step. + name: []const u8, + + /// The filename (not the path) of the file to create. This will + /// be placed in a unique hashed directory. Use out_path to access. + out_name: []const u8, + + /// Library files (.a) to combine. + sources: []LazyPath, +}; + +/// The step to depend on. +step: *Step, + +/// The output file from the libtool run. +output: LazyPath, + +/// Run libtool against a list of library files to combine into a single +/// static library. +pub fn create(b: *std.Build, opts: Options) *LibtoolStep { + const self = b.allocator.create(LibtoolStep) catch @panic("OOM"); + + const run_step = RunStep.create(b, b.fmt("libtool {s}", .{opts.name})); + run_step.addArgs(&.{ "libtool", "-static", "-o" }); + const output = run_step.addOutputFileArg(opts.out_name); + for (opts.sources, 0..) |source, i| { + run_step.addFileArg(normalizeArchive( + b, + opts.name, + opts.out_name, + i, + source, + )); + } + + self.* = .{ + .step = &run_step.step, + .output = output, + }; + + return self; +} + +fn normalizeArchive( + b: *std.Build, + step_name: []const u8, + out_name: []const u8, + index: usize, + source: LazyPath, +) LazyPath { + // Newer Xcode libtool can drop 64-bit archive members if the input + // archive layout doesn't match what it expects. ranlib rewrites the + // archive without flattening members through the filesystem, so we + // normalize each source archive first. This is a Zig/toolchain + // interoperability workaround, not a Ghostty archive format change. + const run_step = RunStep.create( + b, + b.fmt("ranlib {s} #{d}", .{ step_name, index }), + ); + run_step.addArgs(&.{ + "/bin/sh", + "-c", + "/bin/cp \"$1\" \"$2\" && /usr/bin/ranlib \"$2\"", + "_", + }); + run_step.addFileArg(source); + return run_step.addOutputFileArg(b.fmt("{d}-{s}", .{ index, out_name })); +} diff --git a/vendor/libghostty-vt/src/build/LipoStep.zig b/vendor/libghostty-vt/src/build/LipoStep.zig new file mode 100644 index 00000000..c9c1530e --- /dev/null +++ b/vendor/libghostty-vt/src/build/LipoStep.zig @@ -0,0 +1,42 @@ +//! A zig builder step that runs "lipo" on two binaries to create +//! a universal binary. +const LipoStep = @This(); + +const std = @import("std"); +const Step = std.Build.Step; +const RunStep = std.Build.Step.Run; +const LazyPath = std.Build.LazyPath; + +pub const Options = struct { + /// The name of the xcframework to create. + name: []const u8, + + /// The filename (not the path) of the file to create. + out_name: []const u8, + + /// Library file (dylib, a) to package. + input_a: LazyPath, + input_b: LazyPath, +}; + +step: *Step, + +/// Resulting binary +output: LazyPath, + +pub fn create(b: *std.Build, opts: Options) *LipoStep { + const self = b.allocator.create(LipoStep) catch @panic("OOM"); + + const run_step = RunStep.create(b, b.fmt("lipo {s}", .{opts.name})); + run_step.addArgs(&.{ "lipo", "-create", "-output" }); + const output = run_step.addOutputFileArg(opts.out_name); + run_step.addFileArg(opts.input_a); + run_step.addFileArg(opts.input_b); + + self.* = .{ + .step = &run_step.step, + .output = output, + }; + + return self; +} diff --git a/vendor/libghostty-vt/src/build/MetallibStep.zig b/vendor/libghostty-vt/src/build/MetallibStep.zig new file mode 100644 index 00000000..fcf3055f --- /dev/null +++ b/vendor/libghostty-vt/src/build/MetallibStep.zig @@ -0,0 +1,84 @@ +/// A zig build step that compiles a set of ".metal" files into a +/// ".metallib" file. +const MetallibStep = @This(); + +const std = @import("std"); +const Step = std.Build.Step; +const RunStep = std.Build.Step.Run; +const LazyPath = std.Build.LazyPath; + +pub const Options = struct { + /// The name of the xcframework to create. + name: []const u8, + + /// The OS being targeted + target: std.Build.ResolvedTarget, + + /// The Metal source files. + sources: []const LazyPath, +}; + +step: *Step, +output: LazyPath, + +pub fn create(b: *std.Build, opts: Options) ?*MetallibStep { + const sdk = switch (opts.target.result.os.tag) { + .macos => "macosx", + .ios => switch (opts.target.result.abi) { + // The iOS simulator uses the same SDK for Metal as the device, + // but the minimum version tag causes different behaviors. + .simulator => "iphoneos", + else => "iphoneos", + }, + else => return null, + }; + const platform_version_arg = switch (opts.target.result.os.tag) { + .macos => "-mmacos-version-min", + .ios => switch (opts.target.result.abi) { + .simulator => "-mios-simulator-version-min", + else => "-mios-version-min", + }, + else => null, + }; + + const self = b.allocator.create(MetallibStep) catch @panic("OOM"); + + const min_version = if (opts.target.query.os_version_min) |v| + b.fmt("{f}", .{v.semver}) + else switch (opts.target.result.os.tag) { + .macos => "10.14", + .ios => "11.0", + else => unreachable, + }; + + const run_ir = RunStep.create( + b, + b.fmt("metal {s}", .{opts.name}), + ); + run_ir.addArgs(&.{ "/usr/bin/xcrun", "-sdk", sdk, "metal", "-o" }); + const output_ir = run_ir.addOutputFileArg(b.fmt("{s}.ir", .{opts.name})); + run_ir.addArgs(&.{"-c"}); + for (opts.sources) |source| run_ir.addFileArg(source); + if (platform_version_arg) |arg| { + run_ir.addArgs(&.{b.fmt( + "{s}={s}", + .{ arg, min_version }, + )}); + } + + const run_lib = RunStep.create( + b, + b.fmt("metallib {s}", .{opts.name}), + ); + run_lib.addArgs(&.{ "/usr/bin/xcrun", "-sdk", sdk, "metallib", "-o" }); + const output_lib = run_lib.addOutputFileArg(b.fmt("{s}.metallib", .{opts.name})); + run_lib.addFileArg(output_ir); + run_lib.step.dependOn(&run_ir.step); + + self.* = .{ + .step = &run_lib.step, + .output = output_lib, + }; + + return self; +} diff --git a/vendor/libghostty-vt/src/build/SharedDeps.zig b/vendor/libghostty-vt/src/build/SharedDeps.zig new file mode 100644 index 00000000..aa63c082 --- /dev/null +++ b/vendor/libghostty-vt/src/build/SharedDeps.zig @@ -0,0 +1,974 @@ +const SharedDeps = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); + +const Config = @import("Config.zig"); +const HelpStrings = @import("HelpStrings.zig"); +const MetallibStep = @import("MetallibStep.zig"); +const UnicodeTables = @import("UnicodeTables.zig"); +const GhosttyFrameData = @import("GhosttyFrameData.zig"); +const DistResource = @import("GhosttyDist.zig").Resource; + +config: *const Config, + +options: *std.Build.Step.Options, +help_strings: HelpStrings, +metallib: ?*MetallibStep, +unicode_tables: UnicodeTables, +framedata: GhosttyFrameData, +uucode_tables: std.Build.LazyPath, + +/// Used to keep track of a list of file sources. +pub const LazyPathList = std.ArrayList(std.Build.LazyPath); + +pub fn init(b: *std.Build, cfg: *const Config) !SharedDeps { + const uucode_tables = blk: { + const uucode = b.dependency("uucode", .{ + .build_config_path = b.path("src/build/uucode_config.zig"), + }); + + break :blk uucode.namedLazyPath("tables.zig"); + }; + + var result: SharedDeps = .{ + .config = cfg, + .help_strings = try .init(b, cfg), + .unicode_tables = try .init(b, uucode_tables), + .framedata = try .init(b), + .uucode_tables = uucode_tables, + + // Setup by retarget + .options = undefined, + .metallib = undefined, + }; + try result.initTarget(b, cfg.target); + if (cfg.emit_unicode_table_gen) result.unicode_tables.install(b); + return result; +} + +/// Retarget our dependencies for another build target. Modifies in-place. +pub fn retarget( + self: *const SharedDeps, + b: *std.Build, + target: std.Build.ResolvedTarget, +) !SharedDeps { + var result = self.*; + try result.initTarget(b, target); + return result; +} + +/// Change the exe entrypoint. +pub fn changeEntrypoint( + self: *const SharedDeps, + b: *std.Build, + entrypoint: Config.ExeEntrypoint, +) !SharedDeps { + // Change our config + const config = try b.allocator.create(Config); + config.* = self.config.*; + config.exe_entrypoint = entrypoint; + + var result = self.*; + result.config = config; + result.options = b.addOptions(); + try config.addOptions(result.options); + + return result; +} + +fn initTarget( + self: *SharedDeps, + b: *std.Build, + target: std.Build.ResolvedTarget, +) !void { + // Update our metallib + self.metallib = .create(b, .{ + .name = "Ghostty", + .target = target, + .sources = &.{b.path("src/renderer/shaders/shaders.metal")}, + }); + + // Change our config + const config = try b.allocator.create(Config); + config.* = self.config.*; + config.target = target; + self.config = config; + + // Setup our shared build options + self.options = b.addOptions(); + try self.config.addOptions(self.options); +} + +pub fn add( + self: *const SharedDeps, + step: *std.Build.Step.Compile, +) !LazyPathList { + const b = step.step.owner; + + // We could use our config.target/optimize fields here but its more + // correct to always match our step. + const target = step.root_module.resolved_target.?; + const optimize = step.root_module.optimize.?; + + // We maintain a list of our static libraries and return it so that + // we can build a single fat static library for the final app. + var static_libs: LazyPathList = .empty; + errdefer static_libs.deinit(b.allocator); + + // WARNING: This is a hack! + // If we're cross-compiling to Darwin then we don't add any deps. + // We don't support cross-compiling to Darwin but due to the way + // lazy dependencies work with Zig, we call this function. So we just + // bail. The build will fail but the build would've failed anyways. + // And this lets other non-platform-specific targets like `-Demit-lib-vt` + // cross-compile properly. + if (!builtin.target.os.tag.isDarwin() and + self.config.target.result.os.tag.isDarwin()) + { + return static_libs; + } + + // Every exe gets build options populated + step.root_module.addOptions("build_options", self.options); + + // Every exe needs the terminal options + self.config.terminalOptions().add(b, step.root_module); + + // C imports for locale constants and functions + { + const c = b.addTranslateC(.{ + .root_source_file = b.path("src/os/locale.c"), + .target = target, + .optimize = optimize, + }); + if (target.result.os.tag.isDarwin()) { + const libc = try std.zig.LibCInstallation.findNative(.{ + .allocator = b.allocator, + .target = &target.result, + .verbose = false, + }); + c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? }); + } + step.root_module.addImport("locale-c", c.createModule()); + } + + // C imports needed to manage/create PTYs + switch (target.result.os.tag) { + .freebsd, + .linux, + .macos, + => { + const c = b.addTranslateC(.{ + .root_source_file = b.path("src/pty.c"), + .target = target, + .optimize = optimize, + }); + switch (target.result.os.tag) { + .macos => { + const libc = try std.zig.LibCInstallation.findNative(.{ + .allocator = b.allocator, + .target = &target.result, + .verbose = false, + }); + c.addSystemIncludePath(.{ .cwd_relative = libc.sys_include_dir.? }); + }, + else => {}, + } + step.root_module.addImport("pty-c", c.createModule()); + }, + else => {}, + } + + // Freetype. We always include this even if our font backend doesn't + // use it because Dear Imgui uses Freetype. + _ = b.systemIntegrationOption("freetype", .{}); // Shows it in help + if (b.lazyDependency("freetype", .{ + .target = target, + .optimize = optimize, + .@"enable-libpng" = true, + })) |freetype_dep| { + step.root_module.addImport( + "freetype", + freetype_dep.module("freetype"), + ); + + if (b.systemIntegrationOption("freetype", .{})) { + step.linkSystemLibrary2("bzip2", dynamic_link_opts); + step.linkSystemLibrary2("freetype2", dynamic_link_opts); + } else { + step.linkLibrary(freetype_dep.artifact("freetype")); + try static_libs.append( + b.allocator, + freetype_dep.artifact("freetype").getEmittedBin(), + ); + } + } + + // Harfbuzz + _ = b.systemIntegrationOption("harfbuzz", .{}); // Shows it in help + if (self.config.font_backend.hasHarfbuzz()) { + if (b.lazyDependency("harfbuzz", .{ + .target = target, + .optimize = optimize, + .@"enable-freetype" = self.config.font_backend.hasFreetype(), + .@"enable-coretext" = self.config.font_backend.hasCoretext(), + })) |harfbuzz_dep| { + step.root_module.addImport( + "harfbuzz", + harfbuzz_dep.module("harfbuzz"), + ); + if (b.systemIntegrationOption("harfbuzz", .{})) { + step.linkSystemLibrary2("harfbuzz", dynamic_link_opts); + } else { + step.linkLibrary(harfbuzz_dep.artifact("harfbuzz")); + try static_libs.append( + b.allocator, + harfbuzz_dep.artifact("harfbuzz").getEmittedBin(), + ); + } + } + } + + // Fontconfig + _ = b.systemIntegrationOption("fontconfig", .{}); // Shows it in help + if (self.config.font_backend.hasFontconfig()) { + if (b.lazyDependency("fontconfig", .{ + .target = target, + .optimize = optimize, + })) |fontconfig_dep| { + step.root_module.addImport( + "fontconfig", + fontconfig_dep.module("fontconfig"), + ); + + if (b.systemIntegrationOption("fontconfig", .{})) { + step.linkSystemLibrary2("fontconfig", dynamic_link_opts); + } else { + step.linkLibrary(fontconfig_dep.artifact("fontconfig")); + try static_libs.append( + b.allocator, + fontconfig_dep.artifact("fontconfig").getEmittedBin(), + ); + } + } + } + + // Libpng - Ghostty doesn't actually use this directly, its only used + // through dependencies, so we only need to add it to our static + // libs list if we're not using system integration. The dependencies + // will handle linking it. + if (!b.systemIntegrationOption("libpng", .{})) { + if (b.lazyDependency("libpng", .{ + .target = target, + .optimize = optimize, + })) |libpng_dep| { + step.linkLibrary(libpng_dep.artifact("png")); + try static_libs.append( + b.allocator, + libpng_dep.artifact("png").getEmittedBin(), + ); + } + } + + // Zlib - same as libpng, only used through dependencies. + if (!b.systemIntegrationOption("zlib", .{})) { + if (b.lazyDependency("zlib", .{ + .target = target, + .optimize = optimize, + })) |zlib_dep| { + step.linkLibrary(zlib_dep.artifact("z")); + try static_libs.append( + b.allocator, + zlib_dep.artifact("z").getEmittedBin(), + ); + } + } + + // Oniguruma + if (b.lazyDependency("oniguruma", .{ + .target = target, + .optimize = optimize, + })) |oniguruma_dep| { + step.root_module.addImport( + "oniguruma", + oniguruma_dep.module("oniguruma"), + ); + if (b.systemIntegrationOption("oniguruma", .{})) { + step.linkSystemLibrary2("oniguruma", dynamic_link_opts); + } else { + step.linkLibrary(oniguruma_dep.artifact("oniguruma")); + try static_libs.append( + b.allocator, + oniguruma_dep.artifact("oniguruma").getEmittedBin(), + ); + } + } + + // Glslang + if (b.lazyDependency("glslang", .{ + .target = target, + .optimize = optimize, + })) |glslang_dep| { + step.root_module.addImport("glslang", glslang_dep.module("glslang")); + if (b.systemIntegrationOption("glslang", .{})) { + step.linkSystemLibrary2("glslang", dynamic_link_opts); + step.linkSystemLibrary2( + "glslang-default-resource-limits", + dynamic_link_opts, + ); + } else { + step.linkLibrary(glslang_dep.artifact("glslang")); + try static_libs.append( + b.allocator, + glslang_dep.artifact("glslang").getEmittedBin(), + ); + } + } + + // Spirv-cross + if (b.lazyDependency("spirv_cross", .{ + .target = target, + .optimize = optimize, + })) |spirv_cross_dep| { + step.root_module.addImport( + "spirv_cross", + spirv_cross_dep.module("spirv_cross"), + ); + if (b.systemIntegrationOption("spirv-cross", .{})) { + step.linkSystemLibrary2("spirv-cross-c-shared", dynamic_link_opts); + } else { + step.linkLibrary(spirv_cross_dep.artifact("spirv_cross")); + try static_libs.append( + b.allocator, + spirv_cross_dep.artifact("spirv_cross").getEmittedBin(), + ); + } + } + + // Sentry + if (self.config.sentry) { + if (b.lazyDependency("sentry", .{ + .target = target, + .optimize = optimize, + .backend = .breakpad, + })) |sentry_dep| { + step.root_module.addImport( + "sentry", + sentry_dep.module("sentry"), + ); + step.linkLibrary(sentry_dep.artifact("sentry")); + try static_libs.append( + b.allocator, + sentry_dep.artifact("sentry").getEmittedBin(), + ); + + // We also need to include breakpad in the static libs. + if (sentry_dep.builder.lazyDependency("breakpad", .{ + .target = target, + .optimize = optimize, + })) |breakpad_dep| { + try static_libs.append( + b.allocator, + breakpad_dep.artifact("breakpad").getEmittedBin(), + ); + } + } + } + + // Simd + if (self.config.simd) try addSimd( + b, + step.root_module, + &static_libs, + ); + + // Wasm we do manually since it is such a different build. + if (step.rootModuleTarget().cpu.arch == .wasm32) { + if (b.lazyDependency("zig_js", .{ + .target = target, + .optimize = optimize, + })) |js_dep| { + step.root_module.addImport( + "zig-js", + js_dep.module("zig-js"), + ); + } + + return static_libs; + } + + // On Linux, we need to add a couple common library paths that aren't + // on the standard search list. i.e. GTK is often in /usr/lib/x86_64-linux-gnu + // on x86_64. + if (step.rootModuleTarget().os.tag == .linux) { + const triple = try step.rootModuleTarget().linuxTriple(b.allocator); + const path = b.fmt("/usr/lib/{s}", .{triple}); + if (std.fs.accessAbsolute(path, .{})) { + step.addLibraryPath(.{ .cwd_relative = path }); + } else |_| {} + } + + // C files + step.linkLibC(); + step.addIncludePath(b.path("src/stb")); + step.addCSourceFiles(.{ .files = &.{"src/stb/stb.c"} }); + if (step.rootModuleTarget().os.tag == .linux) { + step.addIncludePath(b.path("src/apprt/gtk")); + } + + // libcpp is required for various dependencies. On MSVC, we must + // not use linkLibCpp because Zig unconditionally passes -nostdinc++ + // and then adds its bundled libc++/libc++abi include paths, which + // conflict with MSVC's own C++ runtime headers. The MSVC SDK + // include directories (already added via linkLibC above) contain + // both C and C++ headers, so linkLibCpp is not needed. + if (step.rootModuleTarget().abi != .msvc) { + step.linkLibCpp(); + } + + // We always require the system SDK so that our system headers are available. + // This makes things like `os/log.h` available for cross-compiling. + if (step.rootModuleTarget().os.tag.isDarwin()) { + try @import("apple_sdk").addPaths(b, step); + + const metallib = self.metallib.?; + metallib.output.addStepDependencies(&step.step); + step.root_module.addAnonymousImport("ghostty_metallib", .{ + .root_source_file = metallib.output, + }); + } + + // Other dependencies, mostly pure Zig + if (b.lazyDependency("opengl", .{})) |dep| { + step.root_module.addImport("opengl", dep.module("opengl")); + } + if (b.lazyDependency("vaxis", .{})) |dep| { + step.root_module.addImport("vaxis", dep.module("vaxis")); + } + if (b.lazyDependency("wuffs", .{ + .target = target, + .optimize = optimize, + })) |dep| { + step.root_module.addImport("wuffs", dep.module("wuffs")); + } + if (b.lazyDependency("libxev", .{ + .target = target, + .optimize = optimize, + })) |dep| { + step.root_module.addImport("xev", dep.module("xev")); + } + if (b.lazyDependency("z2d", .{ + .target = target, + .optimize = optimize, + })) |dep| { + step.root_module.addImport("z2d", dep.module("z2d")); + } + self.addUucode(b, step.root_module, target, optimize); + if (b.lazyDependency("zf", .{ + .target = target, + .optimize = optimize, + .with_tui = false, + })) |dep| { + step.root_module.addImport("zf", dep.module("zf")); + } + + // Mac Stuff + if (step.rootModuleTarget().os.tag.isDarwin()) { + if (b.lazyDependency("zig_objc", .{ + .target = target, + .optimize = optimize, + })) |objc_dep| { + step.root_module.addImport( + "objc", + objc_dep.module("objc"), + ); + } + + if (b.lazyDependency("macos", .{ + .target = target, + .optimize = optimize, + })) |macos_dep| { + step.root_module.addImport( + "macos", + macos_dep.module("macos"), + ); + step.linkLibrary( + macos_dep.artifact("macos"), + ); + try static_libs.append( + b.allocator, + macos_dep.artifact("macos").getEmittedBin(), + ); + } + + if (self.config.renderer == .opengl) { + step.linkFramework("OpenGL"); + } + + // Apple platforms do not include libc libintl so we bundle it. + // This is LGPL but since our source code is open source we are + // in compliance with the LGPL since end users can modify this + // build script to replace the bundled libintl with their own. + if (b.lazyDependency("libintl", .{ + .target = target, + .optimize = optimize, + })) |libintl_dep| { + step.linkLibrary(libintl_dep.artifact("intl")); + try static_libs.append( + b.allocator, + libintl_dep.artifact("intl").getEmittedBin(), + ); + } + } + + // cimgui + if (b.lazyDependency("dcimgui", .{ + .target = target, + .optimize = optimize, + .freetype = true, + .@"backend-metal" = target.result.os.tag.isDarwin(), + .@"backend-osx" = target.result.os.tag == .macos, + // OpenGL3 backend should only be built on non-Apple targets. + // Apple platforms use Metal (and macOS may also use the OSX backend). + .@"backend-opengl3" = !target.result.os.tag.isDarwin(), + })) |dep| { + step.root_module.addImport("dcimgui", dep.module("dcimgui")); + step.linkLibrary(dep.artifact("dcimgui")); + try static_libs.append( + b.allocator, + dep.artifact("dcimgui").getEmittedBin(), + ); + } + + // Fonts + { + // JetBrains Mono + if (b.lazyDependency("jetbrains_mono", .{})) |jb_mono| { + step.root_module.addAnonymousImport( + "jetbrains_mono_regular", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Regular.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_bold", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Bold.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_italic", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Italic.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_bold_italic", + .{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-BoldItalic.ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_variable", + .{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono[wght].ttf") }, + ); + step.root_module.addAnonymousImport( + "jetbrains_mono_variable_italic", + .{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono-Italic[wght].ttf") }, + ); + } + + // Symbols-only nerd font + if (b.lazyDependency("nerd_fonts_symbols_only", .{})) |nf_symbols| { + step.root_module.addAnonymousImport( + "nerd_fonts_symbols_only", + .{ .root_source_file = nf_symbols.path("SymbolsNerdFont-Regular.ttf") }, + ); + } + } + + // If we're building an exe then we have additional dependencies. + if (step.kind != .lib) { + // We always statically compile glad + step.addIncludePath(b.path("vendor/glad/include/")); + step.addCSourceFile(.{ + .file = b.path("vendor/glad/src/gl.c"), + .flags = &.{}, + }); + + // When we're targeting flatpak we ALWAYS link GTK so we + // get access to glib for dbus. + if (self.config.flatpak) step.linkSystemLibrary2("gtk4", dynamic_link_opts); + + switch (self.config.app_runtime) { + .none => {}, + .gtk => try self.addGtkNg(step), + } + } + + self.help_strings.addImport(step); + self.unicode_tables.addImport(step); + self.framedata.addImport(step); + + return static_libs; +} + +/// Setup the dependencies for the GTK apprt build. +fn addGtkNg( + self: *const SharedDeps, + step: *std.Build.Step.Compile, +) !void { + const b = step.step.owner; + const target = step.root_module.resolved_target.?; + const optimize = step.root_module.optimize.?; + + const gobject_ = b.lazyDependency("gobject", .{ + .target = target, + .optimize = optimize, + }); + if (gobject_) |gobject| { + const gobject_imports = .{ + .{ "adw", "adw1" }, + .{ "gdk", "gdk4" }, + .{ "gio", "gio2" }, + .{ "glib", "glib2" }, + .{ "gobject", "gobject2" }, + .{ "gtk", "gtk4" }, + .{ "xlib", "xlib2" }, + }; + inline for (gobject_imports) |import| { + const name, const module = import; + step.root_module.addImport(name, gobject.module(module)); + } + } + + step.linkSystemLibrary2("gtk4", dynamic_link_opts); + step.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + + if (self.config.x11) { + step.linkSystemLibrary2("X11", dynamic_link_opts); + if (gobject_) |gobject| { + step.root_module.addImport( + "gdk_x11", + gobject.module("gdkx114"), + ); + } + } + + if (self.config.wayland) wayland: { + // These need to be all be called to note that we need them. + const wayland_dep_ = b.lazyDependency("wayland", .{}); + const wayland_protocols_dep_ = b.lazyDependency( + "wayland_protocols", + .{}, + ); + const plasma_wayland_protocols_dep_ = b.lazyDependency( + "plasma_wayland_protocols", + .{}, + ); + const zig_wayland_import_ = b.lazyImport( + @import("../../build.zig"), + "zig_wayland", + ); + const zig_wayland_dep_ = b.lazyDependency("zig_wayland", .{}); + + // Unwrap or return, there are no more dependencies below. + const wayland_dep = wayland_dep_ orelse break :wayland; + const wayland_protocols_dep = wayland_protocols_dep_ orelse break :wayland; + const plasma_wayland_protocols_dep = plasma_wayland_protocols_dep_ orelse break :wayland; + const zig_wayland_import = zig_wayland_import_ orelse break :wayland; + const zig_wayland_dep = zig_wayland_dep_ orelse break :wayland; + + const Scanner = zig_wayland_import.Scanner; + const scanner = Scanner.create(zig_wayland_dep.builder, .{ + .wayland_xml = wayland_dep.path("protocol/wayland.xml"), + .wayland_protocols = wayland_protocols_dep.path(""), + }); + + // FIXME: replace with `zxdg_decoration_v1` once GTK merges https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6398 + scanner.addCustomProtocol( + plasma_wayland_protocols_dep.path("src/protocols/server-decoration.xml"), + ); + scanner.addCustomProtocol( + plasma_wayland_protocols_dep.path("src/protocols/slide.xml"), + ); + scanner.addCustomProtocol( + plasma_wayland_protocols_dep.path("src/protocols/kde-output-order-v1.xml"), + ); + scanner.addSystemProtocol("staging/xdg-activation/xdg-activation-v1.xml"); + scanner.addSystemProtocol("staging/ext-background-effect/ext-background-effect-v1.xml"); + + scanner.generate("wl_compositor", 1); + scanner.generate("org_kde_kwin_server_decoration_manager", 1); + scanner.generate("org_kde_kwin_slide_manager", 1); + scanner.generate("kde_output_order_v1", 1); + scanner.generate("xdg_activation_v1", 1); + scanner.generate("ext_background_effect_manager_v1", 1); + + step.root_module.addImport("wayland", b.createModule(.{ + .root_source_file = scanner.result, + })); + if (gobject_) |gobject| step.root_module.addImport( + "gdk_wayland", + gobject.module("gdkwayland4"), + ); + + if (b.lazyDependency("gtk4_layer_shell", .{ + .target = target, + .optimize = optimize, + })) |gtk4_layer_shell| { + const layer_shell_module = gtk4_layer_shell.module("gtk4-layer-shell"); + if (gobject_) |gobject| { + layer_shell_module.addImport("gtk", gobject.module("gtk4")); + layer_shell_module.addImport("gdk", gobject.module("gdk4")); + } + step.root_module.addImport( + "gtk4-layer-shell", + layer_shell_module, + ); + + // IMPORTANT: gtk4-layer-shell must be linked BEFORE + // wayland-client, as it relies on shimming libwayland's APIs. + if (b.systemIntegrationOption("gtk4-layer-shell", .{})) { + step.linkSystemLibrary2("gtk4-layer-shell-0", dynamic_link_opts); + } else { + // gtk4-layer-shell *must* be dynamically linked, + // so we don't add it as a static library + const shared_lib = gtk4_layer_shell.artifact("gtk4-layer-shell"); + b.installArtifact(shared_lib); + step.linkLibrary(shared_lib); + } + } + + step.linkSystemLibrary2("wayland-client", dynamic_link_opts); + } + + { + // Get our gresource c/h files and add them to our build. + const dist = gtkNgDistResources(b); + step.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} }); + step.addIncludePath(dist.resources_h.path(b).dirname()); + } +} + +/// Add only the dependencies required for `Config.simd` enabled. This also +/// adds all the simd source files for compilation. +pub fn addSimd( + b: *std.Build, + m: *std.Build.Module, + static_libs: ?*LazyPathList, +) !void { + const target = m.resolved_target.?; + const optimize = m.optimize.?; + + // Simdutf + if (b.systemIntegrationOption("simdutf", .{})) { + m.linkSystemLibrary("simdutf", dynamic_link_opts); + } else { + if (b.lazyDependency("simdutf", .{ + .target = target, + .optimize = optimize, + })) |simdutf_dep| { + m.linkLibrary(simdutf_dep.artifact("simdutf")); + if (static_libs) |v| try v.append( + b.allocator, + simdutf_dep.artifact("simdutf").getEmittedBin(), + ); + } + } + + // Highway + if (b.systemIntegrationOption("highway", .{ .default = false })) { + m.linkSystemLibrary("libhwy", dynamic_link_opts); + } else { + if (b.lazyDependency("highway", .{ + .target = target, + .optimize = optimize, + })) |highway_dep| { + m.linkLibrary(highway_dep.artifact("highway")); + if (static_libs) |v| try v.append( + b.allocator, + highway_dep.artifact("highway").getEmittedBin(), + ); + } + } + + // utfcpp - This is used as a dependency on our hand-written C++ code + if (b.lazyDependency("utfcpp", .{ + .target = target, + .optimize = optimize, + })) |utfcpp_dep| { + m.linkLibrary(utfcpp_dep.artifact("utfcpp")); + if (static_libs) |v| try v.append( + b.allocator, + utfcpp_dep.artifact("utfcpp").getEmittedBin(), + ); + } + + // SIMD C++ files + m.addIncludePath(b.path("src")); + { + // From hwy/detect_targets.h + const HWY_AVX10_2: c_int = 1 << 3; + const HWY_AVX3_SPR: c_int = 1 << 4; + const HWY_AVX3_ZEN4: c_int = 1 << 6; + const HWY_AVX3_DL: c_int = 1 << 7; + const HWY_AVX3: c_int = 1 << 8; + + var flags: std.ArrayListUnmanaged([]const u8) = .empty; + + // Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414 + // To workaround this we just disable AVX512 support completely. + // The performance difference between AVX2 and AVX512 is not + // significant for our use case and AVX512 is very rare on consumer + // hardware anyways. + const HWY_DISABLED_TARGETS: c_int = HWY_AVX10_2 | HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3; + if (target.result.cpu.arch == .x86_64) try flags.append( + b.allocator, + b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}), + ); + + // MSVC requires explicit std specification otherwise these + // are guarded, at least on Windows 2025. Doing it unconditionally + // doesn't cause any issues on other platforms and ensures we get + // C++17 support on MSVC. + try flags.append( + b.allocator, + "-std=c++17", + ); + + // Disable ubsan for MSVC to avoid undefined references to + // __ubsan_handle_* symbols that require a runtime we don't link + // and bundle. Hopefully we can fix this one day since ubsan is nice! + if (target.result.abi == .msvc) try flags.appendSlice(b.allocator, &.{ + "-fno-sanitize=undefined", + "-fno-sanitize-trap=undefined", + }); + + m.addCSourceFiles(.{ + .files = &.{ + "src/simd/base64.cpp", + "src/simd/codepoint_width.cpp", + "src/simd/index_of.cpp", + "src/simd/vt.cpp", + }, + .flags = flags.items, + }); + } +} + +/// Creates the resources that can be prebuilt for our dist build. +pub fn gtkNgDistResources( + b: *std.Build, +) struct { + resources_c: DistResource, + resources_h: DistResource, +} { + const gresource = @import("../apprt/gtk/build/gresource.zig"); + const gresource_xml = gresource_xml: { + const xml_exe = b.addExecutable(.{ + .name = "generate_gresource_xml", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/apprt/gtk/build/gresource.zig"), + .target = b.graph.host, + }), + }); + const xml_run = b.addRunArtifact(xml_exe); + + // Run our blueprint compiler across all of our blueprint files. + const blueprint_exe = b.addExecutable(.{ + .name = "gtk_blueprint_compiler", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/apprt/gtk/build/blueprint.zig"), + .target = b.graph.host, + }), + }); + blueprint_exe.linkLibC(); + blueprint_exe.linkSystemLibrary2("gtk4", dynamic_link_opts); + blueprint_exe.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + + for (gresource.blueprints) |bp| { + const blueprint_run = b.addRunArtifact(blueprint_exe); + blueprint_run.addArgs(&.{ + b.fmt("{d}", .{bp.major}), + b.fmt("{d}", .{bp.minor}), + }); + const ui_file = blueprint_run.addOutputFileArg(b.fmt( + "{d}.{d}/{s}.ui", + .{ + bp.major, + bp.minor, + bp.name, + }, + )); + blueprint_run.addFileArg(b.path(b.fmt( + "{s}/{d}.{d}/{s}.blp", + .{ + gresource.ui_path, + bp.major, + bp.minor, + bp.name, + }, + ))); + + xml_run.addFileArg(ui_file); + } + + break :gresource_xml xml_run.captureStdOut(); + }; + + const generate_c = b.addSystemCommand(&.{ + "glib-compile-resources", + "--c-name", + "ghostty", + "--generate-source", + "--target", + }); + const resources_c = generate_c.addOutputFileArg("ghostty_resources.c"); + generate_c.addFileArg(gresource_xml); + for (gresource.file_inputs) |path| { + generate_c.addFileInput(b.path(path)); + } + + const generate_h = b.addSystemCommand(&.{ + "glib-compile-resources", + "--c-name", + "ghostty", + "--generate-header", + "--target", + }); + const resources_h = generate_h.addOutputFileArg("ghostty_resources.h"); + generate_h.addFileArg(gresource_xml); + for (gresource.file_inputs) |path| { + generate_h.addFileInput(b.path(path)); + } + + return .{ + .resources_c = .{ + .dist = "src/apprt/gtk/ghostty_resources.c", + .generated = resources_c, + }, + .resources_h = .{ + .dist = "src/apprt/gtk/ghostty_resources.h", + .generated = resources_h, + }, + }; +} + +pub fn addUucode( + self: *const SharedDeps, + b: *std.Build, + module: *std.Build.Module, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, +) void { + if (b.lazyDependency("uucode", .{ + .target = target, + .optimize = optimize, + .tables_path = self.uucode_tables, + .build_config_path = b.path("src/build/uucode_config.zig"), + })) |dep| { + module.addImport("uucode", dep.module("uucode")); + } +} + +// For dynamic linking, we prefer dynamic linking and to search by +// mode first. Mode first will search all paths for a dynamic library +// before falling back to static. +const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{ + .preferred_link_mode = .dynamic, + .search_strategy = .mode_first, +}; diff --git a/vendor/libghostty-vt/src/build/UnicodeTables.zig b/vendor/libghostty-vt/src/build/UnicodeTables.zig new file mode 100644 index 00000000..17a839ea --- /dev/null +++ b/vendor/libghostty-vt/src/build/UnicodeTables.zig @@ -0,0 +1,92 @@ +const UnicodeTables = @This(); + +const std = @import("std"); + +/// The exe. +props_exe: *std.Build.Step.Compile, +symbols_exe: *std.Build.Step.Compile, + +/// The output path for the unicode tables +props_output: std.Build.LazyPath, +symbols_output: std.Build.LazyPath, + +pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables { + const props_exe = b.addExecutable(.{ + .name = "props-unigen", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/unicode/props_uucode.zig"), + .target = b.graph.host, + .strip = false, + .omit_frame_pointer = false, + .unwind_tables = .sync, + }), + + // TODO: x86_64 self-hosted crashes + .use_llvm = true, + }); + + const symbols_exe = b.addExecutable(.{ + .name = "symbols-unigen", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/unicode/symbols_uucode.zig"), + .target = b.graph.host, + .strip = false, + .omit_frame_pointer = false, + .unwind_tables = .sync, + }), + + // TODO: x86_64 self-hosted crashes + .use_llvm = true, + }); + + if (b.lazyDependency("uucode", .{ + .target = b.graph.host, + .tables_path = uucode_tables, + .build_config_path = b.path("src/build/uucode_config.zig"), + })) |dep| { + inline for (&.{ props_exe, symbols_exe }) |exe| { + exe.root_module.addImport("uucode", dep.module("uucode")); + } + } + + const props_run = b.addRunArtifact(props_exe); + const symbols_run = b.addRunArtifact(symbols_exe); + + // Generated Zig files have to end with .zig + const wf = b.addWriteFiles(); + const props_output = wf.addCopyFile(props_run.captureStdOut(), "props.zig"); + const symbols_output = wf.addCopyFile(symbols_run.captureStdOut(), "symbols.zig"); + + return .{ + .props_exe = props_exe, + .symbols_exe = symbols_exe, + .props_output = props_output, + .symbols_output = symbols_output, + }; +} + +/// Add the "unicode_tables" import. +pub fn addImport(self: *const UnicodeTables, step: *std.Build.Step.Compile) void { + self.props_output.addStepDependencies(&step.step); + self.symbols_output.addStepDependencies(&step.step); + self.addModuleImport(step.root_module); +} + +/// Add the "unicode_tables" import to a module. +pub fn addModuleImport( + self: *const UnicodeTables, + module: *std.Build.Module, +) void { + module.addAnonymousImport("unicode_tables", .{ + .root_source_file = self.props_output, + }); + module.addAnonymousImport("symbols_tables", .{ + .root_source_file = self.symbols_output, + }); +} + +/// Install the exe +pub fn install(self: *const UnicodeTables, b: *std.Build) void { + b.installArtifact(self.props_exe); + b.installArtifact(self.symbols_exe); +} diff --git a/vendor/libghostty-vt/src/build/XCFrameworkStep.zig b/vendor/libghostty-vt/src/build/XCFrameworkStep.zig new file mode 100644 index 00000000..39f0f9ba --- /dev/null +++ b/vendor/libghostty-vt/src/build/XCFrameworkStep.zig @@ -0,0 +1,77 @@ +//! A zig builder step that runs "swift build" in the context of +//! a Swift project managed with SwiftPM. This is primarily meant to build +//! executables currently since that is what we build. +const XCFrameworkStep = @This(); + +const std = @import("std"); +const Step = std.Build.Step; +const RunStep = std.Build.Step.Run; +const LazyPath = std.Build.LazyPath; + +pub const Options = struct { + /// The name of the xcframework to create. + name: []const u8, + + /// The path to write the framework + out_path: []const u8, + + /// The libraries to bundle + libraries: []const Library, +}; + +/// A single library to bundle into the xcframework. +pub const Library = struct { + /// Library file (dylib, a) to package. + library: LazyPath, + + /// Path to a directory with the headers. + headers: LazyPath, + + /// Path to a debug symbols file (.dSYM) if available. + dsym: ?LazyPath, +}; + +step: *Step, + +pub fn create(b: *std.Build, opts: Options) *XCFrameworkStep { + const self = b.allocator.create(XCFrameworkStep) catch @panic("OOM"); + + // We have to delete the old xcframework first since we're writing + // to a static path. + const run_delete = run: { + const run = RunStep.create(b, b.fmt("xcframework delete {s}", .{opts.name})); + run.has_side_effects = true; + run.addArgs(&.{ "rm", "-rf", opts.out_path }); + break :run run; + }; + + // Then we run xcodebuild to create the framework. + const run_create = run: { + const run = RunStep.create(b, b.fmt("xcframework {s}", .{opts.name})); + run.has_side_effects = true; + run.addArgs(&.{ "xcodebuild", "-create-xcframework" }); + for (opts.libraries) |lib| { + run.addArg("-library"); + run.addFileArg(lib.library); + run.addArg("-headers"); + run.addFileArg(lib.headers); + if (lib.dsym) |dsym| { + run.addArg("-debug-symbols"); + run.addFileArg(dsym); + } + } + run.addArg("-output"); + run.addArg(opts.out_path); + run.expectExitCode(0); + _ = run.captureStdOut(); + _ = run.captureStdErr(); + break :run run; + }; + run_create.step.dependOn(&run_delete.step); + + self.* = .{ + .step = &run_create.step, + }; + + return self; +} diff --git a/vendor/libghostty-vt/src/build/docker/debian/Dockerfile b/vendor/libghostty-vt/src/build/docker/debian/Dockerfile new file mode 100644 index 00000000..ffeef3d6 --- /dev/null +++ b/vendor/libghostty-vt/src/build/docker/debian/Dockerfile @@ -0,0 +1,43 @@ +ARG DISTRO_VERSION="13" +FROM docker.io/library/debian:${DISTRO_VERSION} + +# Install Dependencies +RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \ + apt-get -qq -y --no-install-recommends install \ + # Build Tools + blueprint-compiler \ + build-essential \ + curl \ + libbz2-dev \ + libonig-dev \ + libxml2-utils \ + lintian \ + lsb-release \ + libxml2-utils \ + pandoc \ + # Ghostty Dependencies + libadwaita-1-dev \ + libgtk-4-dev \ + libgtk4-layer-shell-dev && \ + # Clean up for better caching + rm -rf /var/lib/apt/lists/* + +WORKDIR /src + +COPY ./build.zig ./build.zig.zon /src/ + +# Install zig +# https://ziglang.org/download/ + +RUN export ZIG_VERSION=$(sed -n -E 's/^\s*\.?minimum_zig_version\s*=\s*"([^"]+)".*/\1/p' build.zig.zon) && curl -L -o /tmp/zig.tar.xz "https://ziglang.org/download/$ZIG_VERSION/zig-$(uname -m)-linux-$ZIG_VERSION.tar.xz" && \ + tar -xf /tmp/zig.tar.xz -C /opt && \ + rm /tmp/zig.tar.xz && \ + ln -s "/opt/zig-$(uname -m)-linux-$ZIG_VERSION/zig" /usr/local/bin/zig + +COPY . /src + +RUN zig build \ + -Doptimize=Debug \ + -Dcpu=baseline + +RUN ./zig-out/bin/ghostty +version diff --git a/vendor/libghostty-vt/src/build/docker/lib-c-docs/Dockerfile b/vendor/libghostty-vt/src/build/docker/lib-c-docs/Dockerfile new file mode 100644 index 00000000..a3cfdcc9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/docker/lib-c-docs/Dockerfile @@ -0,0 +1,38 @@ +#-------------------------------------------------------------------- +# Generate documentation with Doxygen +#-------------------------------------------------------------------- +FROM --platform=linux/amd64 archlinux:latest AS builder + +# Build argument for noindex header +ARG ADD_NOINDEX_HEADER=false +RUN pacman -Syu --noconfirm && \ + pacman -S --noconfirm \ + doxygen \ + graphviz && \ + pacman -Scc --noconfirm +WORKDIR /ghostty +COPY include/ ./include/ +COPY images/ ./images/ +COPY dist/doxygen/ ./dist/doxygen/ +COPY example/ ./example/ +COPY Doxyfile ./ +COPY DoxygenLayout.xml ./ +RUN mkdir -p zig-out/share/ghostty/doc/libghostty +RUN doxygen + +#-------------------------------------------------------------------- +# Host the static HTML +#-------------------------------------------------------------------- +FROM nginx:alpine AS runtime + +# Pass build arg to runtime stage +ARG ADD_NOINDEX_HEADER=false +ENV ADD_NOINDEX_HEADER=$ADD_NOINDEX_HEADER + +# Copy documentation and entrypoint script +COPY --from=builder /ghostty/zig-out/share/ghostty/doc/libghostty /usr/share/nginx/html +COPY src/build/docker/lib-c-docs/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +EXPOSE 80 +CMD ["/entrypoint.sh"] diff --git a/vendor/libghostty-vt/src/build/docker/lib-c-docs/entrypoint.sh b/vendor/libghostty-vt/src/build/docker/lib-c-docs/entrypoint.sh new file mode 100755 index 00000000..ac9ca1c0 --- /dev/null +++ b/vendor/libghostty-vt/src/build/docker/lib-c-docs/entrypoint.sh @@ -0,0 +1,32 @@ +#!/bin/sh +if [ "$ADD_NOINDEX_HEADER" = "true" ]; then + cat > /etc/nginx/conf.d/noindex.conf << 'EOF' +server { + listen 80; + location / { + root /usr/share/nginx/html; + index index.html; + etag on; + add_header Cache-Control "no-cache" always; + add_header X-Robots-Tag "noindex, nofollow" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always; + } +} +EOF + # Remove default server config + rm -f /etc/nginx/conf.d/default.conf +else + cat > /etc/nginx/conf.d/default.conf << 'EOF' +server { + listen 80; + location / { + root /usr/share/nginx/html; + index index.html; + etag on; + add_header Cache-Control "no-cache" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always; + } +} +EOF +fi +exec nginx -g "daemon off;" diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_001.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_001.txt new file mode 100644 index 00000000..416e8793 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_001.txt @@ -0,0 +1,41 @@ + + + +++==*%%%%%%%%%%%%*==+++ + ++****++ ++****++ + ++**++ ++**++ + xx**+= o+*%$@@@@@@$%*+o =+**xx + xx**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**xx + xx** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **xx + ox** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + x+++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +++x + == ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท == + ox++ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xo + +++~ @$$$$$@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$@@%%%%%%$$$$$$$@@@@@$$$@@@@@@@@@@@@@@@@@@@@$$$$$$ == + == @$$$$* $$$$% =$$$$$@ == + == ยท$$$$@ x@$@ @$$$$$ยท == + == ยท@$$$$% ยท$$$$% *$$$$$@ยท == + == ยท@$$$$@@$%%$$$$$$@@@@@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยท+== + ++++ =@@@@@@@@@* x$@@@@@@@@$x *@@@@@@@@@= ++++ + xx==++ ++==oo + ++===+ ++%%+o o+%%++ +===++ + ++=====%+=++++*=*========***++++***========*=*++++=+%=====++ + xx++==******====++ ++==********==++ ++====******==++xx + ++++ ++++ ++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_002.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_002.txt new file mode 100644 index 00000000..e74134e3 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_002.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++==*%%%**=++++++=**%%%*==++ + ++**=* *=**++ + x+**+= =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= +=++ + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ o+++ + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ %@$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + ==+ยท x@$$$@@%=*%$$@@@@@@@@@@@$$$$$@@@@@@@@@@@@@@@@@@$$$$$@x ยท+== + == @$$$$% ~$@$$$@= x@$$$$$@ == + == $$$$@* %$$@ยท @$$$$$ == + == ยท@$$$@* $$$$+ $$$$$@ยท == + == ยท@$$$$$~ ~ox+=%@@$$$@@*==============*$@$$$$$@ยท == + == ยท@$$$$$@@@@@@@@@@@@@@@@@$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$$@@@$***%@@@@$$$$$$@@@@%***$@@@$$$$$$$@@@+ o+== + ++== ยท=$@@@@@$*~ +%@@@@@@%+ ~*$@@@@@$=ยท ==++ + ===+ +=== + ++====x+ *=**== ==**=* +x====++ + ++=====**%******=========**%****%**=========******%**=====++ + ++++========++xx ++++========++++ xx++========++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_003.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_003.txt new file mode 100644 index 00000000..6da88ffb --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_003.txt @@ -0,0 +1,41 @@ + + ++====****====++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ยทoxx++xxoยท +x**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@+ ยท+*$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ *@$$$@ o=%@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$@* +== + == @$$$$$o %$$$$$ *$$$$$@ == + == $$$$$$@@=ยท @$$@ @$$$$$ == + == ยท@$$$$$x %$$$$% =$$$$$@ยท == + == ยท@$$$$@ ยทx*$@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$@x o=%@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@% == + ==+x ~$@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+*@@@@$$$$@@@@$~ x+== + ++== x*$@@@$*x ยท=%$@@$%=ยท x*$@@@$*x ==++ + ==== =+== + x+====+= x+*===+= ======+x =+====xx + ++====*%%%%***====++====*%*%%*%*====++=====**%%%%*====++ + xx++++====++++ ++++====++++ ++++====++++xx + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_004.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_004.txt new file mode 100644 index 00000000..5ad3a811 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_004.txt @@ -0,0 +1,41 @@ + + ++==************==++ + ++***%=*x~ ~x*=%***++ + ++**+= ==**++ + ==== ~x=*%%%%%%*=x~ ==== + xx**++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++**xo + oo**o~ *@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@* ~o**oo + **oยท =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ยทo** + ==+x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% x+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==xยท @@$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + xx== @$$$@% x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$@* ~=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$@ o+++ + == %$$$$$% +$@@$$$$@@%**************%@@$$$$$% == + == @$$$$$@@%+ $$$$$+ ~@$$$$@ == + == $$$$$$$@@@@= *@$$@ยท @$$$$$ == + == ~@$$$$$@= %@$$$$@+ o$$$$$$@~ == + == ยท@$$$$@= ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@@$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@%x ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++== + x+==xยท o=***=x ~+****+~ x=***=o ยทx==xx + ====xo xx x+ ox==== + ======+x =+=*====++ ++====*=+= x+====== + ++==***%********++++==************==++++********%***==++ + ++++++++++ +x++++++++xx ++++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_005.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_005.txt new file mode 100644 index 00000000..e93b295d --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_005.txt @@ -0,0 +1,41 @@ + + +==*****%%%%*****==+ + ++***%=+ +=%***++ + ++**+= =+**++ + **== ยทx=*%$$$$$$%*=xยท ==** + xx**xx x%@@@@@@@@@$$$$@@@@@@@@@%x xx**xx + xx** ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ **xx + **oยท %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ยทo** + ==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + == @@$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + xx== @$$$$o o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + == $$$$$$$o o*@$$$$$@@=+++++++++++++++$@$$$$$$ == + == @$$$$$$@@$=ยท $$$$@x @$$$$@ == + == $$$$$$$@@@$= $$$$@~ @$$$$$ == + == ยท@$$$$$@* x@@$$$$@*~ ~=@$$$$$@ยท == + == ยท@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ +@@@@@@@@@@@*~ x$@@@@@@@@@@$x ยท*@@@@@@@@@@@+ ++++ + xx==+~ x+=+xยท o+==+o ยทx+=+x ~+==xo + ++==+x == == x+==++ + ++====++ ox=+==**==+= =+=***==+*+x ++====++ + x+==**********==++++++************++++++==**********==++ + ++++++xx xx++++xx ++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_006.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_006.txt new file mode 100644 index 00000000..f9ad6052 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_006.txt @@ -0,0 +1,41 @@ + + +++==*%%*%%%%%%*%%*===++ + ++**=*++ ++*=**++ + ++**=+ +=**++ + xx**+= ~+*%$$@@@@$$%*+~ =+**+x + xx**ox +$@@@@@@@@$$$$$$@@@@@@@@$+ xo**xx + x+** x$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$x **+x + oo** ยท$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$ยท **oo + ==+~ ยท@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@ยท ~+== + ++++ %@$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$@$%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ox++ ~@$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xo + +++~ @$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ ~+++ + == $$$$$$@% =@$$$$$@%xooooooooooooooo*@$$$$$$ == + == @$$$$$$@@@$+ $$$$@o @$$$$@ == + == ยท$$$$$$$@@@%+ $$$$@o @$$$$$ยท == + == ยท@$$$$$$* =@@$$$$@$xoooooooooooooox*@$$$$$@ยท == + == ยท@$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + ++++ ~%@@@@@@@@@$x ยท*@@@@@@@@@@*ยท x$@@@@@@@@@%~ ++++ + xx==+x oooยท ~oo~ ยทoo~ x+==xo + x+==++ +%% %%+o ++==+x + ++=*==+*xx ++*=**==**+*++ ++*+**==**=*++o~ xx*+==**++ + ++==********=+xx ++==********==++ xx++********==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_007.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_007.txt new file mode 100644 index 00000000..78256008 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_007.txt @@ -0,0 +1,41 @@ + + ++++=**%**%%%%**%**=++++ + ==****++ ++*=**== + ++**=+ +=**++ + x+**+= ~+*%$$$@@$$$%*+~ =+**+x + ++**ox +$@@@@@@@@$$$$$$@@@@@@@@$+ xo**++ + x+** x$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$x **+x + xx** $@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$ **xx + ==+~ ยท@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@ยท ~+== + x+++ %@$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + == @@$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + xx++ ~@$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ +++x + +++~ @$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ ~+++ + == $$$$$$@%ยท =@$$$$$@%xooooooooooooooo*@$$$$$$ == + == @$$$$$$@@@$=ยท $$$$@o @$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@o @$$$$$ยท == + == ยท@$$$$$$% =@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@ยท == + == ยท@$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + ++++ ยท%@@@@@@@@@%o =$@@@@@@@@$= ~%@@@@@@@@@%ยท ++++ + ==+x ยทยทยท ยทยท ยทยท x+== + ++=*++ ++%% %*++ ++*=++ + ++==**=*++ooxx+=*=**==**=*=+xxxx+=%=**==**=*=+xxxx++*=**==+x + +++=******==++xx ++++********++++ xx++==******++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_008.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_008.txt new file mode 100644 index 00000000..3e4706fd --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_008.txt @@ -0,0 +1,41 @@ + + ++++==**%%%%%%%%**==++++ + +=***%== ==%***=+ + ++**+= =+**++ + x+**=+ ยทo+*%$$$$$$%*+oยท +=**+x + x+**xx x%@@@@@@@@@$$$$@@@@@@@@@%x xx**+x + x+**~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~**+x + ox**oยท %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ยทo**xo + ==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + x+++ %@$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + ==x @@$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x== + x++= @$$$$ x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ =+xx + +++o @$$$@% +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + == $$$$$$@% ยท*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$% == + == @$$$$$$@@@%+ $$$$@o @$$$$@ == + == $$$$$$$@@@$*ยท $$$$@~ @$$$$$ == + == ยท@$$$$$@%ยท +@$$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยท+== + +++= =@@@@@@@@@*ยท x%@@@@@@@@%x *@@@@@@@@@* =+++ + ==++ +=== + xx=*=+ ++%%+x x+%%++ +=**xx + ++**=*=+++++==*=**++**=%==++++==%=**++**=***+++++=*=**++ + xx++==**==++++ ++========++ ++++======++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_009.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_009.txt new file mode 100644 index 00000000..00e5a617 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_009.txt @@ -0,0 +1,41 @@ + + ++++==***%%%%%%***==++++ + +=****=*o~ ~o*=%***=+ + ++**=* *=**++ + x+**== ~x=*%%%%%%*=x~ ==**+x + ++**++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++**++ + x+**o~ *@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@* ~o**+x + xx**oยท =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ยทo**xx + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + x+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++x + ==xยท @@$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + x+== @$$$$~ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==+x + +++o @$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$@ o+++ + == %$$$$$$+ o%@$$$$$@@=++++++++++++++=$@$$$$$% == + == @$$$$$$@@$*~ $$$$@x @$$$$@ == + == $$$$$$$$@@@%~ $$$$@~ @$$$$$ == + == ~@$$$$$@$~ x@$$$$$@*~ ยท+@$$$$$@~ == + == ยท@$$$$$$ o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@$**@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ยท+== + x+== =$@@@@@@@@* o%@@@@@@@@%o *$@@@@@@@$= ==+x + ==++ ++== + ===+ +=%%++ ++%%== +=== + ++**=%==++++****==++**=***++++***=**++==****++++==%***++ + ++======++++ +++======++x ++++=====+++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_010.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_010.txt new file mode 100644 index 00000000..bdfaa69d --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_010.txt @@ -0,0 +1,41 @@ + + xx++==************==++xx + ++***%*%++ ++%*%***++ + ++**=*+x x+*=**++ + xx====x ยทox+====+xoยท x====xx + x+**++ x%@@@@@@@@@@@@@@@@@@@@%x ++**++ + xx**+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+**xx + oo**+~ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x ~+**oo + ==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==+ยท $@$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$@* ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + +++x @$$$$$ ยท=@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$@ x+++ + == *$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$* == + == @$$$$$$@@*ยท $$$$$= o$$$$$@ == + == $$$$$$$$@@@@x $$$$@ @$$$$$ == + == ~@$$$$$@@+ %$$$$$@x ~$$$$$$@~ == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%== ==== + ++**=***++==%***==++***%**++++**%***++==***%==++**%=**++ + ++++==++++ x+++====+++x ++++==+++x + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_011.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_011.txt new file mode 100644 index 00000000..e3170d53 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_011.txt @@ -0,0 +1,41 @@ + + ++++==********==++++ + ++*****%**+x x+**%*****++ + ++**=*++ ++*=**++ + ====++ ~ooxxoo~ ++==== + xx**++ ~+%@@@@@@@@@@@@@@@@@@%+~ ++**xx + ++**++ ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ ++**+x + ==+x ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท x+== + ==++ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++== + xx== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xx + ==+~ %@$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + xx== @@$$@$o +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + +++x $$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+ =@$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$@= +== + == @$$$$$$@%o x$$$$$% =$$$$$@ == + == $$$$$$$$@@@@= $@$$@ @$$$$$ == + == ~@$$$$$@@*~ +$$$$$% *$$$$$@~ == + == ยท@$$$$$$ ยท*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@xยท~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~*@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====o ==%%++ ++%%=+ ==== + ++**=***=+==%***++++==***%====%***==++++***%====***=**++ + xx++++++++ ++====++ ++++++++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_012.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_012.txt new file mode 100644 index 00000000..90a65ed5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_012.txt @@ -0,0 +1,41 @@ + + ++++++========++++++ + ++==***%*%**==++++==**%*%***==++ + ++==**=*xo ox*=**==++ + ++**+= =+**++ + xx==*= ยท+*$@@@@@@@@@@@@@@$*+ยท =*==xx + xx===+ o%@@@@@$$$$$$$$$$$$$$$$@@@@@%o +===xx + ==++ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + xx== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xx + ==+x x@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$@%**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xx + ++++ %@$$$$ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+ยท o@$$$$$ ยท=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@o ยท+== + == @$$$$$@$~ ~$@$$$$@+ x@$$$$$@ == + == $$$$$$$$@@@%o $$$$@ยท @$$$$$ == + == ยท@$$$$$$@@%x $$$$$+ ~$$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@%***************@@$$$$$@ยท == + == ยท@$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$= x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$@@@% ~+== + x+== +%@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@%+ ==+x + ++=+ +=++ + ++==o ==%%++ ++%%== o==++ + ++***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ x+++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_013.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_013.txt new file mode 100644 index 00000000..b2b6a980 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_013.txt @@ -0,0 +1,41 @@ + + ++++++++====++++++++ + ++++*******%********%*******++++ + +x==**=*=+ +=*=**==xx + ++**== ==**++ + ====++ ยทx*%@@@@@@@@@@@@%*xยท ++==== + ===+ +$@@@@@@$$$$$$$$$$$$@@@@@@$+ +=== + ===+ ยท%@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@%ยท +=== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx==oยท =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ยทo==xo + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ =@$$$$~ ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+ยท ยท@$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ยท ยท+== + == @$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ == + == @$$$$$$@@@*o $$$$@x @$$$$@ == + == ยท@$$$$$$@@@$= $$$$@~ @$$$$@ยท == + == ยท@$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~o*@$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ++=+ +=++ + ++==o ==%%++ ++%%== o==++ + ++***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_014.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_014.txt new file mode 100644 index 00000000..96194979 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_014.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==*****%*%%%%%%*%*****==++ + ++*****%=+ +=%*****++ + ++====+* *+====++ + ++==+= ยทx+*%%$$$$%%*+xยท =+==++ + ====xx x%@@@@@@@@@$$$$@@@@@@@@@%x xx==== + ++==o~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~==+= + ++==oยท *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ยทo==++ + xx==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+==xx + ++++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + ox==x @@$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x==xo + ++++ @$$$@$x ยท+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == $$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$ == + == @$$$$$$@%o x$$$$$% *$$$$$@ == + == $$$$$$$$@@@@+ $$$$@ @$$$$$ == + == ยท@$$$$$$@%o x$$$$$% *$$$$$@ยท == + == ยท@$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$x ยท+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ยท+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$+ ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+=*=***++==%***++xx==*%**====**%*==xx++***%==++***=*=+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_015.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_015.txt new file mode 100644 index 00000000..33cf5ec6 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_015.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + xx++==****************==++xx + ++==**=***++o o++***=**==++ + x+=====*+x x+*=====+x + ++====ox ยทox++==++xoยท xo====++ + ++==++ x*$@@@@@@@@@@@@@@@@@@$*x ++==++ + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@x o+==++ + ===+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==+ยท $@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ++== @@$$$@$**@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + +++x @$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@$ ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$* == + == @$$$$$@$~ o$$$$$$@= x@$$$$$@ == + == $$$$$$$$@@@%~ $$$$@ยท @$$$$$ == + == ~@$$$$$$@@$x $$$$$+ ยท@$$$$@~ == + == ยท@$$$$$$o x$@$$$$$@@*==============*$@$$$$$@ยท == + == ยท@$$$$@% x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$+ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ยท+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==xo + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%==++++%***=+x+==*%**++++**%*==+++=***%++++=*%=**+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_016.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_016.txt new file mode 100644 index 00000000..50eea783 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_016.txt @@ -0,0 +1,41 @@ + + xxxx + ++++==************==++++ + xx++==****%%=*++++++++*=%*****==++xx + ++=====*+~ ~+*=====++ + ++====+= =+====++ + ++===+ ยท+*$@@@@@@@@@@@@@@$*+ยท +===++ + ++===+ o%@@@@@$$$$$$$$$$$$$$$$@@@@@%o +===++ + xx===+ x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@x +===xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$$* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==xยท o@$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$@o ยทx== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@= $$$$$* x$$$$$$ == + == ยท@$$$$$$$@@@@x $$$$@ @$$$$@ยท == + == ยท@$$$$$@@+ *$$$$$$o ยท$$$$$$@ยท == + == ยท@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ===+ +=== + ===+ ++%% %%++ +=== + ++***%++++++**%*=+++===*==++x+==*===++++*%**++x+++%***++ + x+++====++ ++==++++ ++====+++x + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_017.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_017.txt new file mode 100644 index 00000000..e05674a7 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_017.txt @@ -0,0 +1,41 @@ + + + ++++++============++++++ + ++==**==*%*%********%*%*==**==++ + ++=====*== ==*=====++ + ++======+x x+======+x + ++====++ o=%$@@@@@@@@@@$%=o ++====++ + ++==== x%@@@@@@$$$$$$$$$$$$@@@@@@%x ====+x + xx===+ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +===xx + ==== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==== + x+==xยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx==++ + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== x@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ==xo + ++++ +@$$$@@=ox%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$ == + == @$$$$$@@+ *$$$$$$~ %$$$$$@ == + == ยท@$$$$$$$@@@@+ $@$$@ @$$$$@ยท == + == ยท@$$$$$$@$= ~$$$$$* x$$$$$@ยท == + == ยท@$$$$$$ o%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ยท == + == ยท@$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + ++++ ~%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++xx + ==+x ~~~ ยท~~ยท ~~ยท x+== + ==++ x+%% %%++ +=== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ ++++====++++ ++++==++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_018.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_018.txt new file mode 100644 index 00000000..5e92ec0e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_018.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++++++ + xx++==****=**%%%%%%**=****==+++x + x+++====*%==o~ ~o==%*====+++x + ++====+= =+====++ + ====++ o+=*%%%%%%*=+o ++==== + ====xx o*$@@@@@@@@@@@@@@@@@@@@$*o xx==== + ====oยท ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*ยท ยทo==== + ++==oยท *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ยทo==++ + xx==+o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+==xx + ==++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ++== + xx==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx==xo + ++++ @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$~ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == %$$$$@% ยท*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$= ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@ == + == $$$$$$$@@@*o $$$$@o @$$$$$ == + == ยท@$$$$$$@@@$+ $$$$@~ @$$$$@ยท == + == ยท@$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~o*@$$$$$@ยท == + == ยท@$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ยท@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@ยท x== + ++++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x ++++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**+=o~ ++*+**==**+*xo ox*+**++**+*++ ~o=+**++ + ++==****++++ ++==****==++ ++++=***==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_019.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_019.txt new file mode 100644 index 00000000..f528dc87 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_019.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%****===++ + ++=====*+x x+*=====++ + ++====xx ยทox+====+xoยท xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+o x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x o+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + ox==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+==xo + ++== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+== + == *$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ยท =@@$$$$$@@%**************%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~@$$$$$ == + == ~@$$$$$$$@@@@o $$$$@ @$$$$@~ == + == ยท@$$$$$@$o ยท%$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@o == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@= ++++ + **x~ ~+===+o x====x o+===+~ ~+** + **xx ++ ++ xx** + ==**++ ~o*+**==**+= =+**==**+*o~ ++==== + +==*****=+++ ++=******=++ +++==****==+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_020.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_020.txt new file mode 100644 index 00000000..02720e63 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_020.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=***=+xo~~~~ox+=%**=****++++ + xx++===*+= =+*===++xx + ++====++ ยท~~~~ยท ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++==+x + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$@$o x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==xยท +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยทx== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@%x x$$$$$% =$$$$$$ == + == ~@$$$$$$$@@@@= $@$$@ @$$$$@ยท == + == ยท@$$$$$@@*~ +$$$$$% *$$$$$@ยท == + == ยท@$$$$$$ ยท=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@xยท~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@$$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$@@@@@$ x+++ + **o ~=%$$$%=o +*$$$$*+ o=%$$$%=~ o** + xx**o~ ~~**xx + xx**+* ++=***=*+x x+*=****++ *+**xx + ++==*%%%%%**== x+++*%%%%%%*+++x ++**%%%%%*==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_021.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_021.txt new file mode 100644 index 00000000..e7305fbc --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_021.txt @@ -0,0 +1,41 @@ + + + + ++++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* ~ *=*===++ + ++====+= =+====+x + ++===+ ~+%$@@@@@@@@@@@@@@$%+ยท +===+x + ++==++ x$@@@@@$$$$$$$$$$$$$$$$@@@@@%x +===+x + xx==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +===xo + ==++ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ==+o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + ++++ %@$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==xยท +@$$$$% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ยท+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*ยท =$$$$$$ %$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@ == + == ~@$$$$$@@%x o$$$$$* =$$$$$@ == + == ~@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == ~@$$$$@% x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$@$~ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@@*=+=%@@@@$$$$$@@@@o o+++ + o ** +%@@@@@%= o*$@@@@$*o +%@@@@@%+ ** + ++** **++ + x+**+= x****== ==**=*xo =+**++ + ++**%%%%%%%*==++ ++==%%%%%%%*==++ x+==*%%%%%%%**=+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_022.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_022.txt new file mode 100644 index 00000000..943c4432 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_022.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%*%********%%%*****+++x + ++==***%++ ++*=**==+x + ==**+= *+**++ + ox====oo o=%$@@@@@@@@@@@@$%+~ xx==== + xx===+ ~*@@@@@@$$$$$$$$$$$$$@@@@@@$+ ==== + ===+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx==xx + ==+o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + xx== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xo + +++x $@$$$@@*+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ==x =@$$$$% +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == @$$$$@* +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$% == + == o$$$$$$@$o ยท$$$$$$$o o$$$$$$@ == + == +@$$$$$$$@@@@o @$$$@ @$$$$$ == + == =@$$$$$$@@=ยท ~@$$$@+ +@$$$$@ == + == +@$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == +@$$$$@* ~*@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ == + == +@$$$$$@= ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ~+== + xx== +$@@@@@@@$= ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==%%=+ ++%%== oo=*++ + x+==*%=*===**=**++++++*%**====**%*++++++******==*=%*==+x + ++++++ x+++++++ ++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_023.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_023.txt new file mode 100644 index 00000000..673dd948 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_023.txt @@ -0,0 +1,41 @@ + + + + x+++++++==++++++++ + ++++*******%%%**%%%*%*%****=++ + xx==**=*++ ==****++ + ++**+= ++*=**++ + xx====oo ~+*$$@@@@@@@@@@$%=o +x*=++ + x+===+ ยท=@@@@@@@$$$$$$$$$$$$@@@@@@%o ~==== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ==++ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ==+o +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + xx== $@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + +++x @@$$$@@%=*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + == %@$$$$* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == @$$$$@= o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$+ ยท+== + ox== =$$$$$$@%ยท o@$$$$$@o +@$$$$$$ == + ox++ %@$$$$$$$@@@%ยท @$$$@ @$$$$@ == + ox++ %@$$$$$@@@*~ ~@$$$@~ +@$$$$@ == + ox++ %@$$$$$$ ยท=@@$$$$$@@***************%@@$$$$$@ == + ox++ %@$$$$@+ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ == + ox++ %@$$$$$$x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@$ ยท+== + ++++ ยท*@@@@@@@@@%o +@@@@@@@@@@= ~%@@@@@@@@@* ==+x + ===x ยท~ยท ~~ ยท~ยท ++== + ==++ x+%% %%+x ++== + ++**=*++xoox==*===++===%==xoox==%=**+++=*%=*+xox++*=**++ + +++++==+++ +++==+++ ++====++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_024.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_024.txt new file mode 100644 index 00000000..2339b781 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_024.txt @@ -0,0 +1,41 @@ + + + + x+++++++++++++ + ++==***%%%%%%%%%%*%*****+++x + ++***%== o+*=****++ + ++**=* ++====+x + ====xx x=%$@@@@@@@@@$%=x ++**++ + ===+ =$@@@@@@@$$$$$$$$$@@@@@@@$+ xx*=++ + ==++ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ ~o==++ + ===+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ~x==xo + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ==+~ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==+x + x+++ @@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% o+== + ==+~ @$$$$@@%%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == @$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ox== x@$$$$@~ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ o+++ + xx++ $$$$$$$@= =@$$$$$@x ~*@$$$$$= ยท+== + ++++ @$$$$$$$@@@@+ @$$$@ x@$$$@% x== + ++++ @$$$$$$@@@*x o@$$$$ =@$$$@% == + ++++ @$$$$$$% =@@$$$$$@$===============*@@$$$$@% == + xx++ @$$$$$@~ +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@$ == + xx++ @$$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@ ยท+== + +++x *@@@@@@@@@@@%o +$@@@@@@@@@@@=ยท ~*@@@@@@@@@@@= +++x + **xยท ~+=**+o x=**=xยท ~+=*=+~ ~+== + oo**xx ++ ++ o+** + ==**++ =+**==**+= ++**==**==xยท ++==== + +==*%%**===+ ++=***%%*=++ ++===*%%*==+ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_025.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_025.txt new file mode 100644 index 00000000..9667d9db --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_025.txt @@ -0,0 +1,41 @@ + + + + xx++++++++++ + ++==*****%%$%%%%%%%*****++xx + ++*****= x+%***==++ + ++**=* ==**== + ====xx o=%$$@@@@@@@$%*+o ==**xx + ===+ +$@@@@@@@$$$$$$$$$@@@@@@@%x x+**+x + ==++ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* o+**xx + ===+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ o+== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=++ + ==xยท %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ==xx + ++++ @@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= x+++ + ==+ยท @$$$$@@$%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ** + == @$$$$$= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x =+xx + xx++ *$$$$$@ยท =@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ x+++ + ++++ @$$$$$$@x ยท*@$$$$$@+ยท o%@$$$$$~ ~+++ + xx+x $$$$$$$$@@@$x ยท@$$$$ +@$$$@= ยท+++ + xx+x @$$$$$$@@@%x o@$$$$ *@$$$@= ยท+== + xxxx @$$$$$$$ +$@$$$$$@%+++++++++++++++=@@$$$$@= ยท+== + +++x @$$$$$@ยท +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@= ยท+== + +++x @$$$$$$% x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + ++++ @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@x x== + +++o ~$@@@@$$$@@@@@*x~o+*@@@@@$$$@@@@%+o~o=$@@@@$$$@@@@% ++++ + ** ~+%$$$$*o x*$$$$%+ยท ~=%$$$%+~ ยทo** + x+**~~ ~o**xx + x+**==o~ ++*=**=*++ oo==****++ =+**xx + ++==*%%%%%%*==xx xx==*%%%%%%*==++ +=*%%%%%%*==++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_026.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_026.txt new file mode 100644 index 00000000..55581148 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_026.txt @@ -0,0 +1,41 @@ + + + + ++++++++ + ++++****%%%%%%%%%%%***==++ + ++**=*=* ++*=**++ + ++**=*x *=**++ + ++**xx o+*%$$@@@@@$$%=xยท +=== + ===+ +$@@@@@@@$$$$$$$$@@@@@@@@*o ++**xx + ===+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= o+** + ===+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o x=== + ++== ยท@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ++++ + =*oยท %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + ++++ @@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++++ + ==xยท ~@$$$$@@$%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + ox== @$$$$$* ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + xx++ $$$$$$@ยท ยท=@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$ ++++ + +++x @$$$$$$$x ~*@$$$$$@+~~~~~~~~~~~~~~~o$@$$$$@ o+++ + +++o $$$$$$$$@@@%o ยท@$$$$ +@$$$@~ ~+++ + +++o @$$$$$$$@@$+ ~@$$$$ =@$$$@o ~+++ + +++o @$$$$$$$ยท +$@$$$$$@*+xxxxxxxxxxxxxx=@@$$$$@o ~+++ + +++o @$$$$$@~ x%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@o ~+++ + +++o @$$$$$$% o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++x @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$@* == + ==xยท *@@@$$$$$$$@@@@%**%@@@@$$$$$$$@@@$%**$@@@$$$$$$$@@@x x+++ + xx== =$@@@@@@$+ o%@@@@@@@*ยท =$@@@@@$=ยท *= + ++== ==++ + ++**++ ==%%== ++%%** +x**++ + ++***%%%%%%***++ ++***%*%%%%***=+ x+==*%*%%%%***=+ + xxxx xxxx xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_027.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_027.txt new file mode 100644 index 00000000..d0b20808 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_027.txt @@ -0,0 +1,41 @@ + + + + +++x + xx++=***%%%%%%%%%%****++++ + ++***%=* ==%***++ + ++**=*x =+**++ + ++**xx o+*%$$@@@@$$$%=xยท =*== + ===+ +$@@@@@@@@$$$$$$$@@@@@@@@*~ ++** + ==++ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ x+*= + ===+ ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ==++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ o*= + ++++ ~@@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ++++ + == o@$$$$@@$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยทx== + ox== @$$$$$% =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == o + xx++ $$$$$$@o ยท=@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$ ++xx + +++o @$$$$$$$x ~*@$$$$$@=~~~~~~~~~~~~~~~o$@$$$$@ x+++ + +++~ ~@$$$$$$$@@@%o @$$$@ x@$$$$ o+++ + +++~ ~@$$$$$$$@@$=ยท @$$$@ +@$$$@ o+++ + +++~ ยท@$$$$$$$~ x%@$$$$$@%+xxxxxxxxxxxxxx+$@$$$$@ยท ~+++ + +++~ ยท@$$$$$@x o%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@~ ~+++ + +++o @$$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + ==x $@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@@@$$@@@@$$$$$$$@@@= o+++ + x+== o%@@@@@@@@%o =@@@@@@@@$+ o%@@@@@@@$x ==xx + ++=+ +=++ + ++**++ ++%%*= %%%= x~=*++ + x+++*%*%====%*%*==xx++**=%*===***===++++***%**==*=%*==xx + ++++++++ xx++=+++ ++=+++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_028.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_028.txt new file mode 100644 index 00000000..aec5d18e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_028.txt @@ -0,0 +1,41 @@ + + + + + ++=***%%%%%%%%%%****++++ + ++***%=* ยท ==****++ + ++**=*x *=**+x + ++**xo o+*%$$@@@@$$%%=x ===+ + ===+ ยท=$@@@@@@@@$$$$$$$@@@@@@@@=~ ++== + ==+x x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ x+== + ==++ ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท +=++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@ยท ==+x + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ยทx== + ++++ o@$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++x+ + == +@$$$$@@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + ox== @$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$@= +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$% ++xx + +++o $$$$$$$@= =@$$$$$@*o~~~~~~~~~~~~~~o*@$$$$@ x+++ + +++~ x@$$$$$$$@@@$x @$$$@ @$$$$ o+++ + +++~ x@$$$$$$$@@$*~ @$$$@ ~@$$$@ o+++ + +++~ o@$$$$$$$x o*@$$$$$@%+xxxxxxxxxxxxxx+%@$$$$@ o+++ + +++~ o@$$$$$@* ~*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$@ o+++ + +++~ ~@$$$$$$$ยท ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ยท@$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + == o@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$@@$ ~+++ + ++=+ +@@@@@@@@@@*~ ~%@@@@@@@@@$x +@@@@@@@@@* ==xo + ==+o ~~~ ยท~~ ~~ +=== + ==== **++ =**= +=== + ++===*=+o~ยทยท++*=**++++*%=*x~ยทยทx+*=**++++**=*+xยทยท++*=**++ + ++======++ +=++====++ ++======++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_029.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_029.txt new file mode 100644 index 00000000..a5763027 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_029.txt @@ -0,0 +1,41 @@ + + + + + ++=**%%%%%%%%%%%%*==+++x + ++==*%=* ยท ==****=+ + ++**=* *=**++ + ++**xo o=*%$$@@@@@$$%=x ==++ + ===+ ยท=$@@@@@@@$$$$$$$$@@@@@@@@=~ ++== + ==+x +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ++== + ===+ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท +=++ + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@ยท ==++ + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ยทx== + ++++ x@$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == +@$$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยทx== + ox== ~@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$* ++xx + +++~ $$$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$@ ++++ + +++ยท x@$$$$$$$@@@$= $$$$@o @$$$$ x+++ + +++ยท x@$$$$$$$@@@*o $$$$@x @$$$@ x+++ + +++ยท x@$$$$$$$= ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$@ o+++ + +++~ x@$$$$$@$ ยท*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + +++~ o@$$$$$$$x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ~x== x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + =* +@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@ ยท+++ + ++++ =@@@@@@@@@@@+ o$@@@@@@@@@@*~ =@@@@@@@@@$o =++x + **x~ ox+xo ~x++xยท oxx~ x+== + **+= ==++ ++== ++** + ==**=*+x ยทo*+**++===*++ =+*===++**+*~ยท ยทo==**++ + ++++****==++ ++==**=+++ ++==**==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_030.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_030.txt new file mode 100644 index 00000000..7a9f0d7b --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_030.txt @@ -0,0 +1,41 @@ + + + + + ++=**%%%%%%%%%%*%*===+xx + ++===*== +=%***++ + x+**=* =+**+x + ++*= ยทx=%$$@@@@@@$$%=xยท ==++ + ==++ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ++== + ==+x +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ x+== + ===x o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท +=++ + ++++ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@ยท ==xx + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ยทx== + ++++ +@$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == =@$$$$$@@$%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยทx== + ox== o@$$$$$$x ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx+x @$$$$$$@ ~*@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$* ++xx + +++~ ~$$$$$$$$$~ o$@$$$$@$xยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทx@@$$$@ ++xx + +++ยท +@$$$$$$$$@@@%o +@$$@% %$$$$ x+++ + +++ยท +@$$$$$$$@@@%x =@$$$% %$$$@ x+++ + +++ยท +@$$$$$$$% =@@$$$$@@*+++++++++++++++*@@$$$@ x+++ + +++ยท x@$$$$$$@ =$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ o+++ + +++~ x@$$$$$$$* +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$@@ ยทx== + +++x ~=@@@@@@@@@@@$x ยทx$@@@@@@@@@@@=~ ~*@@@@@@@@@@+ ++++ + **oยท ~+***=x x=***+~ ~+**+~ ~+== + xx**x+ +o ++ xx** + ++**=*xยท o+=+**===*++ ยทx=+**====++ ++==== + +++==*%%*===++ ++===*%%**=+=+ +==***==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_031.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_031.txt new file mode 100644 index 00000000..f7615a10 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_031.txt @@ -0,0 +1,41 @@ + + + + + ++=**%*%%%$$%%%*%*==++++ + ++***%++ ++%***++ + x+**=* =+**++ + ++== ~+*%$@@@@@@@@$%*+~ ==++ + =*++ o*@@@@@@@$$$$$$$$$$@@@@@@@*o ++== + **+x =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= x+== + ==+x x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ +=++ + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==+x + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ยทx== + +++x =@$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == *@$$$$$@@$%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + ox== x@$$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$@~ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$* ++xx + +++~ ~$$$$$$$$@+ =@$$$$$@+ ~%@$$$@ ++xx + +++ยท +@$$$$$$$$@@@$+ @$$$@ x@$$$ x+++ + +++ยท +@$$$$$$$@@@%x o@$$$@ =@$$@ x+++ + +++ยท +@$$$$$$$$ +$@$$$$$@%=++++++++++++++*@@$$$@ x+++ + +++ยท +@$$$$$$@o +$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$@ x+++ + +++ยท x@$$$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$@@ ยท+== + +++o o=@@@@@@@@@@@@*oยท ~+$@@@@@@@@@@@$+~ o*@@@@@@@@@@* ++xx + **o ~=%%%%=o x*%%%*+ o=%%*x ~+** + x+**x+ x~ xx ox**xo + ==**== ==****=*+o =+****==++ x+==== + ++==*%%%%**=++ ++==**%%%*===+ ++=*%%**==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_032.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_032.txt new file mode 100644 index 00000000..0f9edba5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_032.txt @@ -0,0 +1,41 @@ + + + + ++ + xx++=*%%%%%%%%%%%%%***==++ + ++***%++ o+****++ + ++**=* =+**++ + ==== o=%$@@@@@@@@@@$%=x +=== + **++ x%@@@@@@@$$$$$$$$$$$@@@@@@%x ++** + **+o *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* o+** + ==+o +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o x=++ + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==+x + ** ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x *@$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ++++ + == %@$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ยทx== + ox+= +@$$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$ ยท=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$* ++xx + +++~ o$$$$$$$$$$o ~$@$$$$@= o@$$$@ ++++ + +++ยท +@$$$$$$$$$@@@$o %@$$@o @$$$ x+++ + +++ยท +@$$$$$$$$@@%+ $$$$$= @$$@ x+++ + +++ยท +@$$$$$$$$x x%@@$$$$@@%==============*$@$$$@ x+++ + +++ยท +@$$$$$$$$ x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$@ x+++ + +++ยท x@$$$$$$$$* o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$@@~ ยทx== + +++o ยทo=@@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%x~~o=@@@@@@@@@@% ++xx + ** ~=%$$$%=~ x*$$$%*x o=%$%+ยท ยทx** + ++**~o ~o**xx + ++****=* ++==**=*+x xx*=****++ =+**xx + ++==*%%%%%**++ ++++*%%%%%%*+++x ++**%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_033.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_033.txt new file mode 100644 index 00000000..6155425b --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_033.txt @@ -0,0 +1,41 @@ + + + + ++++++ + ++==*%%%%%%*****%%%*%**=++ + +=***%+o ยท *=**=+ + ++**+= ++**++ + ==++ ยทx*$$@@@@@@@@@@@$%+~ +=== + **+x +$@@@@@@$$$$$$$$$$$$@@@@@@@= x+** + **+o ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท ~x** + ==+o =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ x+== + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + =* ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ** + +++x *@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + == *@$$$$$$@@*=%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + ox== x@$$$$$$$x o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$@ยท o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$ ++xx + +++~ ยท$$$$$$$$$@* x@$$$$$%ยท =@$$@ xx++ + +++~ x@$$$$$$$$$@@@@* @$$$$ +@$$ o+++ + +++~ x@$$$$$$$$@@$+ =@$$$$ %$$@ o+++ + +++~ o@$$$$$$$$* o%@@$$$$$@@%**************$@@$$@ o+++ + +++~ o@$$$$$$$@ยท o%@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$@ o+++ + +++~ ~@$$$$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ยท@$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$@@o +== + +++o ~o=$@@@@$$$@@@@@%+oo+%@@@@@$$$@@@@@=xoo=$@@@@@@@@@$ ++++ + o ** ยท=%$$$$%+ x*$$$$%=~ ~=%$%=~ ยทo** + x+**ยท~ ยท~**+x + ++****==x ox*=***=+x ==****+= =+**++ + ++==*%%%%%%*==++ ==*%%%%%%*==++ ++**%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_034.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_034.txt new file mode 100644 index 00000000..690b7d83 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_034.txt @@ -0,0 +1,41 @@ + + + + +++++++++++ + ++=**%%%**==++==**%*%***++ + +=**=* =+*===+x + ++**++ xx==== + ==== o=%$@@@@@@@@@@@@@$%+~ ++**xo + **+x ยท=@@@@@@$$$$$$$$$$$$$$$@@@@@$x ~**+x + **+o ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + ==+o =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ยทx** + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ x+++ + ** ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ +@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ o+++ + == +@$$$$$$$@%xo=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + == @$$$$$$$@ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++xx + ++++ $$$$$$$$$@ ยท=@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$@ x+++ + ++xx @$$$$$$$$$@%~ o$$$$$$+ +$$$~ ~+++ + +++x @$$$$$$$$$$@@@@% o@$$@% *@@+ ยท+++ + +++x @$$$$$$$$$@@*~ %$$$$$ $$@+ ยท+++ + +++x @$$$$$$$$@x ยท=@@@$$$$$@@$$$$$$$$$$$$$$$@@$$@+ ยท+++ + +++x @$$$$$$$$@ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++x @$$$$$$$$$@x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + xxxx @$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยทx== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox+= *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$@+ == + +++o ox+*@@@@@$$$@@@@@*+xx=$@@@@$$$$@@@@$=xx+%@@@@@@@@@$ ++++ + ox** x%$$@$$*x ~=%$@@$%=ยท x%$$*o ** + ++** **xx + xx*****=xx ==****+= ++****+= =+**+x + xx++***%%%%**=++ ++**%%%%%%**++ ++=**%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_035.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_035.txt new file mode 100644 index 00000000..32f4209a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_035.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++++ + ++=**%*%==xxxxxx++**%*%*==++ + +=**=* ~+*=**++ + ++**++ ==**xx + ===+ +%$@@@@@@@@@@@@@@@$%+ ox**++ + **+x ~*@@@@@$$$$$$$$$$$$$$$$$@@@@@*~ ==++ + **x~ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ ==++ + ==+x =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ **xx + ++=+ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ~+== + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +++x + ++++ o@$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยทx== + ==+ยท ~@$$$$$$$$@+ ยท+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + == @$$$$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ox== =$$$$$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$@~ ~+++ + xx++ @$$$$$$$$$$@%o x$$$$$$ =$@* ยท+== + xx++ @$$$$$$$$$$$@@@@= %@$$@~ @@$ == + xx++ @$$$$$$$$$$@%x o$$$$$% +$@$ == + xx++ @$$$$$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$@$ == + xx++ @$$$$$$$$$$ +%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$@x x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$@= == + +++o oxx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@*+xx*@@@@@@@@@@ยท x+++ + o ** ~=$$@@$%=ยท +%$@@@$*x ~*$$*x ** + ++** **+x + xx******++ =+****+= +x==**== =+**++ + +=**%%%%%%**++ ++***%%%%***+++x ++==*%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_036.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_036.txt new file mode 100644 index 00000000..9545578f --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_036.txt @@ -0,0 +1,41 @@ + + + + ++++==****==++++ + x==***%==oo x+==%***=+ + xx===*++ ++**== + ++**x+ ยทoxx++xxoยท ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$=~ +=== + **+x o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%~ x+== + =*+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท x+== + ++=+ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท *=o~ + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+++ + ox== @@$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++o @$$$$$$$$$$~ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == %$$$$$$$$$@ =$@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$@x ยท+== + == @$$$$$$$$$$o =@@@$$$$@@@$$$$$$$$$$$$$$$@@$@ == + == o$$$$$$$$$$$@@*~ %$$$$$ $$ == + == x@$$$$$$$$$$$@@@@* +@$$@* %@ == + == x@$$$$$$$$$$@*ยท +@$$$$@= =$@ == + == x@$$$$$$$$$@ ~*@@@$$$$$$@@@@@@@@@@@@@@@@@@@$@ == + == x@$$$$$$$$$@ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$@%xx*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$@% == + +++o ~+x+*@@@@@$$$$@@@@%=xx=$@@@@$$$$@@@@@*xx+*@@@@@@@@@o x+++ + ** o*$@@@$%+ =%$@@$$*o x*$%x ** + x+** **+x + xx******+= x=***==x ==**==++ =+**++ + ++==*%%%%*%*==xx ++==*%*%%%%*==++ ++**%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_037.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_037.txt new file mode 100644 index 00000000..1e29736e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_037.txt @@ -0,0 +1,41 @@ + + + + ++++=***%%%%***=++++ + ++==*%*%+x x+**%*==++ + x+**=*+o *=**++ + ++**x ~x==*****=+oยท *=== + **++ +%@@@@@@@@@@@@@@@@@@@@$=~ ++**xx + **+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@@* ~o**xx + ==+o o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ยทx** + ++=+ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + +++o %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + == $@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ++++ $@$$$$$$$$$* o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+++ + +++ยท o@$$$$$$$$$@ o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$@ == + == $$$$$$$$$$$$+ x%@@$$$$@@%*=============*$@$ยท == + == @$$$$$$$$$$$@@$+ %$$$$% $= ==xx + == @$$$$$$$$$$$$@@@$x =@$$@= $* ==xo + == @$$$$$$$$$$$@x %@$$$$@*ยท ~$@= ==xo + == @$$$$$$$$$$@ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@@@= ==xo + == @$$$$$$$$$$@ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == *@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$@% == + +++x ~=xx+%@@@@$$$$@@@@@*+xx*@@@@@$$$$@@@@%=xx+%@@@@@@@@o o+++ + ** +%$@@$$*o o*$$@@$%+ +%%x ** + x+** **++ + ++**==****xo ==****++ =+****+= =+**++ + ++ x+==*%*%%%%*==++ ==**%%%%%%**=+ +=**%***++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_038.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_038.txt new file mode 100644 index 00000000..01d7bdf8 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_038.txt @@ -0,0 +1,41 @@ + + + + x+++==*%%%%%%%%%%%%*==++ + ++***%++ ox**%*==+x + x+**+= x+*=== + ==== ยทx=*%$@@@@@$$%*+~ x+**++ + **++ o*@@@@@@@@$$$$$$$@@@@@@@@%+ ==++ + **+o =@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@%o +=++ + ==+x o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=++ + ++++ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==+x + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ยทx== + ++++ x@$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ +++x + == +@$$$$$$$$$@@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + ox== @$$$$$$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$@= =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@% ++xx + +++o $$$$$$$$$$$$@= =@$$$$$@*o~~~~~~~~~~~~~~o%@ x+xx + +++~ o@$$$$$$$$$$$$@@@%x @$$$@ ~ o+++ + +++~ x@$$$$$$$$$$$$@@$*~ @$$$@ x o+++ + +++ยท x@$$$$$$$$$$$$o o%@$$$$$@%xxxxxxxxxxxxxxx+$@ o+++ + +++ยท x@$$$$$$$$$$@+ o*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$ o+++ + +++ยท +@$$$$$$$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++ยท +@$$$$$$$$$$$@@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ xxxx + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == x@@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$@$ == + ++++ *+oox*@@@@@$$$@@@@@*xox=$@@@@$$$@@@@@%+oo+%@@@@@@@x o+++ + **oยท x%$$@$%*o ~=%$$$$%+ +*x **xo + xx**~ยท **++ + xx**+=****+= ++****== x+******xo =+**+x + ++ +=***%%%%%**== ++==*%*%%%%*==+x x+==***=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_039.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_039.txt new file mode 100644 index 00000000..cea0cf6e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_039.txt @@ -0,0 +1,41 @@ + + + ++++++ + +++=**%$%%******%%$%%*=+++ + ++***%+o x+%=**++ + ++**+= ==**++ + ===+ ยทx*%$@@@@@@@@@@$%*x ==== + **+x +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ++** + ==+o *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~+** + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ~+== + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ++++ + ==xยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + xx++ @@$$$$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% o+++ + ==xยท @$$$$$$$$$$@@%=*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + == @$$$$$$$$$$$$ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++xx + ox== +$$$$$$$$$$$@% ยท*@@@@$$$$$$@@@@@@@@@@@@@@@@@@ o+++ + xx++ $$$$$$$$$$$$$@$o ~$$$$$$@+ x= ยท+== + xx++ @$$$$$$$$$$$$$$@@@$o $$$$@ x== + xx++ @$$$$$$$$$$$$$@@%o $$$$$+ ยท x== + xx++ @$$$$$$$$$$$$$ =@@$$$$$@@%**************%@$ x== + ++++ @$$$$$$$$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$* ยทx== + +++x @$$$$$$$$$$$$$+ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + ++xx @$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++~ ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ xx++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + ==+ยท ~@@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$@$ == + ++++ %*xoox%@@@@@$$@@@@@$+oox=@@@@@$$$@@@@@*xoo+%@@@@@@x ~+== + **+ยท +%$$$$%+ o=%$$$%=o +o **xo + xx**o~ **++ + xx**+=****==+o ~x==**==++ ==****++ =+**+x + ++xx x+==*%%%%%%*==++ x+++**%%%%%*==++ ++****++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_040.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_040.txt new file mode 100644 index 00000000..e8bfe577 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_040.txt @@ -0,0 +1,41 @@ + + + ++++======++++ + +=***%=*++++xx++++**%*%*==+x + xx==**++ o+****++ + ++**x+ ยท *=** + ===+ ยท+*$@@@@@@@@@@@@@@@$*+ยท xx**++ + **+x ยท*@@@@@$$$$$$$$$$$$$$$$$@@@@@%o =*++ + ==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@x +=++ + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ox=* @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+++ + == $@$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + ++++ %@$$$$$$$$$$$@$o ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==xยท o@$$$$$$$$$$$@* ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$+ ยท+== + == @$$$$$$$$$$$$$% ~*@@@$$$$$@@@@@@@@@@@@@@@@@@ == + == $$$$$$$$$$$$$$@@*~ =$$$$$* == + == @$$$$$$$$$$$$$$$@@@@x @$$$@ == + == @$$$$$$$$$$$$$@@*~ =$$$$$* == + == @$$$$$$$$$$$$$* ~=@@@$$$$$@@@@@@@@@@@@@@@@@@o == + == ยท@$$$$$$$$$$$$@= ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == ~@$$$$$$$$$$$$$@%~ ~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยทx== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++~ ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ~+++ + +++ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==x %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++~ @@$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$@@ == x + ++++ =@*oยทยท~+@@@@@@@@@@@@$+~ยท~x%@@@@@@@@@@@@*oยทยทo=@@@@@x ~+== + ==x~ ~=%%$%*+ x*%$%%=~ ยท **xx + x+**x~ oo **++ + x+**+===****+= *=****==xo ++==**==++ =+**++ + ++++ ++****%%%***++ ++==**%%%***+++x ++==**++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_041.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_041.txt new file mode 100644 index 00000000..aa4c7489 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_041.txt @@ -0,0 +1,41 @@ + + + x+++==******===+++ + ++==****++xo ยท~++**%***++ + ++****+x ==%*++ + ++*=ox ~ox+++xx~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@%=~ ==== + =*+x o%@@@@$$$$$$$$$$$$$$$$$$$@@@@@%o x+** + ==+x ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o ~+** + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+== + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=++ + ++++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ** + == +@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + xx== o@$$$$$$$$$$$$$% ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@o ยท+== + +++x @$$$$$$$$$$$$$$ ยท=@@@@$$$$$$$$$$@@@@@@@@@@@@$@ == + +++ยท +$$$$$$$$$$$$$$$ยท ~*@@$$$$$@@@%%%%%%%%%%%%%%= ==xo + == %@$$$$$$$$$$$$$$@@=~ $$$$$% ++xx + == $$$$$$$$$$$$$$$$$@@@@= *@$$@x ++xx + == $$$$$$$$$$$$$$$$@= =@$$$$$x ++xx + == $$$$$$$$$$$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@ ++xx + == @$$$$$$$$$$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++ยท x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + ==+ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + +++o @@$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$@@ ==xo + xx== x@@+~ ยทx$@@@@@@@@@@@*o o*@@@@@@@@@@@$+~ ยท+$@@$o ~+== + ==+o +*%%*=o o=*%%*x *=+x + ox**+o ++ x+ o~**++ + xx**===+****==+= ++*=****+= o+*=****==++*=**++ + ++++xx x+==********==+x ++********==++ ++==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_042.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_042.txt new file mode 100644 index 00000000..96715cf2 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_042.txt @@ -0,0 +1,41 @@ + + + xx===***%%%%%%%%***=++ + ++**=%=+ ยทo==%*==++ + ++**+= *=**++ + ==== o+=*%$$$$%*=+o =+** + **++ ~=$@@@@@@@@@@@@@@@@@@@@@%+ o**++ + ==+x +$@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$+ ==++ + ++++ ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* *=xo + ==+ยท %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ยทx== + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + +++~ @$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + ox== x@$$$$$$$$$$$$$$@ =$@@@$$$$$$$$@@@@@@@@@@@@@@ ++x+ + xx++ $$$$$$$$$$$$$$$$$* =@@$$$$@@*+++++++++++++ ~+++ + ++++ @$$$$$$$$$$$$$$$$@@@*x *@$$$% ยท+== + +++x @$$$$$$$$$$$$$$$$$@@@*o =@$$$* ยท+== + ++xx @$$$$$$$$$$$$$$$$$~ o$@$$$$@$xยทยทยทยทยทยทยทยทยทยทยทยทยท ยท+== + xx+x @$$$$$$$$$$$$$$$@ o*@@@@$$$$$$$@@@@@@@@@@@@@@* ยท+++ + +++x @$$$$$$$$$$$$$$$$~ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$+ ยท+++ + +++o @$$$$$$$$$$$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++o ยท@$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ยท+++ + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ++++ *@@$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@@@@$@@@ ==xx + ox== %@@=~ x$@@@@@@@@@@%x ~*@@@@@@@@@@@=ยท x$$~ ~+== + ==++ o+==x~ ~x+=+o *=xx + ox**++ == == == xo**++ + xx==**+=*=****==+= ยทx*=******+=x~ *=******+===**++ + x++=++ ++==********++xx xx++********++++ ++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_043.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_043.txt new file mode 100644 index 00000000..849ab379 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_043.txt @@ -0,0 +1,41 @@ + + + ++==***%*%%%%%%%%%**=+++ + ++**=*+x o+**%*== + ++**+= x+**=+ + ===+ o+*%$@@@@@@@$%=+~ ++**+x + =*=x o*@@@@@@@$$$$$$$$$@@@@@@@%+ +=++ + ==+x +@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@$x x+== + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o x+== + xx== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ +=++ + ==+~ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o *=xo + ox== $@$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + +++x $@$$$$$$$$$$$$$$@@@%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==x =@$$$$$$$$$$$$$$$% +$@@@@$$$$$$$$$$$$$$$$$$$$$$$ ++++ + == @$$$$$$$$$$$$$$$@= +$@@@$$$$$$$@@@@@@@@@@@@@= ยทx== + == o$$$$$$$$$$$$$$$$$@= =@$$$$$@*~ยทยทยทยทยทยทยทยทยทยทยท~ == + ox== =@$$$$$$$$$$$$$$$$$@@@$+ @$$$@ == + ox== *@$$$$$$$$$$$$$$$$$@@$=~ @$$$@ยท == + ox== *@$$$$$$$$$$$$$$$$$o o%@$$$$$@$+xxxxxxxxxxx+ == + ox++ %@$$$$$$$$$$$$$$$@= o%@@@@$$$$$$$@@@@@@@@@@@@@@ == + xx++ %@$$$$$$$$$$$$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++~ ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ~+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx+x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ยทx== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ++++ o@@$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@@@@@@$ ==xx + xx== =@@@+ =@@@@@@@@@@%o x$@@@@@@@@@@+ ยท= ~x== + ===+ ~xxo ยทoxoยท ยทo**xx + ===+ =*=+ +=*= x+== xx**++ + ==**=**=**==**=*+= ยทo=+*=****===*x~ +=*=**==***=**++ + ++++++++ ++==******==++ ++==******==++ x+++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_044.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_044.txt new file mode 100644 index 00000000..5ebc901a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_044.txt @@ -0,0 +1,41 @@ + + xx++++++ + ++***%*%*%****%%%%$%**== + x+==**+= x+%%%*++ + ++**x+ =***++ + ===+ ~+%$@@@@@@@@@@@@$*+ยท *=== + ===+ +$@@@@@$$$$$$$$$$$$$@@@@@@$=ยท xx**xx + ===+ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **+x + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xo + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx** + ++++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ++++ + == x@$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@* == + xx== ~@$$$$$$$$$$$$$$$$$@$=+%@@@@$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + +++x @$$$$$$$$$$$$$$$$$@ x%@@@@$$$$$$$$$$$$$$$$$$$$* == + ==+ยท +$$$$$$$$$$$$$$$$$$@ x%@@@$$$$$$@@@@@@@@@@@@ == + == $$$$$$$$$$$$$$$$$$$$@= =@$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$@@@@= +@$$@* ++xx + == @$$$$$$$$$$$$$$$$$$$@@%x %$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$x +$@@$$$$@@$%%%%%%%%%%* ++xx + == @$$$$$$$$$$$$$$$$$$@ x$@@@@$$$$$$$$$@@@@@@@@@@% ++xo + == @$$$$$$$$$$$$$$$$$$$% +%@@@@$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + x+== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + +++o ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+++ + xx+= $@@$$$@@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@% ++xx + ox==xยท ยท%@@@%o ~%@@@@@@@@$+ =@@@@@@@@@*~ o+== + +++= ~x**xo + ====~o **== =*** +=%%x+++**++ + ++**===*%=**====**=*++++==*=**====**=*==++++%**=**==**==++ + ++++==++++ ++++****==++++ x+++==***=+++x xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_045.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_045.txt new file mode 100644 index 00000000..5ad22dd6 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_045.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x+==***%****======%%%%%%==++ + xx=**=++ **%*++ + ++**xx x+%*++ + ===+ x*$@@@@@@@@@@@@@@$*+ยท ++**+x + ===+ =$@@@@@$$$$$$$$$$$$$$$@@@@@$+ **++ + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ==++ + x+== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ==xยท *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ **xo + x+++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ยท+== + ==+ยท @$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + == @$$$$$$$$$$$$$$$$$$$@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$@ ยท+== + xx++ %$$$$$$$$$$$$$$$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$@ == + +++o @$$$$$$$$$$$$$$$$$$$$ o%@@@$$$$$$@@@@@@@@@@@ ++++ + +++ยท x$$$$$$$$$$$$$$$$$$$$$@= =@$$$$$o ++++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$@@@@= %@$$@o x+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$@$= $$$$$% x+++ + ==+ยท =@$$$$$$$$$$$$$$$$$$$$ o%@@$$$$$@@@$$$$$$$$$ xx++ + ==+ยท *@$$$$$$$$$$$$$$$$$$@$ o*@@@@$$$$$$$$$$$$$$$$$$@ x+++ + ==x %@$$$$$$$$$$$$$$$$$$$$% o*@@@@$$$$$$$$$$$$$$$$$$$$$$$ ++++ + == $@$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + xx++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ *@$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@@@@ ~+== + ++== *@@@$$$@@@@$%%@@@@$$$$$$$$@@@@%%$@@@$$$$$$$$@@@@$%$* =+xx + ==+~ +$@@@%o ~*@@@@@@@$= +$@@@@@@@*o o+== + ++==o x+*=xx + ====x+ ++%%== ox***= ****+=**++ + ++**=*=****=**++==**=%****%*%***++==**=*=***%%%***======+x + ++++++++ ++++======++ x+++======++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_046.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_046.txt new file mode 100644 index 00000000..be798415 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_046.txt @@ -0,0 +1,41 @@ + + ++++====++++xx + x+==***%=*=+xooox+==%%%*%*== + xx**==+x x+%*==+x + ++**ox ยทยทยทยท %=== + ===+ ยท+%@@@@@@@@@@@@@@@@$%+ยท x**++ + ==++ *@@@@@$$$$$$$$$$$$$$$$$@@@@@%+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ x+== + ==+~ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +=++ + ox== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x *= + +++o $@$$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$@% o+++ + == %@$$$$$$$$$$$$$$$$$$$$@$o ยท=$@@@$$$$$$$$$$$$$$$$$$@$ == + == @$$$$$$$$$$$$$$$$$$$$@% =$@@@$$$$$$$$$$$$$$$$% ++x+ + x+++ %$$$$$$$$$$$$$$$$$$$$$$$ =$@@$$$$$@@@@@@@@@ o+++ + xx++ @$$$$$$$$$$$$$$$$$$$$$$@@%o x$$$$$% ยท+++ + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ $$$$@ ยทx== + ++++ @$$$$$$$$$$$$$$$$$$$$$$@@%o x$$$$$% ยทx== + +++x @$$$$$$$$$$$$$$$$$$$$$$$ =$@@$$$$$@@@@@@@@@% ยท+== + xx+x @$$$$$$$$$$$$$$$$$$$$$@% =@@@@$$$$$$$$$$$$$$$$$= ยท+== + +++x @$$$$$$$$$$$$$$$$$$$$$$@$o ~=$@@@$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยทx== + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ox++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + ox== x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== @$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@ ยท+== + ox== @@@@$$$@@@@@*+x+*@@@@$$$$$@@@@%=+x=$@@@@$$$$@@@@@*x ==++ + ==++ x%$@@$=o x%$@@@$%x ~=$@@@@$=~ ++== + ++==+o x+==+x + ++==++ ++****++ ++*=*=++ x*=**==xx + ++==*****%%%%***++++*****%%%%**=**++++==***%*%%*****==++ + ++++++++++ xx++++++++++xx ++++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_047.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_047.txt new file mode 100644 index 00000000..8dfb7978 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_047.txt @@ -0,0 +1,41 @@ + + ++++========++++ + ++==**=*==+x~ยทยทยทยท~x+**%%%%*=+x + ++**==xx ยทx%*%*++ + ++==~o ~~ooo~ยท *=**+x + ===+ ~=$@@@@@@@@@@@@@@@@@$*x *==+ + ===+ *@@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท ++** + ++=+ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o ox** + ox== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~+*= + ==+o ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ x+++ + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==+x + ++++ *@$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@ ยท+== + ==xยท x@$$$$$$$$$$$$$$$$$$$$$$$%~ x%@@@@$$$$$$$$$$$$$$$$@ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$ x%@@@@$$$$$$$$$$$$$@ x+++ + o == x$$$$$$$$$$$$$$$$$$$$$$$$$ x%@@$$$$$@@@$$$$= ยท+== + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$@$+ ยท$$$$$% == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= %@$$@o == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$@*~ +@$$$$$~ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$ ~*@@@$$$$$$@@@@@@@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$@+~o=@@@@$$$$$$$$$$$$$$$$$$@$ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + o == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + x+++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o ~+++ + ==xยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ +++x + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++xo + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== @@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$@@$ ~+== + ox== +$@@@@@@@@@@@%+~ o*@@@@@@@@@@@@=o ~+$@@@@@@@@@@%ยท ==++ + ==++ ~+*%%%=x x=%%%*+~ ~+*%%=x +=== + ++==++ xo xx +===xx + ++====++ ======++ox =+**====++ =+====+x + ++++=***********==++++==**=**%******++++==**=*********++ + ++++++++++ ++++++++++ ++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_048.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_048.txt new file mode 100644 index 00000000..74a3df81 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_048.txt @@ -0,0 +1,41 @@ + + ++======**====++ + ++****=*++o~ ยท~++%*$%**++ + x+**+= **%*++ + ==== ~oxxxxxo~ ++**++ + ===+ o*$@@@@@@@@@@@@@@@@@@%=~ ==== + ===+ *@@@@$$$$$$$$$$$$$$$$$$$@@@@@%x xx**xo + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + ** =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ** + +++x @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยท+== + =* x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ++++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$@+ ** + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$x +$@@@@$$$$$$$$$$$$@= ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท x$@@@@$$$$$$$$$@~ ยท+++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$* x$@@$$$$$@@$$ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x +$$$$$o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o @$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$@$o ยท$$$$$$% == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$@x =$@@@$$$$$@@@@ == + xx== +@$$$$$$$$$$$$$$$$$$$$$$$$$$@x =$@@@$$$$$$$$$$$$$ == + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$@$+x=$@@@$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + xx++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +x+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + +++ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ox== *@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$@@% ~+== + ==+~ +@@@@@@@@@@%x ~%@@@@@@@@@@= =@@@@@$+ ==++ + ==== ~ooo ยทooo~ +=== + xx==++ยท~+==+ +=** o+** xo==== + ++==========+= ~ ~x==*=======+=xo ++%=======+=++==*===== + x+++++++==**********==++++====********==++++++==****==++ + ++++++++ xx+++++xxx ++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_049.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_049.txt new file mode 100644 index 00000000..a8e97f28 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_049.txt @@ -0,0 +1,41 @@ + + ++====*****=**=+++ + ++***%==++ ยทยทx+**$%**++ + ++**+= =*%*++ + ==== ยทox+++++xoยท x+**++ + ===+ x*@@@@@@@@@@@@@@@@@@@$=o *+== + ===+ ยท*@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= xx**xx + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% **+x + ** +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ **xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ยทx** + ** o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ++++ + xx++ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% +$@@@$$$$$$$$$$@% +++x + == %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ +$@@@@$$$$$$@+ ยท+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x +$@@$$$$$@ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@%o %$$$$$ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@* o@$$@% == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@=ยท +@$$$$@* == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o%@@@$$$$$$@~ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o*@@@@$$$$$$$$$$ == + o == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%++*@@@@$$$$$$$$$$$$$@ == + x+== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +++x + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == o + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ยทx== + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + == ~@@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$@@@+ o+== + ==+x o =@@@@@@@@@*ยท o$@@@@@@@@$x *@*~ ==xx + +++=xยท ==+= + oo====xo x+%%+x %%=+ %*%* ++==== + ++============+==*++++*=%*=======*+=++++==**=========*%=*=++ + ++==++xx++====******==++ ++====******==++xx++====++++ + ++++ ++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_050.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_050.txt new file mode 100644 index 00000000..3151bf44 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_050.txt @@ -0,0 +1,41 @@ + + ++===*******===+++ + ++****=*+x o+**%**=++ + ++**++ +=$*== + ==== ~o++===++o~ o+%*++ + ==++ x%@@@@@@@@@@@@@@@@@@@$*x =+** + ===+ ~%@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= o**+x + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท **xx + ** =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= **xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ยทo** + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+++ + xx++ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$@% *= + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* +$@@@$$$$$@$ +++x + == %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +$@@@$$@= ยทx== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ +$@$@ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@%o $$ยท == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ *@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *@@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$@@@$@ยท == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$@@@@$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@%+=$@@@@$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +++x + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% x== + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @@$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@@@% == + ==xยท $@@@$@@@@$***%@@@$$$$$$$@@@@%***@@@@$$$$$$@@@@$***%ยท x+== + ==++ +$@%+ =$@@@@@$*o x%@@@@@@%+ ยทx==xx + +++++x xx==++ + ==+=++ ==**=+ +=%**= x+***==+==++ + =======*%**==========*****%**==========*******%**=======++ + ++++====++ ++++========++xx xx++========++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_051.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_051.txt new file mode 100644 index 00000000..bdf577d2 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_051.txt @@ -0,0 +1,41 @@ + + x++===*******===+++ + ++**=*==+x o+**%**=++ + ++**+= ==%*++ + ===+ ~x+=====+x~ x=**++ + **+x +%@@@@@@@@@@@@@@@@@@@$*x =+== + ===x o$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= ox**xx + ++=+ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* **xx + oo== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ** + ==+o o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยท+== + == *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ++++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@@$$$$@+ == + ==xยท ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท=@@@@$@= ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท=@@@~ ~+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ยท* == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@*~ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$o == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% =$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% =@@@@$$ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@*+=$@@@$$$$$@ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% +++x + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ยท+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= ** + ==+ยท =@@@@$$$$@@@@@*+xx=$@@@@$$$$@@@@%=xx+%@@@@$$$$@@@@* ++++ + ==++ +%$@@@%=o o*$@@@$*+ +%$@@$*+ o+==xo + ++++++ ++==++ + ====*=x+ ++**==++ x+*===++ x+====++ + ++=====***%%***=**+++======*%%%*****==++====*%*%%**=**==++ + ++++====++++xx +++++===++++++ ++++++==++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_052.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_052.txt new file mode 100644 index 00000000..22bc6aea --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_052.txt @@ -0,0 +1,41 @@ + + ++++===*******===+++ + +=***%==x~ o+**$%*=++ + +=**++ **%*++ + x+**++ ยทox+====+xo~ ==**+x + xx**xo o*@@@@@@@@@@@@@@@@@@@@%=~ ==++ + xx**xยท *@@@@$$$$$$$$$$$$$$$$$$$$@@@@@%o ++*= + ==+o x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+** + ++++ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ~+== + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + ==+ยท @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@@ o+++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท x == + ox++ *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* x+++ + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$=ยท ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@*ยท ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@*ยท ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@=+*@@~ o+++ + +++ยท x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$$ o+++ + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + o == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == $@$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$@@@ ยทx== + ==+ยท x@@@*o o%@@@@@@@@@@@%xยท ~=@@@@@@@@@@@@=~ x%@$~ ==++ + ==++ x=***=o o+***=+ยท ++== + x+===+ ++ ox ~x +===xx + ++====x+==*====+++ ++*=====++ x+*=*===++*+====xx + ++==****++++==************++++++=*******%***==++++==**++ + ++ ++++++++++xx ++++++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_053.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_053.txt new file mode 100644 index 00000000..a49bc03a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_053.txt @@ -0,0 +1,41 @@ + + ++++==*********==+xx + x+==**=*+=o~ ยทยท++%*%%*=++ + ==**++ %***++ + x+**++ ~o++====+x~ *+** x + ++** +%@@@@@@@@@@@@@@@@@@@@*x **++ + ++=* o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= +=++ + ox**o %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=++ + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยทx== + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + ==xยท *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$o ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == o + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ox== $@$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@% ~+== + ==xยท o$@@@@@@@$+ +@@@@@@@@@@@%o o%@@@@@@@@@@$+ ==xx + ==++ ยทx+o ยทx+=+xยท o+==+o +=== + xx===+ == ++ o+=++=== + ++====+=xo ~x*===**==+=xo ++****==+=+x ++*=**==== + xx++=*********++++++=***********++++++==**********==++xx + ++xx++ xx++++xx xx++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_054.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_054.txt new file mode 100644 index 00000000..ffc240ee --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_054.txt @@ -0,0 +1,41 @@ + + ++====******====++ + x+==**=*++oยท ~x==%*%*== + ++**=*+x ++%=== + ++**xx ~x++==++o~ o+**++ + ===+ o*$@@@@@@@@@@@@@@@@@@@*x =+** + ===+ ยท*@@@@$$$$$$$$$$$$$$$$$$$$@@@@$= oo**xx + ++=+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* **xx + xx== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ยท~** + ==+~ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ o+== + xx== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xx + +++o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +++x + ++++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยทx== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยทx== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + ++xx @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยทx== + xx== @@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$@@@~ ++++ + ox==x =@@@@@@@@@@%~ ~%@@@@@@@@@$+ =@@@@%~ ยทx== + ==++ ~oo~ ยทoooยท ==++ + xx===+ %% %% **=+ +x==++ + ox====**====+*+x ++*=**===*=*++ xx**==**=*==+=+=*=**++ + ++xx ++==********==++ ++==********==++ ++++****++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_055.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_055.txt new file mode 100644 index 00000000..f233e3ef --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_055.txt @@ -0,0 +1,41 @@ + + ++=====*****====++ + ++***%=*=+oยท x+**%%**=+ + ++**+* ==**=+ + ==== ยท~ox+++xo~ ++**++ + xx**++ +%@@@@@@@@@@@@@@@@@@@%+ยท =*== + ox**xo +$@@@@$$$$$$$$$$$$$$$$$$$@@@@@*~ ++*= + =*+o o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ++xx + +x++ =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +x++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ยท+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x+++ + ++++ ยท@@$$@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@@% ==++ + xx== x$$= ~%@@@@@@@@@%~ =@@@@@@@@@$+ o= o+== + ==++ ยทยท ยทยท ยทx==xx + ox===+ %*+x %%=+ **** ++**++ + x==**=**=**==**+*++xxxx++%***==**=*=*xxoo++%=***=======**++ + xx++++xx xx++********++xx ++==******==++ ++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_056.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_056.txt new file mode 100644 index 00000000..60a52991 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_056.txt @@ -0,0 +1,41 @@ + + ++++====**====++++ + ++***%=*==o~ ยท o+*=%%%*==++ + ++**== x+%***++ + ++**++ ~ooooo~ยท **== + ++**o o=$@@@@@@@@@@@@@@@@@@*x ++**++ + ++== o%@@@@$$$$$$$$$$$$$$$$$$$@@@@@= **++ + ++== ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* **++ + ox=* +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ยทo** + ==+o ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x+== + xx== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + ==+o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+== + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ยท *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ==xo + +++x =@@$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@$~ == + xx== +$@@@@@$+ =@@@@@@@@@*ยท o%@@@@@@@@$+ ++++ + ===+ x+== + ===+ **%% ++%%+x x+%%=+**xx + ++**+*++++*=**====**=*=+++++***=**++**=*==++++==%=**++=+ + xx++=====+xx xx++======++++ ++========++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_057.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_057.txt new file mode 100644 index 00000000..ce6e787a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_057.txt @@ -0,0 +1,41 @@ + + xx+++===========++xx + ++++***%=*==xx~~~~ox==******+++x + xx==**== ++**==xx + ++**++ ยท~~~~ยท ++**++ + ==== x*$@@@@@@@@@@@@@@@@@*+ ==*= + ===+ =$@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท ++** + ===+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท x+== + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xx + +++x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ยท+== + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + xx++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==+ยท ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + =* @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+~ %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@$ ยทx== + ++== o%@@@@@@@@%o *@@@@@@@@$= x%@@@@@@@$= ++++ + ===+ +=== + ====xx ++%%=+ %%== +=== + ++**=*==++++=**=**++=*****++++==%=**++==***%++++==%***++ + ++=======+++ ++++=====+++ xx++++==++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_058.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_058.txt new file mode 100644 index 00000000..76dd6000 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_058.txt @@ -0,0 +1,41 @@ + + xx++++++====++++++ + ++==**%%*%**==++++==**%*****++ + ++***%== x+*=**++ + ==**++ ==**++ + xx**++ x*$@@@@@@@@@@@@@@@%=o x+**++ + ++**xx ~*@@@@@$$$$$$$$$$$$$$$$@@@@@$+ ~==++ + xx**o~ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + =*+~ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ยทx*=xx + ++++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+== + x+== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + +++x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x+++ + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + +++~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ~+++ + +++o ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + == $$$$@@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$@@@+ x+++ + ++++ +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@%o ==+x + ==++ +=++ + ===+=*%% ==%%++ ++%%*= x~==== + ++++++**=%**++==%***==++==*%**++++**%***++==***%***=**++ + x+++==++++ ++++++==+++x +++++x + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_059.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_059.txt new file mode 100644 index 00000000..c2adc0c0 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_059.txt @@ -0,0 +1,41 @@ + + xx++++++++++++++++ + ++==***%%%************%***==++ + ++***%== =+****++ + xx====++ ++====xx + ++**++ x=%@@@@@@@@@@@@@$%+o +=**xx + ++**oo o%@@@@@@$$$$$$$$$$$$$$@@@@@$= x+*=+x + ++== +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* x+==xx + x+== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ++== + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==+x + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==+x + ++++ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* == + == @@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $%%%$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++~ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ยท+++ + xx++ *@$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$@ ~+++ + == x@@@$$$@@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$@@@ยท ++++ + ++++ +$@@@@@@@$= ~*@@@@@@@@%~ +$@%~ == + ==+x ==++ + ===+o ==%%+o ==%%++ ++%%== xo=*++ + ++**++++***%**====%***==++==***%====**%***++++**%%%***++ + ++++==++ ++++++++++ ++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_060.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_060.txt new file mode 100644 index 00000000..f28078a1 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_060.txt @@ -0,0 +1,41 @@ + + ++++++++++++++ + ++++****%%%%%%%%%%%%%*****++xx + ++***%**+x +=*=**==+x + x+**=*++ ==**++ + ++**+= ยทx=%$@@@@@@@@@$%=o xx==== + ==== o*@@@@@@@$$$$$$$$$$$@@@@@@%x ==== + ==*= =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ox== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+== + ==+o x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+++ + x+== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+== + ==+o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++++ + xx== x@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + xx++ ~%@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ยท+== + ++++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ ============*@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ +++x + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + ==+ยท *$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+xx + +++x @@$$$$$$@@$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$@ x+xx + ox== =@@@@@@$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$$@@@$$$@@@@@$ ==xo + +++x ~*ยท =$@@@@@@@%x o%@@@@@@@@* = ยทx== + **+o ==++ + ==+= ++%$=* %%** **%% xo**++ + ++****==++++***%====***=**++++***%**==**%=**++++****==xx + ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_061.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_061.txt new file mode 100644 index 00000000..c33681cc --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_061.txt @@ -0,0 +1,41 @@ + + ++++++++++++++ + ++++*****%*%%%%%%*%%****==++ + ++=****%++ x+*===**++ + x+==**=* =+====++ + ++**+= ~x=*%$$@@@$$%=+~ ++==++ + ====oo =%@@@@@@@@$$$$$$@@@@@@@@%+ ox==++ + ==*= +$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%ยท ยทx==++ + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ o+==xx + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=== + ==+~ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+ยท @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + ox== @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + xx++ %@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ++xx *ooooooooooooooo=@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o =ooooooooooooooo=@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ~+++ + +++o @@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++o @@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$@@ ++++ + ox== *@@@$@@@$$$@@@@$$$$$$$$@@@@$$@@@@@$$$$$$$@@@@$$$@@@$ ==xo + ==+x o%$= ~%@@@@@@@@%~ =$@@@@@@@$+ ยท+== + **+o ==xx + ==+= ++%%=+ ++%**= %%*% oo**++ + ++***%**++xx+=***%====*=**==++++***%====***=**++++**==+x + ++++++++ ++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_062.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_062.txt new file mode 100644 index 00000000..b8e23ebe --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_062.txt @@ -0,0 +1,41 @@ + + xx++++++++ + xx++==*******%%*******==++++ + ++==**=***++ ยท ++==*=**==++ + ++==**=%+o ox=+====++ + ++**== ~ox+====++oยท xo+===++ + ++==++ ยท+%@@@@@@@@@@@@@@@@@@@@*x +===++ + ====~ *@@@@@$$$$$$$$$$$$$$$$$$$$@@@@%~ ++==++ + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++==xo + ++==oยท o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ยทx==xx + ++== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ==+~ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยทx== + ox== @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + ++++ *$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++++ + +++x @$$@@%**************%@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++~ ยท$$$* ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + +++~ ~@$@~ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$@+ ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ~+++ + +++o @$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ox++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++~ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + ox== %@@@$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@@$$$$$$$$@@@@$$@$ ==xo + ==+o x%@@@%~ ยท*@@@@@@@@$+ x%@@@@@@@@*ยท ยท+== + **xo ==xx + ==+= ++%%*= %%** **%%+o**++ + ++***%*=%*==++++***%==++***=**++++***%==++==%***++x++++x + ++ ++++++++ ++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_063.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_063.txt new file mode 100644 index 00000000..e3eb943d --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_063.txt @@ -0,0 +1,41 @@ + + xxxxxxxx + ++++==**************==++++ + ++++*****%==++xx oo++==%*==**==++ + ++**=*== ++======xx + ++===*++ ~oooo~ ++====++ + ++==+= ~+%@@@@@@@@@@@@@@@@@$*x +===++ + ++==xx o%@@@@@$$$$$$$$$$$$$$$$$$@@@@$+ +===++ + ++==o~ x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ +=== + xx==+~ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ~+==xo + ++== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+o %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ~+== + == %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + xx++ x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* ++xx + ++++ @$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++o +$$$$$= *$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + +++o @$$$@ @$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o %$$$$$% ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ~+++ + +++o $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++~ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++xx + xx== %@@@$$$$$@@@@$@@@@@$$$$$$$$@@@@$@@@@@$$$$$$$$$@@@@$$ ==xo + ==+o +$@@@@$+ +$@@@@@@@@%~ ~%@@@@@@@@$+ ยทx== + **+o ==xx + **++ +=%%+o ++%%== *%=+=*++ + ++***%==****++++==*%**++++*=**==++++***%++++==*=**++++xx + ++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_064.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_064.txt new file mode 100644 index 00000000..9fd11f4c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_064.txt @@ -0,0 +1,41 @@ + + + xx++++==********====++++ + ++==****%%****++++==**%**=====++++ + ++==***%++ x+*+====++ + xx====== =+====++ + ++====x+ o=%$@@@@@@@@@@@@$%=o xo====++ + ++===+ ~*$@@@@@$$$$$$$$$$$$$$@@@@@$= +===++ + x+==++ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ==== + ===+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+==xx + ox==xยท %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยท+== + ==+ยท @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + ox== @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% ++++ + xx++ +@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + xx++ +@$$$$@= *@$$$$$$$$$$$$$$$$$$$$$$$$= ยท+== + ++++ x@$$@* %@$$$$$$$$$$$$$$$$$$$$$$@% ยทx== + xx++ %$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ +$@@$$$$$@@$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$@% x== + xx++ @@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + ==+ยท %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + +++o @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ xxxx + ox== %@@$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$$@@@@ ++xx + ==+o =$@@@@@@@* *@@@@@@@@@%o x%@@@@@@@@@= ** + **x~ ==++ + **+= +=%% %%=x *+**++ + ++***%++++*=**==++===%==++x+==*=**++++*%**++++++*=**++++ + ++++==++ ++====++ ++++==++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_065.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_065.txt new file mode 100644 index 00000000..b1ee24c5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_065.txt @@ -0,0 +1,41 @@ + + + xx++++==========++++xx + ++++==**=*%%%%******%*%*======++++ + ++==**=**%+o ++*=====++ + ++===*+= ======++ + ====++ ยทx=%$@@@@@@@@@$%=x +x====++ + x+====oo ยท=$@@@@@@@$$$$$$$$$@@@@@@@%+ +===++ + ==== +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +===xx + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==== + ++==xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยท+==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ==xo + ++++ =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ++++ + ==+ยท x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == +%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$* x== + == =@$$$$$@+ ~*@$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$@ o@$$$$$$$$$$$$$$$$$$$$$$ == + == ~@$$$@ +$$$$$$$$$$$$$$$$$$$$$$@ == + == +$@$$$$$@$=++++++++++++++*@@$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + ==x %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + +++ยท =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++~ o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o+++ + +x+x @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ox== %@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@o ++++ + +++o =@@@@@@@@@$x =@@@@@@@@@@* o%@@@@@@@@@%o *= + **xยท ~~ ~~ยท ยท~~ +=++ + **++ **** +=%% ~==++ + ++**=*+xoo++*+**++++**=*+x~~ox=+**==++**=*++ooox++*=**++ + ++======++ ++====++++ ++++====++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_066.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_066.txt new file mode 100644 index 00000000..8b97f549 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_066.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++++ + ++++==*******%%%%***==**====++ + ++=====**%++ x+****====++ + ++====+=++ +x======+x + ++====++ ~x=**%%%%**=+~ +x====++ + ++===+ o*$@@@@@@@@@@@@@@@@@@@@$*o +===++ + ++==++ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@* +===+x + xx===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ++== + ==++ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ++==o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ~+==xo + ==++ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + ox== x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+== + ++== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ==xo + +++x o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$@o ==xx + +++~ o%@$$$$$@@=++++++++++++++=$@$$$$$$$$$$$$$$$$$$$$$ ++++ + +++ยท $$$$@+ @$$$$$$$$$$$$$$$$$$$@ ++++ + ==+ยท $$$$@~ @$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ยท x@@$$$$@*~ ยท+@$$$$$$$$$$$$$$$$$$$$@ xx++ + ==+ยท o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$@ x+xx + +++ยท x@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++ยท =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + +++ยท x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ~+++ + +++o @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+++ + xx+x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + ++++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + == *@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$@@$ o+++ + +++x =@@@@@@@@@@@+ o%@@@@@@@@@@%~ ยท=@@@@@@@@@%ยท ==xx + **xยท ox++o ~x++x~ o+x~ x+== + **++ ==+x x+=+ ++== + ++**=%++ ยทo=+**++===*++ ++*===+=**==oยท ~x*+**++ + x++==**===++ +++=***=+++x ++=***==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_067.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_067.txt new file mode 100644 index 00000000..f8d68716 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_067.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++xx + ++====****************==++++ + ++++=====*=*+x ~o==***=====++ + ++====== ++=+====+x + xx====+= ~x+=====+o~ ++====++ + xx====ox o*$@@@@@@@@@@@@@@@@@@@%+ยท +===++ + ox==== o%@@@@@$$$$$$$$$$$$$$$$$$$@@@@@= +===++ + ==== ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +===xo + ++==o x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ==+x ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ยทx==xo + ++== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ==+o $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + ox== @@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==++ + xx+= ยท*@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$$$ ++++ + xx++ ~*@@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$$$$$$$$$$$$$$@ o+++ + +++x ~@$$$@x +$$$$$$$$$$$$$$$$$$$x ยท+++ + +++x @$$$@ @$$$$$$$$$$$$$$$$$@+ ยท+++ + +++x ~$$$$$$@o x@$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++x =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$$$@+ ยท+++ + +++x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + xx+x @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+++ + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + +++x @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* x== + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + xx++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$@@ ยท+== + +++x ~*@@@@@@@@@@@%o +$@@@@@@@@@@$+ o*@@@@@@@@@+ ++xx + **oยท ~+===+o x====x ~+=+ยท o+== + x+**x++x ++ ++ x+** + ++==*=++ ~o*+**==**+= ++**==**+*x~ ++**=+ + ++=****===++ ++=******=++ +++==**==+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_068.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_068.txt new file mode 100644 index 00000000..829d8f6a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_068.txt @@ -0,0 +1,41 @@ + + + ++++xx++ + ++++====************==++++ + ++==**===%**++xo~~~~xx+=%*%****=+++x + ++======+= *=*===++ + ++====++ ยท~~~ยท =+====xx + ++==++ ยท+%$@@@@@@@@@@@@@@@@$=o +===++ + ++==+x o*@@@@@$$$$$$$$$$$$$$$$$@@@@@$x +===+x + ++==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +===xo + ox==+x =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ยทo==+x + xx==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ++++ @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ==xo + ==+ยท @@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++x+ + == x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + xx== +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$= ยท+== + xx++ ~$$$$$$ +$$$$$$$$$$$$$$$$$$ == + ++++ @* *@$$@o @$$$$$$$$$$$$$$$$@ == + ++++ x$$$$$$ยท =$$$$$$$$$$$$$$$$$@ == + ++++ =$@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$@ == + ++++ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ++++ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + xx++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ox== =$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$@@+ == + +++o oo+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@@*x~o+%@@@@@@@@% ++++ + ** x*%$$$*+ ~=%$$$%=o +*%=~ ยทo** + xx**ยท ยทo**++ + xx******+= =+****=* ++=***=*+x =+**xx + ++==%%%%%%==++ ++==*%%%%%**=+ x+==*%%*==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_069.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_069.txt new file mode 100644 index 00000000..2868c56c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_069.txt @@ -0,0 +1,41 @@ + + + + ++++==**********====++++ + xx++==****%%**++++++++**%**=**==++ + ++**=*=* ~x*=====++ + xx====+= ++====++ + ++===+ ~=%$@@@@@@@@@@@@@@$%+ยท o=*==++ + x+==++ +$@@@@@$$$$$$$$$$$$$$$$@@@@@%o +===xx + ox===+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +===xx + ==++ ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ++== + ++== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ==+x $@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + == +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == +$@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$$ == + == ~$$$$$$+ x$$$$$$$$$$$$$$$$@ == + == %@%ยท ~@$$@% =@$$$$$$$$$$$$$$$ == + == %$$$$$~ $$$$$$$$$$$$$$$$@ == + == ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ == + == ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$@% == + +++o x*+=*@@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@%=+=*@@@@$@@@@o x+++ + ~o** o*$@@@@$*o +%@@@@@%+ x*$%+ ** + ++** **++ + ++**==**== ==****xo =+****++ ++**++ + +x ++==%%%%%%%*==++ x+==*%%%%%%%**=+ +=*%%%**=+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_070.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_070.txt new file mode 100644 index 00000000..67de5408 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_070.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%%%********%*%*****==++ + ++==**=*++ ++*=**==++ + ++**+= =+**++ + xx====ox ~+%$@@@@@@@@@@@@$%=~ xo====xx + xx===+ ยท=@@@@@@$$$$$$$$$$$$$$@@@@@@=ยท +===++ + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ++==o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ==++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ ~$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+ยท x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ยท+== + == x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$@ == + == *@$$$$@+ ยท%$$$$$$$$$$$$$$$ == + == ยท@@@+ %@$$@x $$$$$$$$$$$$$$@~ == + == ยท*o $$$$$* ยท$$$$$$$$$$$$$$@~ == + == =@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$$$$$$$$$$@~ == + == =$@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@~ == + == +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ~@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$@@$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$@@ == + ==+~ %@@$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$@@@@$$@@@% ยท+== + xx== =@@@@@@@@%x x%@@@@@@@@= *@@$+ ==xx + ++=+ +=++ + ++==x+**%* **%%+x +=%%=+ oo==++ + ++==++x+**=%=*==**%***++x+==*%*%====****+++++=*****===++ + ++++++ ++++++++ ++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_071.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_071.txt new file mode 100644 index 00000000..42ed5452 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_071.txt @@ -0,0 +1,41 @@ + + + + +++++++===+++++++x + xx++*****%*%*%%%*%%*%*******++xx + xx++***%== +=*=**==++ + ++**+=+x *=**++ + ====x+ x=%$@@@@@@@@@@$%*xยท xx==== + ==== x%@@@@@@$$$$$$$$$$$$@@@@@@$+ +=== + ==== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + xx==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + xx++ %$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + +++o =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยทx== + == =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$@ == + == x ยท%@$$$$@= o$$$$$$$$$$$$$$$~ == + == @@@$x %@$$@x $$$$$$$$$$$$$@+ == + == @%x $$$$$* $$$$$$$$$$$$$@+ == + =* +$@@$$$$@@%***************@@$$$$$$$$$$$$$@+ == + =* x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$@+ == + == x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท $@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$@@@ ยทx== + xx== =@@@@@@@@@@= o%@@@@@@@@@%~ =@@@%ยท ++xx + ==++ ยทยท ยท~ยท x+== + ==++ x+%% %%+x **=+ +=== + ++**==++===%==xoox==%===++==*%==+xoo++*=**+++=**=**=**++ + +++==+++ +++===++++ ++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_072.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_072.txt new file mode 100644 index 00000000..95a4a2af --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_072.txt @@ -0,0 +1,41 @@ + + + + +++++++++++++x + ++==*****%*%%%%%%%%*****==++ + ++***%=*x~ *=*=**++ + x+**==++ xx=+**++ + ++**++ ~+*%$@@@@@@@@$$%=o +x**=+ + ++==oo ~*@@@@@@@$$$$$$$$$$@@@@@@@%x =*== + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +=== + x+==o $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + ==+o @@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==xx + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xx + xx== $%$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% x+++ + +++x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + ==+ยท =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$@ == + == @x ยท*@$$$$@$o o$@$$$$$$$$$$$$x ==xo + == $@@@$x o@$$$$ *@$$$$$$$$$$@* ==xo + == @@@*o =@$$$$ %$$$$$$$$$$$@* ==xo + == % ยท=@@$$$$@@%===============*@@$$$$$$$$$$$@* ==xo + == ยท=$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$@* ==xo + == % =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ~@@@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$@@+ == + x+++ =%o +$@@@@@@@@@@@+ยท o*@@@@@@@@@@@*o ยท+$@@@* ++++ + =*+~ x=**=xยท o+=*=+o ยทx** + **+x x+ +x ++ ox**xo + ====*===**+*+x x+*=**===*++ *+**==**++++*=== + +x xx===*%%*===++ x +==*%%*=*=++ ++=+ x + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_073.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_073.txt new file mode 100644 index 00000000..d2d6c6d7 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_073.txt @@ -0,0 +1,41 @@ + + + + x+++++++++++ + ++++****%%%%%%%%$%%%****++++ + ++****=*+x *=*=**++ + xx==**++ ++*=**++ + ++**++ x=%$$@@@@@@@$%*+~ ++**++ + ++**oo =$@@@@@@@$$$$$$$$$@@@@@@@%x =*== + ++==oยท o$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* +=== + x+=*oยท %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ==+x $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==xx + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ==+ยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xx + xx== @%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ++++ o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + ==xยท x o*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$@ == + == $$~ o$@$$$$@$o ยทx@$$$$$$$$$$$$= ==xx + == @$@@@*~ *@$$@= $$$$$$$$$$$@% ++xx + == @@@@*o %$$$$* $$$$$$$$$$$@% ++xo + == @= ~*@@$$$$@@*+++++++++++++++%@$$$$$$$$$$$@% ++xx + == @ ยท*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$$@% ++xx + == @+ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == o + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == +@@$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$@% == + ++++ %@$=o~o=$@@@@$$$@@@@@*x~~x*@@@@@$$$@@@@$+o~o=$@@@$~ x+++ + **oยท ~+%$$$%*o x*$$$$%+ยท ยท ** + x+**o~ ~**+x + xx**+==+****== ++*=**=*x+ oo==****++ =+**+x + ++=+ ++==*%%%%%%*== xx+=*%%%%%%**=++ ++==++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_074.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_074.txt new file mode 100644 index 00000000..bb794eea --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_074.txt @@ -0,0 +1,41 @@ + + + + xx+++++x + ++==***%*%%%%%%%%%****+++x + x+==***%++ ~x%*****++ + ++**++ ++*=**xx + xx**++ o+*%$$@@@@@$$%*x~ +x**++ + x+**xx +$@@@@@@@@$$$$$$$@@@@@@@@%x =*++ + x+**o~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* +=++ + o **+~ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ==+x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==xx + x+== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o o+== + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + ox== @@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ++++ $o o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + +++~ ยท@ o%@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$@ == + == %$$ยท x$@$$$$@$x~~~~~~~~~~~~~~~+@$$$$$$$$$$$* =+xo + == @$$@@@*~ *@$$@* $$$$$$$$$$$$ ++xx + == @$@@@*x *$$$$* $$$$$$$$$$$$ ++xo + == @$* =@@$$$$@@=xxxxxxxxxxxxxx+*@$$$$$$$$$$@$ ++xx + == @@ =$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$@$ ++xx + == @$+ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == + == %@$$$$$@@@$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$@@@$$$$$@@ == + +++o +@@@@$**%$@@@$$$$$$$@@@@%**%@@@@$$$$$$$@@@$**%@@@@@* ~+== + ox** ~ ~*@@@@@@@%o +$@@@@@@$= oo ==xx + ++== ==++ + ++**o+ ++%%** ++%%**xx **%%++ +x**++ + +=**++ ++***%*%%%%*%*++ xx==*%*%%%%*%*==++ +=**=+ + xx ++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_075.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_075.txt new file mode 100644 index 00000000..497f7840 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_075.txt @@ -0,0 +1,41 @@ + + + + x+++ + ++==***%%%%%%%%%%***==++ + ++**=*++ ~x%*%*==+x + ++**== ++*===+x + xx**== ~+*%$$@@@@@$$%=xยท ++**++ + xx**+x x%@@@@@@@@$$$$$$$@@@@@@@@%o ==++ + xx**x~ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* +*++ + **x~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + ==++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* *=xx + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x o+== + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + == @@$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ++++ %$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + +++~ @* +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$@ == + ==+ *$@= =@$$$$$@*o~~~~~~~~~~~~~~o*@$$$$$$$$$$* +++x + == $$$@@@$+ @$$$@ @$$$$$$$$$$ ++xx + == @$$@@$*~ @$$$@ ~@$$$$$$$$$$ ++xx + == @$$x o%@$$$$$@%+xxxxxxxxxxxxxx+$@$$$$$$$$$$$ ++xx + == @@= ~*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$$$$ ++xx + == @$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + +++~ *@@@@@@$$@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@@@$$@@@@@$ ยทx== + oo== x* ยท*@@@@@@@@$x +$@@@@@@@@= ~+ ==+x + ++=+ +=++ + ++==o **** =*%%x ++%$++ ==++ + x+==****+x++***%**==**%***++++==**=*====%***=+xx++**==xx + ++++++ +++++++x + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_076.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_076.txt new file mode 100644 index 00000000..517a5a5b --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_076.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%*%***==++ + +=****++ o+*=%*==xx + ++**+= ++*=== + **+= ~+*%$$@@@@@$$%=x~ ++**++ + xx**+x x%@@@@@@@@$$$$$$$@@@@@@@@%x ==++ + ox**x~ *@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* =*++ + =*+~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + ++++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* *=oo + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x o+== + =++o %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == $@@$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+++ + xx++ *@% =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$o ยท=@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$@ยท == + ==+ยท *$$$x ~*@$$$$$@=~~~~~~~~~~~~~~~o%@$$$$$$$$$% ++xx + == $$$$@@@%o @$$$@ x@$$$$$$$$@ ++xx + == $$$$@@$=ยท @$$$@ +@$$$$$$$$@ ++xx + == $$$$~ x%@$$$$$@*+xxxxxxxxxxxxxx+$@$$$$$$$$$@ ++xx + == $$@o o%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$$@ ++xx + == $$$% o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท $@@$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@@ยท == + x+== ยท%@$x =@@@@@@@@@@= o$@@@@@@@@@$o =o ++xx + ===+ ยท~~ยท ~o~ x+== + ===+ ++%% %%+x **=+ ++== + ++**=**===++**=*=+~ยท~o+=%=**++===*==o~ยทยทx+%=**++++****++ + ++ x++=====++xx ++======++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_077.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_077.txt new file mode 100644 index 00000000..4ead3508 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_077.txt @@ -0,0 +1,41 @@ + + + + + +++==**%%%%%%%%*%%**=+++ + ++****++ ยทx**%*==xx + ++**+= +x*=== + ==+= ~+*%$$@@@@@$$%*+~ +x**++ + xx**+x x%@@@@@@@@$$$$$$$@@@@@@@@%x ==++ + **x~ *@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@* +*++ + ==+~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + ++++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* **oo + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ o+== + +++o %@$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == $@@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+++ + ++++ *@$~ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$@ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$$@~ == + ==+ยท =$$$% x@@$$$$@$x~~~~~~~~~~~~~~~=@$$$$$$$$$% ++xo + == $@$$@@@$=ยท %$$$@+ @$$$$$$$$@ ++xx + == $@$$@@@*o %$$$@= $$$$$$$$$@ ++xx + == $@$$= ยท=@@$$$$@@=xxxxxxxxxxxxxx+*@$$$$$$$$$@ ++xx + == $$$$ =$@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$@ ++xx + == $$$$x =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท @@$$$@@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@@@x == + x+++ x$@@%o +@@@@@@@@@@@+ o%@@@@@@@@@@%o =+ ++++ + ==+x ox+xo ~x++xยท ~+** + **++ == ==++ ++== ++** + ++**==*=**==**=*++ ยทo*+**++===*++ ++*===++****=+ + ++++ ++++****==++ +==***==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_078.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_078.txt new file mode 100644 index 00000000..4a7f7ab7 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_078.txt @@ -0,0 +1,41 @@ + + + + + +++===*%*%%%%%%%%%%*=+=+ + ++**=*++ ยทo**%*==xx + ++**+= +x*=== + ==== o+*%$@@@@@@@$%*+o +x**++ + xx**+x +%@@@@@@@$$$$$$$$$@@@@@@@%x ==++ + **x~ ยท%@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@%~ +=++ + =*+~ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ++++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% **oo + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ~+== + +++o %@$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + == $@$@$%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ o+++ + xx++ *@$% +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$@= +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$@o ==xo + ==+ยท =$$$@= =@$$$$$@=~ยทยทยทยทยทยทยทยทยทยทยทยทยทยท~*@$$$$$$$$$ ++xo + == %@$$$@@@$+ @$$$@ ยท@$$$$$$$@ ++xx + == $@$$$@@$= @$$$@ o@$$$$$$$@ ++xx + == $@$$$~ x%@$$$$$@$+++++++++++++++=$@$$$$$$$$@ ++xx + == $@$@+ o%@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$$@ ++xx + == $$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + ==xยท ~@@$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@@= == + xx++ =@@@@$x ยทx$@@@@@@@@@@@=~ o*@@@@@@@@@@@%x ยท+* x+++ + **x~ x=***+~ ~+***=o ยทo** + **+x o++ ++ ++ oo**xo + ====++x+*===**==+o ยทx*=**====++ =+**==*===== + ++=+++ ++===*%%*==+++ ++==*%%**=++ xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_079.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_079.txt new file mode 100644 index 00000000..98c847b4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_079.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%$%%**=++ + ++****+o ==%*==xx + ++**+= ox*=== + =*== o=%$$@@@@@@@$$%=o xx**++ + xx**+x +$@@@@@@@$$$$$$$$$@@@@@@@$+ =*++ + **o~ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@%o +=++ + **x~ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ++=+ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% **xx + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ~+== + +++o %@$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + == $@$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ *@$@ยท ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ == + +++o @$$@ ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$@o ==xo + ==+ยท =$$$$$~ o$@$$$$@%~ x@$$$$$$$$$ ++xx + == %@$$$$@@@%o %@$$@+ $$$$$$$$@ ++xx + == $@$$$@@$=~ %$$$@* $$$$$$$$@ ++++ + == $@$$$= o*@@$$$$@@*++++++++++++++=%@$$$$$$$$@ ++xx + == $@$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$$$@ ++xx + == $@$$$+ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == o@@$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@@* == + ++++ *@@@@@*xยท ~x%@@@@@@@@@@@$+~ o=@@@@@@@@@@@@*oยท ~+* x+++ + **xยท o x*%%%*+ยท ~=%%%%=o ยทo** + ox**xo oo xx xx oo**xx + ===*+x *+****=*+x =+****==++ ++==*****=== + ++====++ ++==**%%%**=++ ++=**%%%*==+x ++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_080.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_080.txt new file mode 100644 index 00000000..b8e4d013 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_080.txt @@ -0,0 +1,41 @@ + + + + ++ + +==**%*%%$%%%%%%%%%*==++ + +=**=*xยท +=%***++ + ++**++ *=**+x + **++ ~+*$$@@@@@@@@@$$*+~ ox**++ + xx**oo ~=@@@@@@@$$$$$$$$$$$@@@@@@@=~ +=++ + xx**oยท x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +==+ + **xยท %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + +++x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==xx + xx== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ยท+== + =++~ $@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + == @@$@@%*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ %$$$= o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++o @$$@x x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$@x ==xo + ==+ยท =$$$$@* +@$$$$$@o =@$$$$$$$$ ++xx + == %@$$$$@@@@* @$$$@ o@$$$$$$@ ++xx + == $@$$$@@@*~ o@$$$@ =$$$$$$$@ ++++ + == $@$$$% ยท=@@$$$$$@$*==============%@@$$$$$$$@ ++xx + == $@$$@~ ยท=$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$@ ++xx + == $@$$$$~ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == +@@$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@@% == + ++++ %@@@@@@*x~~x*@@@@@@$@@@@@$+o~o+$@@@@@$@@@@@@*x~~x*~ o+++ + **oยท ยท+x o*%$$$%+ยท ยท+%$$$%*o ** + xx**o~ ~**++ + xx**+= x+=****=++ ~x==****+= =+****==**++ + ++==**==xx xx==**%%%%%*==++ ++**%%%%%*==++ ++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_081.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_081.txt new file mode 100644 index 00000000..aed9c8aa --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_081.txt @@ -0,0 +1,41 @@ + + + + ++++++++ + ++==*%*%%%****%%$%%%**++xx + xx==**=* ++%***++ + ++**++ ==**++ + xx**++ o=%$@@@@@@@@@@@$%=o ==++ + xx**oo o%@@@@@@$$$$$$$$$$$$$@@@@@@%o +=== + x+** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++== + **oยท $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท +=++ + +++x $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@ยท ==xx + xx== *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ยทx== + ==+~ @@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + == @$$@@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + xx++ %$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++~ @$$@% =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$x ==xo + ==xยท *$$$$@$o ~$$$$$$@x o@$$$$$$$$ ++xx + == %@$$$$$@@@$o $$$$@ @$$$$$$@ ++xx + == $@$$$$@@*~ $$$$@+ o@$$$$$$@ ++++ + == $@$$$$ ยท=@@$$$$$@@%**************%@@$$$$$$$@ ++xx + == $@$$@% =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$$@ ++xx + == $@$$$$+ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == +@@$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@@$ == + ++++ $@@@@@@$=xox=@@@@@$$$@@@@@*xoo+%@@@@@$$$@@@@$=oox*o o+++ + **xยท ~==ยท ~=%$$$$*x +%$$$$%=ยท ** + x+**~ยท **+x + xx**+= ==****++ ==****+= =+****==**++ + ++==**==++ ++**%%%%%*==++ +=***%%%%%**++ ++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_082.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_082.txt new file mode 100644 index 00000000..ba491c1f --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_082.txt @@ -0,0 +1,41 @@ + + + + xx++++++++++ + ++==***%%%*=++++==**%*%*==++ + ++**=*++ ยท *=**++ + ===* ==**++ + ++**x+ o=%$@@@@@@@@@@@@@$%=~ ==== + ++** +$@@@@@$$$$$$$$$$$$$$$@@@@@$+ ++*= + xx** *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* x+== + o ** o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++++ + ==+o ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==+x + x+== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยทx== + ==xยท @@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$@%xo=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + ++++ $$$$@ ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++~ ยท@$$$@ ยท=@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$+ ==xo + ==+ยท *$$$$$@*~ x$$$$$$+ =$$$$$$$$ ++xx + == $@$$$$$@@@@* +@$$@* %@$$$$$@ ++xx + == $@$$$$@@=ยท %$$$$$ ยท$$$$$$$@ ++xx + == $@$$$$~ ~*@@@$$$$$@@$$$$$$$$$$$$$$$@@$$$$$$$@ ++xx + == $@$$$@ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$ == + +++x $@@@@@@@%+xx=%@@@@$$$$@@@@@*xox*@@@@@$$$$@@@@%+xx=x o+++ + ** o*%+ +%$@@$%=~ o*$$@@$%+ ** o + x+**~ **+x + xx**+= =+****++ ++****== x+****==**++ + ++==*%**++ ++**%%%%$%**=+ ++==*%%%%*%*==++ ++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_083.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_083.txt new file mode 100644 index 00000000..d730b375 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_083.txt @@ -0,0 +1,41 @@ + + + + ++++++++++=++x + ++==*%*%**++xxxxxx==%*%**=++ + +=**=*+~ *=**=+ + x+**== ++**++ + ++**xo ยท+%$@@@@@@@@@@@@@@@$%x ==== + ++*= ~*@@@@@$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ++== ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ ~+** + xx** +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= x+== + ==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ +=++ + x+++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ** + ==xยท @$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++++ + ox== @$$$@*~ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ยท+== + xx++ @$$$@~ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + +++~ o$$$$$x x%@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$= ==xo + ==x %@$$$$@$+ %$$$$$+ ~$$$$$$$$ ++xx + == $@$$$$$$@@@$~ @$$@$ +@$$$$$@ ++xx + == $@$$$$@@= %$$$$$x ยท$$$$$$$@ ++xx + == $@$$$@x o*@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$@ ++xx + == $$$$$@ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$@=ยท ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == $$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$ == + +++x ยท$@@@@@@@$=xx+%@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx=x o+++ + ** o*%=~ x%$@@@$%x ~=%$@@$%=~ **xo + ++** **++ + x+**+= =+****== xx****== ==**==**+x + ++=**%**=+ ++***%%%%*%*++xx ++==*%%%%*%*==++ xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_084.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_084.txt new file mode 100644 index 00000000..1fd3352b --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_084.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + +=***%**+x~ยท oo==%%%*++++ + +=**== x+*=**++ + ++**++ ~ox+++xo~ ===+ + ++== +%@@@@@@@@@@@@@@@@@@$%x ++**xx + ===+ =@@@@@$$$$$$$$$$$$$$$$$$$@@@@@+ oo**xx + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ยทo** + x+== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ~+== + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ++++ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ** + == x@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ox== ~@$$$@= o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยทx== + +++x @$$$@+ o%@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$@ == + +++ยท +$$$$$% x%@@$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$= ==xo + == $@$$$$@@$+ x@$$$$x =$$$$$$$ ++xx + == $$$$$$$$@@@$o @$$$@ @$$$$$@ ++xx + == $$$$$$@$x ยท$$$$$$$ยท o$$$$$$$@ ++xx + == $$$$$@= +$@@@$$$$$@@@@@@@@@@@@@@@@@@@$$$$$$$@ ++xx + == $$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$@@=x+$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++xx + == @$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* =+xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$ == + +++x ยท@@@@@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$$@@@@%+x+o o+++ + ** o*$%x ~=%$@@$%=~ +%$@@@$%x **xo + ++** **++ + ++**+= xx****** ==****xo ==******+x + ++***%%*++xx ++==*%%%%*%**=++ ++==*%*%%%%***++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_085.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_085.txt new file mode 100644 index 00000000..4e074e65 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_085.txt @@ -0,0 +1,41 @@ + + + + ++++****%%%%***=++++ + ++==***%+x ox**%*==++ + xx===*+x *=**++ + ++**x+ ~x+=*****=+oยท ==*= + ===+ x%$@@@@@@@@@@@@@@@@@@@$*o +x**+x + ==+x o%@@@@$$$$$$$$$$$$$$$$$$$$$@@@@%~ **xx + ===+ ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% **xx + ++++ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ยทx** + ** @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + +++x +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x *= + =* *@$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + xx++ +@$$$$x +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + +++o @$$$@* +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ == + ==+ยท =$$$$$$ยท +$@$$$$$@@*==============*@@$$$$$$= ==xo + == $@$$$$$@@*x $$$$@x o@$$$$@$ ++xx + == $$$$$$$$@@@%~ $$$$@ @$$$$$$ ++xx + == @$$$$$@%ยท o$$$$$$@+ +@$$$$$$$ ++xx + == @$$$$@* ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$$ ++xx + == @$$$$$% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$@@%*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == =@$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@$ == + +++x ยท$@@@@@@@@$=xx+%@@@@$$$$@@@@@*+xx*@@@@@$$$$@@@@%=xxo o+++ + ** o*$%+ +%$@@$$*o o*$$@@$%+ ** o + ++** **++ + x+**+= ~x=*****xo ==***=++ =+******+x + ++==*%%*==xx x+==*%*%%%%*==++ ==**%$%%%%**=+ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_086.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_086.txt new file mode 100644 index 00000000..d1ca5713 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_086.txt @@ -0,0 +1,41 @@ + + + + x+++==*%%$%%%%%%%**===++ + +=****++ ox****== + ++**+= ++**++ + ==== ~+*%$$@@@@@$%*=xยท ++**+x + xx**xx +%@@@@@@@@$$$$$$$@@@@@@@@*o **++ + ox**o~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + **+~ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + ++=x %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ** + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+++ + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + == @@$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + +x++ $$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + +++~ ยท@$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == %$$$$$@* +@@$$$$@%x~~~~~~~~~~~~~~o=@$$$$$$= ==+x + == @$$$$$$@@@$+ $$$$@o @$$$$@$ ++xx + == @$$$$$$@@@*o $$$$@x @$$$$@$ ++xx + == @$$$$$@= ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@$ ++xx + == @$$$$@$ ยท=@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@$ ++xx + == @$$$$$$~ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xx + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == =@$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@% == + +++x $@@@@@@@@@=xox=@@@@@$$$@@@@@%+oo+%@@@@@$$$@@@@$=xo~ o+++ + ** o*$%=~ o*%$$$$%x +%$$$$%=~ ** o + ++**~ **+x + x+**+= *=***=++ =+****+= =+******++ + ++==*%%*==++ ==**%%%%%***++ ++***%%%%%**++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_087.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_087.txt new file mode 100644 index 00000000..cfec2a69 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_087.txt @@ -0,0 +1,41 @@ + + + ++++ + ++=**%*%*%******%%%*==++ + xx==*%== +=*=**++ + ++*=x+ ==**xx + xx**++ o=%$@@@@@@@@@@@%*+~ xx**++ + ++**~ o%@@@@@@$$$$$$$$$$$$@@@@@@$=ยท =*++ + x+** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=++ + ** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + x+== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ o+== + ==xยท @@$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + ox== @$$$$@$=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ x+++ + +++x @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% == + ==+ยท +$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$$$o ยท%@$$$$@= o$$$$$$$= ==xo + == @$$$$$$$@@@$o $@$$@o @$$$$@% ++xx + == @$$$$$$@@%x $$$$$= @$$$$@% ++xx + == @$$$$$$~ +$@$$$$$@@%**************%@@$$$$$@% ++xx + == @$$$$$$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@% ++xo + == @$$$$$$= x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == =@@$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@% == + +++x $@@@@@@@@@*xoo+$@@@@@$$@@@@@%+oox*@@@@@$$$@@@@@=xoยท o+++ + **oยท ~=%%=o +%$$$$%+ o*%$$$%=~ ** o + x+**~ยท ~**+x + ++**+= ==****++ =+****+= ++******++ + ++==*%%**=++ ++***%%%%***++ ++***%%%%%**++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_088.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_088.txt new file mode 100644 index 00000000..237b9c93 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_088.txt @@ -0,0 +1,41 @@ + + + ++++====++++++ + ++==*%**==++++++++==**%*==++ + ++**=* ==**++ + ++**+= ยท ++**++ + ++== ~=%@@@@@@@@@@@@@@@@$*x ==== + ++=+ x$@@@@@$$$$$$$$$$$$$$$$@@@@@$= ++== + ++=+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* x+== + x+== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ++++ o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ยท+== + =* =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + xx++ +@$$$$@x ยท+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + +++~ @$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$% == + == $$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == @$$$$$$@%o x$$$$$$ =$$$$$$+ == o + == $$$$$$$$@@@@= %@$$@~ @$$$$@= ==xx + == @$$$$$$@%o o$$$$$$ =$$$$$@= ==xo + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@= ==xo + == @$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$@x +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@* == + ++++ %@@@@@@@@@*oยทยทo=@@@@@@@@@@@@%x~ยท~x%@@@@@@@@@@@@=o~ x+++ + **xยท +*%=o ~=%%$%*x x*%$%%=~ ยทo** + x+**o~ o oo**++ + ++**+=xo =+****== *+****==+x ++==****++ + ++==******++ ++****%%%***++ ++==**%%%***==+x + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_089.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_089.txt new file mode 100644 index 00000000..b3622446 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_089.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==++ ox==%***== x + ==**++ ++*===xx + ++**++ ยทoxx++xxo~ +x**=+ + ===+ ~=$@@@@@@@@@@@@@@@@@@$*o ++** + ==+x ~%@@@@@$$$$$$$$$$$$$$$$$$$@@@@$x x+** + ==+x ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= +=++ + ox=* ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + +++o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + == $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + x+++ $@$$$@% ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + ==+ยท x@$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$$ == + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@=ยท $$$$$* o$$$$$$o == + == @$$$$$$$@@@@+ $$$$@ยท @$$$$@x == + =* @$$$$$$@+ *@$$$$$o %$$$$$@x == + =* @$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@x == + == @$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + == $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@+ == + ++++ *@@@@@@@@@=o ยทx%@@@@@@@@@@@%xยท ~=@@@@@@@@@@@@=~ ++++ + **xยท o=*+~ x=%%%=x ~+*%%*+~ ยทx** + x+**xo xo ++ ++oo**+x + ++**=*xx *=****==+x *+****==++ ++==****++ + ++==******++ ++==********++xx ++==********==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_090.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_090.txt new file mode 100644 index 00000000..e781e1e3 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_090.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==oยท ยทx==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + xx**xx o*@@@@@@@@@@@@@@@@@@@@@@*x xx**+x + xx**o~ ยท%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~o**xx + **xยท *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ยทo** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++++ + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + xx== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ =++x + ++xo @$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$$+ ~*@$$$$$@$=+++++++++++++++%@$$$$$@ == + == $$$$$$$@@@=~ $$$$$x @$$$$$~ == + == @$$$$$$@@@@= $$$$@~ @$$$$@~ == + == @$$$$$@% +@$$$$$@%~ยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@~ == + == @$$$$@$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@~ == + == @$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@o == + ++++ +@@@@@@@@@+ยท +$@@@@@@@@@@%o ~*@@@@@@@@@@@+ ++++ + **+~ x+o o+=+x~ ~x+=+o ~x** + xx**+x == == ++++**+x + xx**==+= x+==*****=+* ~x*=******=*x~ =+==**==xx + ++==******++ ++==********+++x xx++********==+x + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_091.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_091.txt new file mode 100644 index 00000000..425fbf02 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_091.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + ++**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%oยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@ == + == $$$$$$$@@@$= $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@$*o $$$$@x @$$$$@~ == + == ยท@$$$$$$+ ~*@$$$$$@$=xxxxxxxxxxxxxx+%@$$$$$@~ == + == ยท@$$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@~ == + == ยท@$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@@ x== + ++++ o$@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+o ยทo~ ~oo~ ~oxoยท o+== + xx**+x == == ==++**+x + x+==**+*x~ ++*=**==**+*+x x+*=**==**=*++ ~x*=****==xx + x+++****==++ ++++********++++ ++==******=++x + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_092.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_092.txt new file mode 100644 index 00000000..04079903 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_092.txt @@ -0,0 +1,41 @@ + + xx++++xx + ===*%%%%*%****%*%%%%*=== + ++**=%++ ++%=**++ + ===* **== + x+**+x ~+*$@@@@@@@@@@@@$*+~ x+**+x + ++== =$@@@@@$$$$$$$$$$$$$$@@@@@$= ==++ + ++*= ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ =*++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==+x + =*o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= o*= + ++++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ++++ + == =@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + x+++ =@$$$$@$**@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++xx + +++ยท ยท@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ยท+++ + == @$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$@%~ ~$$$$$$@= x@$$$$$$ == + == ยท@$$$$$$$@@@%~ $$$$@ยท @$$$$@ยท == + == ยท@$$$$$$@@%x $$$$$+ ยท@$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@%***************$@$$$$$@ยท == + == ยท@$$$$@% x$@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@= +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$@@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@$ ยท+== + ++++ =@@@@@@@%o ~%@@@@@@@@@+ +@@@@@@@@@%~ ++++ + ===+ +=== + x+=*=+ ++%%++ %%=+ =*====xx + ==**=*==++*=*=**==**=*=*++++=**=**===*=**%++++==%***==++xx + ++++==**==++ ++==****==++++ ++++****==++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_093.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_093.txt new file mode 100644 index 00000000..06e4f4db --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_093.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x+==*%*%*%========%*%*%*==+x + ++**=*+x x+*=**++ + xx**=* ==**xx + ++**o x*$@@@@@@@@@@@@@@$*x o**++ + ++=+ o*@@@@@$$$$$$$$$$$$$$$$@@@@@*o +=++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + =* *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* *= + ++++ *@$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+ยท o@$$$$@* o*@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ยท+== + == @$$$$@* =$@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$% o*@@$$$$@@*++==========++=$@$$$$$$ == + == ยท@$$$$$@@$+ยท $$$$$x $$$$$@ยท == + == ยท@$$$$$$@@@*o %$$$@~ @$$$$@ยท == + == ยท@$$$$$$~ o$@$$$$@*~ ยท+@$$$$$@ยท == + == ยท@$$$$@= +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o o*@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$$@@ *= + ==+~ *@@@$$$$$$@@@@%%$@@@@$$$$$$$@@@@$%%@@@@$$$$$$$$@@@@* ~+== + ++== o%@@@@@@= x$@@@@@@@%x ~*@@@@@@@@= ==++ + ===+ +=== + ox====o+ ++%%== x+%%** *=====xo + ++**=**%***=*=**++==***%****%**=**++==**=*=***%**=**==++ + x+++====++++ ++++======++xx ++++======++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_094.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_094.txt new file mode 100644 index 00000000..bd9522ac --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_094.txt @@ -0,0 +1,41 @@ + + xx++++====++++xx + ++==*%**==++oooo++==**%*==++ + ++**=* *=**++ + ++**+= ยทยทยทยท =+**++ + ++== x=$@@@@@@@@@@@@@@@@$=x ==++ + ==++ =$@@@@$$$$$$$$$$$$$$$$$$@@@@$= ++== + ===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท +=++ + ox** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** o + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$ == + == ยท@$$$$@= ยท*@$$$$@x =@$$$$@ยท == + == ยท@$$$@ ~@$$@ @$$$@ยท == + == ยท@$$$$x +$$$@ x$$$$@ยท == + == ยท@$$$$@@*==============*@@$$$$@$=++++==========*@@$$$$@ยท == + == ยท@$$$$$$@@@@@@@@@@@@@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@% == + ==+o ~@@@@$$$$@@@@*+x+*@@@@$$$$$@@@@%=xx=$@@@@$$$$@@@@@*~ o+== + xx== x*$@@$*o o*$@@@@%+ ยท=%@@@@$*o ==xx + ==== ~ยท++== + ====+= =+*==*++ ++****++ ++=*==== + ++*****%**%***==++++**=***%%%*****++++***%%%%%***=**++++ + ++++++++++ ++++++++++++ ++++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_095.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_095.txt new file mode 100644 index 00000000..b566afe9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_095.txt @@ -0,0 +1,41 @@ + + ++============++ + ++**%%=*++o~ยทยทยทยท~o++*=%%**++ + +=**== ==**=+ + ++**++ ยท~~oo~~ยท ++**++ + ===+ +%@@@@@@@@@@@@@@@@@@%+ +=== + ===+ ยท*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท ++== + ===x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x=== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==xยท =@$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$@= ยทx== + == @$$$$$% +%@@@@@$$$$$$$$$$% ยท+%@@@@@$$$$$$$$$@ == + == $$$$$@o +%@@@$$$$$$@ +%@@@$$$$$$$ == + == ยท@$$$$$$~ o$$$$$$$$ o$$$$$$@ยท == + == ยท@$$$$$$@@@%~ x@$$$$$@@@@*ยท +@$$$$@ยท == + == ยท@$$$$$@+ $$$$$$$$x $$$$$$@ยท == + == ยท@$$$$@o ยท+$@@@$$$$$@ ~+$@@@$$$$$@ยท == + == ยท@$$$$$* ~=$@@@@@$$$$$$$$$= ~=$@@@@@$$$$$$$$@ยท == + == ยท@$$$$$@@$%$@@@@@$$$$$$$$$$$$$$@@$%$@@@@$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@+ == + ==+x %@@@@@@@@@@%xยท ยทx*@@@@@@@@@@@@=~ ~+$@@@@@@@@@@@%x x+== + ++==oยท x*%%=x o=%%%*+~ ~+*%%%=x ยทx==++ + ====oo ox xo==== + ====+=+x ++======+= ======+= ~ ++*===== + ++==*******=**==++++************==++++==*****%******++++ + ++++++++++ +++++++++x ++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_096.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_096.txt new file mode 100644 index 00000000..aff846aa --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_096.txt @@ -0,0 +1,41 @@ + + ++====****====++ + +=***%==+o o+==%***=+ + ===*++ ++*=== + ++**x+ ยทooxxxxooยท +x**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$@@%%@@@@$$$$$$$$$$$$$$$@@$%%@@@@$$$$$$$$$$$$$@ x+++ + ==+ =@$$$$$ x%@@@@$$$$$$$$$$$$ x$@@@$$$$$$$$$$@= +== + == @$$$$@$ +$@@@$$$$$$$@% +$@@@$$$$$$$@ == + == $$$$$$@% =@$$$$$$$@* *@$$$$$$$ == + == ยท@$$$$$$@@@$= $$$$$$$$@@@$+ $$$$$$@ยท == + == ยท@$$$$$$@@@$+ $$$$$$$$@@@$+ $$$$$$@ยท == + == ยท@$$$$$@% =@$$$$$$$@* *@$$$$$$@ยท == + == ยท@$$$$$% +$@@@$$$$$$$@% +$@@@$$$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$$$$$$$$ x%@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$@$%%@@@@$$$$$$$$$$$$$$$@@$%%@@@@$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@@ ยทx== + ++++ o$@@@@@@@@$+ ยท*@@@@@@@@@@*ยท x$@@@@@@@@@$x ++++ + xx==+x ยทoo~ ~oo~ ~ooo~ o+==+x + ++==+x ===+ +=** x++==++ + ++====+=+x x+*+*=====+===o~ ~o=+*+=====*==+x ++*=====++ + ++++==********==++++==**********==++++++=*********==++++ + ++++++ xxxx++xxxx ++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_097.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_097.txt new file mode 100644 index 00000000..316e4929 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_097.txt @@ -0,0 +1,41 @@ + + ++====****====++ + xx+=***%==x~ ~x==%***=+xx + x+===*++ ++*===+x + ++**+x ~ox++++xo~ x+**++ + ==++ x*$@@@@@@@@@@@@@@@@@@$*x ++== + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + x+== @@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ ==+x + +++x @$$$$@@=xx%@@@@$$$$$$$$$$$$$$@@=x+%@@@$$$$$$$$$$$$$@ x+++ + ==x *$$$$$$ x$@@@$$$$$$$$$$$% +$@@@$$$$$$$$$$* x== + == @$$$$$$ =$@@@$$$$$$$% =$@@@$$$$$$@ == + == $$$$$$@@+ ~$$$$$$$$@@+ ~$$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@+ $$$$$$@~ == + == ยท@$$$$$$@@$+ $$$$$$$$@@$+ $$$$$$@ยท == + == ยท@$$$$$$x +$@$$$$$$$$o =$@$$$$$$@ยท == + == ยท@$$$$@$ x$@@@$$$$$$$$@% x$@@@$$$$$$$$@ยท == + == ยท@$$$$$$o ~*@@@@$$$$$$$$$$$$$~ o%@@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==xยท $@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@$ ยทx== + ++++ =@@@@@@@@*~ o%@@@@@@@@$+ =@@@@@@@@@*ยท ++++ + ox==++ ++==xx + ++===+ **%* =*%% +++===++ + ++=====%+=++==*==========*==++==***========%+=++++*=*+====++ + xx++==******==++xxx+++==******==+++x+x++==******====++ + xx++xx xx++xx xx++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_098.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_098.txt new file mode 100644 index 00000000..7eb1d4ac --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_098.txt @@ -0,0 +1,41 @@ + + xx++==********==++xx + x+==*%**=+~ยท ยท~+=**%*==+x + x+=*=*+x x+*=*=+x + ++**xo ยทox+====+xoยท ox**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+o x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+** + ==+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+== + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@@+ยท~=@@@@$$$$$$$$$$$$$$@@xยท~=@@@@$$$$$$$$$$$$@ x+++ + == *$$$$$$ ยท=@@@@$$$$$$$$$@% ~*@@@@$$$$$$$$$* == + == @$$$$$$ ~*@@@$$$$$$$% ~*@@@$$$$$$@ == + == $$$$$$$@*~ =$$$$$$$@@*ยท =$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@+ $$$$$$@~ == + == ยท@$$$$$$@$x o$$$$$$$@@$x x$$$$$$@ยท == + == ยท@$$$$$$ +$@@$$$$$$$$ +$@@$$$$$$@ยท == + == ยท@$$$$@$ x$@@@@$$$$$$$$@% x$@@@@$$$$$$$$@ยท == + == ยท@$$$$$@$~ x%@@@@$$$$$$$$$$$$$@%ยท x%@@@@$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == $@$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$@@@@%**%@@@@$$$$$$@@@@$***%@@@@$$$$$$@@@@+ o+== + ++== ยท=$@@@@@%x o*@@@@@@$= =$@@@@@@*x ==++ + ===+ +=== + ++====x+ x+****+x ****++ =+====++ + ++=====**%****%==========********==========**%****%*======++ + ++++======++++ ++++========++ ++++========++xx + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_099.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_099.txt new file mode 100644 index 00000000..624e9eea --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_099.txt @@ -0,0 +1,41 @@ + + +++===********===+++ + ++==*%**++~ยท ยท~++**%*==+x + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$@$o +$@@@$$$$$$$$$$$$$$@$o +$@@@$$$$$$$$$$$$@ x+++ + == *$$$$$$ +$@@@$$$$$$$$$@% +$@@@$$$$$$$$$* == + == @$$$$$$ +$@@$$$$$$$$ =$@@$$$$$$@ == + == $$$$$$$@%o x$$$$$$$@@%o +$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@ยท == + == ยท@$$$$$$@*~ +$$$$$$$@@*~ =$$$$$$@ยท == + == ยท@$$$$$$ ยท=$@@$$$$$$$% ~*@@@$$$$$$@ยท == + == ยท@$$$$$$ =@@@@$$$$$$$$$@% ยท=@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@xยท~=$@@@$$$$$$$$$$$$$$@$xยท~=@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@@% == + ==+x ~$@@@@$$$@@@@$=xx+%@@@@$$$$@@@@@*+xx=$@@@@$$$$@@@@%~ x+== + ++== o*$@@$%=ยท +*$@@@$*x ~=%@@@$%+ ==++ + ==== ~ยท++== + xx====+= ++**=*+= x+====+= x=+====xx + ++====*%%%***=====++====%%%%%**=====++====*%*%%*%*====++ + xx++++===+++++ xx++++====++++ xx++++====++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_100.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_100.txt new file mode 100644 index 00000000..c66e0879 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_100.txt @@ -0,0 +1,41 @@ + + +++===********===+++ + ++==*%**++~ยท ยท~++**%*==+x + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x==++ + **++ +%@@@@@@@@@@@@@@@@@@@@%+ ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + ==+~ x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$@ ==xo + +++x @$$$$$%ยท o*@@@@$$$$$$$$$$$$$@% o%@@@@$$$$$$$$$$$@ x+++ + == *$$$$@$ o%@@@@$$$$$$$$@% x%@@@@$$$$$$$$* == + == @$$$$$$ x%@@$$$$$$$$ x%@@$$$$$$@ == + == $$$$$$$@$+ ~$$$$$$$@@$x o$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@~ == + == ยท@$$$$$@@= *$$$$$$$@@+ *$$$$$$@ยท == + == ยท@$$$$$$ o%@@@$$$$$$$% o%@@@$$$$$$@ยท == + == ยท@$$$$$$ o*@@@@$$$$$$$$$$% o%@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@=ox*@@@@$$$$$$$$$$$$$$@@=ox*@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@=~ x%@@@@@@@@@@@%x ~=@@@@@@@@@@@@= ++== + xx==xยท o=***+~ x=***=x ~+****+~ ยทx==++ + ====xo +o xx xx==== + ======+x o=+=====*+x =+=*====++ ++====== + ++==***%******==++++==***%********++++==************==++ + ++++++++++ ++++++++++ ++++++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_101.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_101.txt new file mode 100644 index 00000000..63dc3de2 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_101.txt @@ -0,0 +1,41 @@ + + xx+===********===+xx + ++==*%**++~ยท ยท~++**%*==+x + xx**=*+x x+*=**xx + ++*=xo ยทox+====+xoยท ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$% ~*@@@@$$$$$$$$$$$$$@* o*@@@@$$$$$$$$$$$@ x+++ + == *$$$$@% o*@@@@$$$$$$$$@% o%@@@@$$$$$$$$* == + == @$$$$$$ o%@@$$$$$$$$ o%@@$$$$$$@ == + == $$$$$$$@$+ ยท$$$$$$$$@$+ ~$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@ยท == + == ยท@$$$$$@@+ *$$$$$$$@@+ %$$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$$% x%@@@$$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@=xx%@@@@$$$$$$$$$$$$$$@@=x+%@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@o == + ++++ +@@@@@@@@@@$+ ยท=@@@@@@@@@@@*~ o%@@@@@@@@@@$x ++++ + ox==+~ x+=+o x+=+xยท o+==+o ~+==+x + ++==+x ++ == ++==++ + ++====++ x+*+==**==++ ox=+==***=+* =+==**++ + ++==**********==++xx++**********==++++++************++++ + ++++++xx ++++++xx xx++++xx + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_102.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_102.txt new file mode 100644 index 00000000..a53995be --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_102.txt @@ -0,0 +1,41 @@ + + xx+===********===+xx + +++=****+=oยท ยทo==****=++x + x+=*=*+x x+*=*=+x + ++**+x ~x++==++x~ x+**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **xx x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x xx** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$@* ~*@@@@$$$$$$$$$$$@ x+++ + == *$$$$@$ ~*@@@@$$$$$$$$@% o*@@@@$$$$$$$$* == + == @$$$$$$ o%@@$$$$$$$$ o%@@$$$$$$@ == + == $$$$$$$@$=ยท ยท$$$$$$$$@$+ ~$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@~ == + == ยท@$$$$$@@+ *$$$$$$$@@+ %$$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$$% x$@@@$$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@=x+%@@@@$$$$$$$$$$$$$$@@=x+%@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@ ยทx== + ++++ ~%@@@@@@@@@%~ o%@@@@@@@@@$+ =@@@@@@@@@@%~ ++++ + ox==+x oo~ ~ooยท ~oo~ x+==xo + x+==++ ++** %% ++==++ + ++=*==+*xx ~x++*=**=*===*+x ++*=**==**+*++ x+*===*=++ + ++==********++xx ++==********==++ ++==********==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_103.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_103.txt new file mode 100644 index 00000000..04ce9a00 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_103.txt @@ -0,0 +1,41 @@ + + x++====******====++x + ++==***%==xo ox==%***==+x + xx=*=*++ ++*=*=xx + ++**++ ~ox++++xo~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$*o ++** + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$% ~*@@@@$$$$$$$$$$$$$@* o*@@@@$$$$$$$$$$$@ x+++ + ==+ *@$$$@% o*@@@@$$$$$$$$@% o%@@@@$$$$$$$@* x== + == @$$$$$$ o%@@$$$$$$$$ o%@@$$$$$$@ == + == $$$$$$$@$= ยท$$$$$$$$@$+ ~$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@ยท == + == ยท@$$$$$@@+ *$$$$$$$@@+ %$$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$$% x$@@@$$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@=xx%@@@@$$$$$$$$$$$$$$@@=x+%@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$$@@@ ยท+== + ++++ %@@@@@@@@@*ยท *@@@@@@@@@$x x$@@@@@@@@@* ++++ + ==+x ยทยท ยทยท ยทยท x+== + x+==++ +=%% +%*+ ++==++ + xx==**=*++xxxx*=*=**==**=*++xxxx+=*=**==**=*=+xxxx++*+**==++ + ++++******==++ xx++*******=++++ ++++=*******++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_104.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_104.txt new file mode 100644 index 00000000..6447b659 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_104.txt @@ -0,0 +1,41 @@ + + xx++=====**=====++xx + ++++***%**++~ยท ยท~++**%***+++x + xx==*%=+ +=%*==xx + ++**++ ~~oooo~~ ++**++ + ==== ยท+%@@@@@@@@@@@@@@@@@@%+ยท ==== + **+x ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ==+x ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@@ ==xo + +++x $$$$$@%ยท o%@@@@$$$$$$$$$$$$$@%ยท x%@@@@$$$$$$$$$$$$ x+++ + ==+ยท =@$$$@$ x%@@@@$$$$$$$$@% x%@@@@$$$$$$$@= ยท+== + == @$$$$$$ x%@@$$$$$$$$ x$@@$$$$$$@ == + == $$$$$$$@$+ ~$$$$$$$@@$x o$$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@~ == + == ยท@$$$$$$@= =$$$$$$$@@= *$$$$$$@ยท == + == ยท@$$$$$$ o%@@@$$$$$$$% o%@@@$$$$$$@ยท == + == ยท@$$$$$$ o*@@@@$$$$$$$$$$% o*@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@=oo*@@@@$$$$$$$$$$$$$$@@+ox*@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยทx== + +++= =@@@@@@@@@= +$@@@@@@@@%o ~%@@@@@@@@$= ++++ + ==++ +=== + xx=*=+ =*%% ++%%++ +=**+x + ++**=*=+++++******++**=%*=++++==*=**++**=*==++++++*=**++ + xx++======++++ ++======++++ ++++==**==++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_105.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_105.txt new file mode 100644 index 00000000..3a119586 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_105.txt @@ -0,0 +1,41 @@ + + xx++============++xx + ++++**%%**==xo~~~~ox==**%%**+++x + xx==**== ==**==xx + ++**+= ยท~~~~ยท =+**++ + ===+ x*$@@@@@@@@@@@@@@@@$*x +=== + **++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++** + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ox== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@$ ==xo + ++++ $@$$$@$o +$@@@$$$$$$$$$$$$$$@$~ +$@@@$$$$$$$$$$$@$ x+++ + ==+ยท +@$$$$$ +$@@@@$$$$$$$$@% +$@@@$$$$$$$$@+ ยท+== + == @$$$$$$ +$@@$$$$$$$$ +$@@$$$$$$@ == + == $$$$$$$@%o x$$$$$$$@@%o x$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@ยท == + == ยท@$$$$$$@*~ +$$$$$$$@@*~ =$$$$$$@ยท == + == ยท@$$$$$$ ยท*@@@$$$$$$$% ~*@@@$$$$$$@ยท == + == ยท@$$$$$$ ยท=@@@@$$$$$$$$$@% ~*@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@+ยท~=@@@@$$$$$$$$$$$$$$@$xยท~=@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ยท+== + ++== =$@@@@@@@$= x%@@@@@@@@%~ *@@@@@@@@$+ ==+x + ==++ ++== + ===+ ==*%+x ++%%=+ +=== + ++**=%==++++*=**==++**=*==++++***=**++******++++==%=**++ + ++======++++ ++======++++ ++++======++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_106.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_106.txt new file mode 100644 index 00000000..d472c438 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_106.txt @@ -0,0 +1,41 @@ + + ++++++====++++++ + xx+=***%%%**==++++==**%%%***=+xx + ox==**=*x ox*=**==xo + ++**+= =+**++ + ==== ~+%$@@@@@@@@@@@@@@$%+~ ==== + ===+ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x +=== + ===+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ox== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xx + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + xx== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@% ==xo + ++++ %@$$$@@+~o=@@@@$$$$$$$$$$$$$$@@+~o*@@@@$$$$$$$$$$$@% ++++ + ==+ยท x@$$$$$ ~*@@@@$$$$$$$$$$% o*@@@@$$$$$$$$@x ยท+== + == @$$$$$$ ~*@@@$$$$$$$% o%@@@$$$$$$@ == + == $$$$$$$@=ยท =$$$$$$$@@= *$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$$$$$$@@@@x $$$$$$@ยท == + == ยท@$$$$$$@$x o$$$$$$$@@$x x$$$$$$@ยท == + == ยท@$$$$$$ x$@@$$$$$$$$ +$@@$$$$$$@ยท == + == ยท@$$$$@$ x%@@@@$$$$$$$$@% x$@@@@$$$$$$$$@ยท == + == ยท@$$$$$@$~ x%@@@@$$$$$$$$$$$$$@%ยท x%@@@@$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + x+== +$@@@@@@@$+ o%@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%== ==== + ++**=***++==%***==++***%**++++*=%**=++==***%==++**%=**++ + x+++==++++ x+++++==++xx ++++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_107.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_107.txt new file mode 100644 index 00000000..0d5034d4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_107.txt @@ -0,0 +1,41 @@ + + ++++++++++++++++ + ++*****%*%=******=%*%*****++ + ++**=*+x x+*=**++ x + ++**+= =+**++ + ====ox o=%@@@@@@@@@@@@@@%=o xo==== + ===+ ~*@@@@@@$$$$$$$$$$$$$$@@@@@@*~ +=== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=== + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xo + ==+x o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ox== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@= ==xo + ++++ *@$$$@@*x+%@@@@$$$$$$$$$$$$$$@@*x+%@@@@$$$$$$$$$$$@* ++++ + ==xยท ~@$$$$$ x%@@@@$$$$$$$$$$% x$@@@@$$$$$$$$@~ ยทx== + == @$$$$$$ x$@@@$$$$$$$% +$@@@$$$$$$@ == + == $$$$$$@@+ %@$$$$$$@@x %$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$$$$$$@@@@x @$$$$$@ยท == + == ยท@$$$$$$@$=ยท $$$$$$$$@$=ยท ~$$$$$$@ยท == + == ยท@$$$$$$ ~*@@$$$$$$$$ o%@@$$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$@% ~*@@@@$$$$$$$$@ยท == + == ยท@$$$$$@* ยท=@@@@$$$$$$$$$$$$$@* ~*@@@@$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== x%@@@@@@@$+ ~%@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====x ==%%++ ++%%== ==== + ++***%**====%***++++==*%**====%***==++++***%==+=***=**++ + xx++++++++ ++==++++ ++++++++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_108.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_108.txt new file mode 100644 index 00000000..0b07a3b3 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_108.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==***%*%*%%%%%%*%*%***==++ + ++**=*== ==*=**++ + ++**=*++ ++*=**++ + ++**++ ~+*%@@@@@@@@@@%*+~ ++**++ + ==== o*@@@@@@@$$$$$$$$$$@@@@@@@*o ==== + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + xx==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ox== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@o ==xo + ++++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$@$%%@@@@$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$ o%@@@@$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@% x%@@@$$$$$$$$ == + == @$$$$$@% x@$$$$$$$@* x@$$$$$$@ == + == ยท@$$$$$$$@@@*ยท $$$$$$$$@@@@* $$$$$$@ยท == + == ยท@$$$$$$@@$+ยท $$$$$$$$@@$+ $$$$$$@ยท == + == ยท@$$$$$$x o%@$$$$$$$$o x$@$$$$$$@ยท == + == ยท@$$$$@% o%@@@@$$$$$$$@% x%@@@@$$$$$$$@ยท == + == ยท@$$$$$$x o*@@@@$$$$$$$$$$$$$o o%@@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + ox== x%@@@@@@@$+ ~*@@@@@@@@*~ =$@@@@@@@$+ ==+x + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + +++++++x ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_109.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_109.txt new file mode 100644 index 00000000..c187dd59 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_109.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==****%%*%%%%%%*%%****==++ + ++****=*++ ++*=****++ + ++=*==+= =+====++ + ++**++ ~+=%$$@@@@$$%=+~ ++**++ + ++==oo +%@@@@@@@@$$$$$$@@@@@@@@%+ oo==++ + ++== o$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$o ==++ + ++==~ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~==++ + ox==+~ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ~+==xo + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ++++ ~@$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$@~ ++++ + ==+~ @$$$$$~ =@@@@$$$$$$$$$$$$$ยท ยท=@@@@$$$$$$$$$$@ ~+== + == $$$$$@% ยท=$@@@$$$$$$$@% ยท*@@@@$$$$$$$$ == + == @$$$$$$= ยท*@$$$$$$$$+ ~*@$$$$$$@ == + == $$$$$$$@@@*x $$$$$$$$@@@*o $$$$$$$ == + == ยท@$$$$$$@@@$+ $$$$$$$$@@@%x $$$$$$@ยท == + == ยท@$$$$$@* =@$$$$$$$@= =@$$$$$$@ยท == + == ยท@$$$$@$ +$@@@$$$$$$$@% +$@@@$$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$ +$@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$@@$$@@@$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== +%@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_110.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_110.txt new file mode 100644 index 00000000..0ecf74c8 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_110.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++++==******%%%*******==++++ + ++==**=***++ ++******==++ + ++==**=*+o o+*=**==++ + ++====x ยทo++====++oยท x====++ + ++==++ +%@@@@@@@@@@@@@@@@@@@@%+ ++==++ + ++==xo +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ ox==++ + ++==+~ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ~+==++ + ==+x =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= x+== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==xยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยทx== + ++== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$@ ==++ + +++x @$$$$$* ยท=@@@@$$$$$$$$$$$$$$= ~=@@@@$$$$$$$$$$$@ x+++ + == *$$$$@$ ยท*@@@@$$$$$$$$@% ~*@@@@$$$$$$$$* == + == @$$$$$$ ~*@@$$$$$$$$ ~*@@$$$$$$@ == + == $$$$$$$@@=~ $$$$$$$$@@=ยท ยท$$$$$$$ == + == ~@$$$$$$$@@@@x $$$$$$$$$@@@@o $$$$$$@~ == + == ยท@$$$$$@@x %$$$$$$$@@x %$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$% +$@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$% +$@@@$$$$$$$$$$@ยท == + == ยท@$$$$$@@*++%@@@@$$$$$$$$$$$$$$@@*+=%@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ยท+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+===***++==%***++x+==*%**====**%*==xx++***%==++***=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_111.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_111.txt new file mode 100644 index 00000000..f9eafa6a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_111.txt @@ -0,0 +1,41 @@ + + ++xxxx++ + xx++==****************==++xx + x+==**==*%=*++oo oo++==%*==**==++ + x+++**=*+= =+*=**+++x + ++====++ ~oooo~ ++====++ + ++===+ +%@@@@@@@@@@@@@@@@@@%+ +===++ + ++==+x ยท*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท x+==++ + x+==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x+==+x + ==++ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=== + ++== ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ==++ + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + ++== $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ ==++ + +++x $$$$$@@x ยท+$@@@$$$$$$$$$$$$$$@$o ~=@@@@$$$$$$$$$$$$$ x+++ + ==xยท =@$$$$$ =$@@@$$$$$$$$$@% =$@@@$$$$$$$$@= ยท+== + == @$$$$$$ =$@@$$$$$$$% ยท=@@@$$$$$$@ == + == $$$$$$$@%o +$$$$$$$@@*~ +$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$$$$$$@@@@+ $$$$$$@ยท == + == ยท@$$$$$$@%o x$$$$$$$@@%~ +$$$$$$@ยท == + == ยท@$$$$$$ =@@@$$$$$$$$ ยท=@@@$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$$$$$% =$@@@$$$$$$$$$@ยท == + == ยท@$$$$$@$x ยท+$@@@$$$$$$$$$$$$$$@$o ยท=$@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ยท+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%*=++++%***=+++==*%**++++**%*==+++=***%++++==%=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_112.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_112.txt new file mode 100644 index 00000000..75ce50d7 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_112.txt @@ -0,0 +1,41 @@ + + + xx++====********====++xx + ++==**=**%****++++****%**=**==++ + ++=====*+x ++*=====++ + ++====== ======++ + ++====xx o=%$@@@@@@@@@@@@$%=o xx====++ + ++===+ ยท*$@@@@@$$$$$$$$$$$$$$@@@@@$*ยท +===++ + xx===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +===xx + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$$@@$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$@= ==xx + ++++ *@$$$$@$*%@@@@$$$$$$$$$$$$$$$@@$*%@@@@$$$$$$$$$$$$@* ++++ + ==+ยท ~@$$$$$ ~*@@@@$$$$$$$$$$$$ ~*@@@@$$$$$$$$$@~ ยท+== + == @$$$$$$ ~*@@@$$$$$$$@% o*@@@$$$$$$$@ == + == $$$$$$@%~ o$$$$$$$$@%ยท x$$$$$$$$ == + == ยท@$$$$$$$@@@%~ $$$$$$$$$@@@%ยท $$$$$$@ยท == + == ยท@$$$$$$@@%+ $$$$$$$$@@%x $$$$$$@ยท == + == ยท@$$$$$$o x$@$$$$$$$$~ +$@$$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$@% +$@@@$$$$$$$$@ยท == + == ยท@$$$$$$+ x%@@@@$$$$$$$$$$$$$x x%@@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ==++ ++== + ===+ ++%% %%++ +=== + ++**=%+++x++**%*=+++===*==++++==*===++++*%**++++++****++ + ++++====++ ++====++ ++====++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_113.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_113.txt new file mode 100644 index 00000000..ee9846d6 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_113.txt @@ -0,0 +1,41 @@ + + + xx++++============++++xx + ++====**=**%%%****%%%**=**====++ + ++====**=*o~ ~o*=**====++ + ======++ ++====== + x+====++ ~+*%$@@@@@@@@$%*+~ ++====++ + xx==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ====xx + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + =+== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + x+==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + ++++ x@$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$o ยท*@@@@$$$$$$$$$$$$$~ ~*@@@@$$$$$$$$$$@ ~+== + == $$$$$@% ~*@@@@$$$$$$$@* ~*@@@@$$$$$$$$ == + == @$$$$$$+ ~*@$$$$$$$$x o*@$$$$$$@ == + == ยท@$$$$$$@@@*o $$$$$$$$@@$*~ @$$$$$@ยท == + == ยท@$$$$$$@@@$+ $$$$$$$$@@@$+ @$$$$$@ยท == + == ยท@$$$$$@* +@$$$$$$$@* =@$$$$$$@ยท == + == ยท@$$$$@% +$@@@$$$$$$$@% +$@@@$$$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$ +$@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$$@@@@$$$$$$$$$$$$$$$$@@$$@@@@$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + xx++ ยท%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++++ + ==+x ยท~~ ยท~~ยท ~~~ x+== + ==++ x+%% %%++ +=== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ x+++====++xx ++++====++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_114.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_114.txt new file mode 100644 index 00000000..396d5a29 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_114.txt @@ -0,0 +1,41 @@ + + + xx++++++++++++++++++ + ++++==****=***%%%%***=****==++++ + ++++=====%==o~ ~o==%=====++++ + ++====+= =+====++ + ====++ ~x=**%%%%**=x~ +===== + ====++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++==== + ====o~ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*ยท ~o==== + ++==xยท =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ยทx==++ + xx==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+==xx + =+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++= + xx==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx==xo + ++++ @$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$@$~ x%@@@@$$$$$$$$$$$$$@%ยท x%@@@@$$$$$$$$$$$@ o+== + == %$$$$$$ x%@@@@$$$$$$$$@% x%@@@@$$$$$$$$% == + == @$$$$$$ x%@@$$$$$$$$ +$@@$$$$$$@ == + == $$$$$$$@%+ o$$$$$$$@@%x x$$$$$$$ == + == ยท@$$$$$$$@@@@+ $@$$$$$$$@@@@+ $$$$$$@~ == + == ยท@$$$$$@@=ยท =$$$$$$$@@= *$$$$$$@ยท == + == ยท@$$$$$$ ~*@@@$$$$$$$% o*@@@$$$$$$@ยท == + == ยท@$$$$$$ ~*@@@@$$$$$$$$$$% ~*@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@+~o*@@@@$$$$$$$$$$$$$$@@+~o*@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ยท@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + x+++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x +=++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**==o~ ++*+**++**+*xo ox*+**++**=*++ ~o=+**=+ + ++==***=++++ ++==****==++ x+++****==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_115.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_115.txt new file mode 100644 index 00000000..f5d198a4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_115.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%****===++ + ++=====*+x x+*=====++ + ++====xx ยทox+====+xoยท xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+==xo + ++== @@$$$$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$$@*x+%@@@@$$$$$$$$$$$$$$@@*x+%@@@@$$$$$$$$$$$$@ x+== + == *$$$$$$ x%@@@@$$$$$$$$$$% x%@@@@$$$$$$$$$* == + == @$$$$$$ x%@@@$$$$$$@% +$@@@$$$$$$@ == + == $$$$$$@@+ *$$$$$$$@@x %$$$$$$$ == + == ~@$$$$$$$@@@@x $$$$$$$$$@@@@x $$$$$$@~ == + == ยท@$$$$$$@@=ยท $$$$$$$$@$= ~$$$$$$@ยท == + == ยท@$$$$$$ ~*@@$$$$$$$$ o*@@$$$$$$@ยท == + == ยท@$$$$@$ ~*@@@@$$$$$$$$@% ~*@@@@$$$$$$$$@ยท == + == ยท@$$$$$@* ~*@@@@$$$$$$$$$$$$$@* ~*@@@@$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@* ++++ + **x~ ~+===+o x====x o+=*=+~ ~+** + **+x ++ ++ x+** + ==**++ ~o*+**=***+= =+**==**+*o~ ++==== + +==****==+++ ++=******=++ ++===*%**==+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_116.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_116.txt new file mode 100644 index 00000000..0d845be9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_116.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=**%=+xo~~~~ox+=***=****++++ + xx++===*== =+*===++xx + ++====++ ยท~~~~ยท ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +===+x + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$@$ x+++ + ==xยท +@$$$$$x o%@@@@$$$$$$$$$$$$$o x%@@@@$$$$$$$$$$@+ ยทx== + == @$$$$@% o%@@@@$$$$$$$@* x%@@@@$$$$$$$@ == + == $$$$$$$o o%@$$$$$$$$~ x%@$$$$$$$ == + == ยท@$$$$$$@@$+ $$$$$$$$@@%+ $$$$$$@ยท == + == ยท@$$$$$$@@@@* $$$$$$$$@@@$* $$$$$$@ยท == + == ยท@$$$$$@* x@$$$$$$$@* x@$$$$$$@ยท == + == ยท@$$$$@% o%@@@$$$$$$$@* x%@@@$$$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$$$$$ x%@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$@@%$@@@@$$$$$$$$$$$$$$$$@@%$@@@@$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@@$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@$ยท x+++ + **~ยท ~=%$$$%=o +*$$$$*+ o=%$$$%=~ ** + xx**o~ ~ยท**xx + ox**+* ++****=*+x x+*=****++ *+**xx + ++==*%%%%%**== x++=*%%%%%%*=++x ++**%%%%%*==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_117.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_117.txt new file mode 100644 index 00000000..591cfaea --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_117.txt @@ -0,0 +1,41 @@ + + + + ++++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* *=*=*=++ + x+====+= =+====+x + ++===+ ยท+%$@@@@@@@@@@@@@@$%+ยท +===++ + x+==++ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x ++==+x + ox==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++==xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+== + ++== %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$$$@@@$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$$@% ++++ + ==+ยท x@$$$$@@%=*@@@@@$$$$$$$$$$$$$$@@%**@@@@$$$$$$$$$$$$$@x ยท+== + == @$$$$$% +$@@@@$$$$$$$$$$% +$@@@@$$$$$$$$$@ == + == $$$$$@* o*@@@$$$$$$@+ o*@@@$$$$$$$ == + == ยท@$$$$$@%ยท x$$$$$$$@* x$$$$$$@ยท == + == ยท@$$$$$$@@@@* *@$$$$$$@@@@= %@$$$$@ยท == + == ยท@$$$$$@$o ~$$$$$$$@%~ o$$$$$$@ยท == + == ยท@$$$$@* ยท=$@@$$$$$$@= ยท=$@@$$$$$$@ยท == + == ยท@$$$$$% o%@@@@@$$$$$$$$$* x%@@@@$$$$$$$$$@ยท == + == ยท@$$$$$@@*+=$@@@@$$$$$$$$$$$$$$@@*+=$@@@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$@@@$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@x o+++ + ** +%@@@@@%+ o*$@@@@$*o +%@@@@@%+ **xo + x+** **++ + x+**++ ox****== ==****xo =+**++ + +=**%%%%%%%*==++ ++==*%%%%%%*==++ x+==*%%%%%%%**++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_118.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_118.txt new file mode 100644 index 00000000..436dfbe1 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_118.txt @@ -0,0 +1,41 @@ + + + + x+++++========++++++ + ++==*****%%%********%%%*****==++ + ++==**=*=+ ++*=**==++ + ++**+= =+**++ + xx====ox ~=%$@@@@@@@@@@@@$%=~ xo====xx + x+===+ ยท=@@@@@@$$$$$$$$$$$$$$@@@@@@=ยท +===+x + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + x+== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==+x + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+ยท ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยท+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$$@@@@@@@@@@@@@@@@@$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$$ == + == ยท@$$$$@$+xxxxxxxxxxxxxx=$@$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$@o x$$$x @$$$$@ยท == + == ยท@$$$@~ o@$@~ @$$$$@ยท == + == ยท@$$$$@%oยทยทยทยทยทยทยทยทยทยทยทยทยทยทo%@$$$@%oยทยทยทยทยทยทยทยทยทยทยทยทยทยท~*@$$$$$@ยท == + == ยท@$$$$$@@@@@@@@@@@@@@@@@@$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== +$@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==$%++ ++%%== oo==++ + x+==*%**====****++++++*%**====**%*++++++**=*====*=%*==++ + ++++++ ++++++++ ++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_119.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_119.txt new file mode 100644 index 00000000..a334097c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_119.txt @@ -0,0 +1,41 @@ + + + + ++++++====++++++ + xx++*******%*%%**%%*%*******++xx + xx+=****== ==****=+xx + ++**+*x x*+**++ + ====x+ x*%$@@@@@@@@@@$%*x +x==== + ==== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==== + ==++ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx==xยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทo==xx + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+ยท @$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$@ ยท+== + == @$$$$$$$@@@@$%==**$@@@@$$$$$$$$@@@@@%*==*%@@@@$$$$$$$@ == + == @$$$$$@@= =@@$$$$$@%o o$@$$$$$@ == + == ยท@$$$$@* + *$$$$@ xo o@$$$$@ยท == + == ยท@$$$$* ยทx * $$$@ * = =@$$$@ยท == + == ยท@$$$@x @ * x $$$$ x+ % * +@$$$@ยท == + == ยท@$$$$$ + *$$$@ x o@$$$$@ยท == + == ยท@$$$$@$ ยทยท o+=$@@$$$$@x ~ ยท++%@@$$$$$@ยท == + == ยท@$$$$$$@@= =@@@$$$$$$$@@*o @@@$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + xx== *@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%ยท +++x + ==++ ยท~ยท ~~ ยท~ยท x+== + ==++ x+%% %%+x ++== + ++**=*++xox+==%*==++===%==xoox==%===++==*%==+xoo++*=**++ + +++++===++ +++==+++ +++===++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_120.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_120.txt new file mode 100644 index 00000000..8bd15cf1 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_120.txt @@ -0,0 +1,41 @@ + + + + x++++++++++++++x + ++==*****%%%%%%%%%%*****==++ + ++**=*=* *=*=**++ + xx**=*++ ++*=**++ + ++**x+ ~+*$$@@@@@@@@$$*+~ +x**++ + ==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ==== + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + ox==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ x@$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@x ++++ + ==+~ @$$$$$$@@@*xยท ยทx*$@@$$$$$$$$@@@%=~ o=%@@@$$$$$@ ~+== + == $$$$$$@@+ x@@$$$$$@* $@$$$$$$ == + == @$$$$@% ~~o=$x $$$$$@ ยท~~x%= +@$$$$@ == + == ยท@$$$$$ % = @$$@ =ยท %$$$$@ยท == + == ยท@$$$@x @ o *x $$$$ ~+ x @ o@$$$@ยท == + == ยท@$$$@+ $ x ยท @$$$ ยท+ + ~ *$$$$@ยท == + == ยท@$$$$$ * $$$$@~ xยท +@$$$$@ยท == + == ยท@$$$$$$ ยทo%*xxx=$@$$$$$@~ ~=%+ox+%@@$$$$$@ยท == + == ยท@$$$$$$@= x@@@$$$$$$$@$ยท @@@$$$$$$$@ยท == + == ยท@$$$$$$$@@@$=x~ยทยท~x%$$$$$$$$$$$@@@$*+oยทยท~o*$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@*o ยท+$@@@@@@@@@@$+ยท o*@@@@@@@@@@@* ++++ + **+~ ~+=*=+o ยทx=**=xยท o+=*=+~ ยท+** + **xx ++ ++ o+**xx + ====++ *=**==**+= =+**==**+= ++==== + +==**%*==+++ ++=******=++ +++=**%%*=== + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_121.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_121.txt new file mode 100644 index 00000000..ad287b62 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_121.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++****%%%%%%%%%%%%****++++ + ++***%*%x~ ~x%*%***++ + x+**==++ ++==**++ + ++**++ ยทx*%$$@@@@@@$$%*xยท ++**++ + ++==~ o*@@@@@@@@$$$$$$$$@@@@@@@@*o ~==++ + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ==xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + x+++ o@$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$@o ++xx + ==x~ @$$$$$$@@%x o*@@$$$$$$$$@@$=~ ~=$@@$$$$$@ ~x== + == $$$$$$@$ยท $@$$$$$@+ *@$$$$$$ == + == @$$$$@* ยท~ยท~x$o %$$$$@ oยทยทo%= ~@$$$$@ == + == ยท@$$$$$ * = @$$@ % o *@$$$@ยท == + == ยท@$$$@x @ * *+ $$$$ oo * @ o@$$$@ยท == + == ยท@$$$@+ % @$$$ * %$$$$@ยท == + == ยท@$$$$@ + @$$$@x ~o *@$$$$@ยท == + == ยท@$$$$$@ ยทx==x=*%@@$$$$$@x o+=x+=%$@@$$$$$@ยท == + == ยท@$$$$$$@%ยท x@@$$$$$$$$@@x @@$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@%=++++*$$$$$$$$$$$$$@@@$*=+++=%$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@$$$@@@@@=x~o+%@@@@@$$@@@@@%+o~x=@@@@@$$$@@@@$ x+++ + **~ยท ~=%$$$%=o x*$$$$*x o=%$$$%=~ ** + xx**o~ ~~**xx + xx**+= ++******+x x+******++ *+**xx + ++==*%%%%%%*=+ xx==*%%%%%%*=+xx +=*%%%%%%*==++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_122.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_122.txt new file mode 100644 index 00000000..53880658 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_122.txt @@ -0,0 +1,41 @@ + + + + x++++++x + ++==***%%%%%%%%%%***==++ + x+==**=*+o o+*=**==+x + ==**++ ++**== + ++**++ x=%$$@@@@@@$$%=x ++**++ + ++**oo ~=@@@@@@@@$$$$$$$$@@@@@@@@=~ oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + ++=* ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ *=++ + ==x~ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + ++++ o@$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$@o +++x + ++x~ @$$$$$$@@%x o*@@$$$$$$$$@@$=ยท ยท=$@@$$$$$@ ~x++ + == $$$$$$@$ $@$$$$$@x =@$$$$$$ == + == @$$$$@= ~ยท o%~ *$$$$$ ~ ยท*+ @$$$$@ == + == ยท@$$$$% = * @$$@ $ + =@$$$@ยท == + == ยท@$$$@o @ ~$ *+ $$$$ xo @ @ x@$$$@ยท == + == ยท@$$$@= = @$$@ % $$$$$@ยท == + == ยท@$$$$@ ~ยท x@$$$@= x ยท $@$$$$@ยท == + == ยท@$$$$$@x *@@@@$$$$$@% o$@@@$$$$$$@ยท == + == ยท@$$$$$$@@= x@$$$$$$$$$@@%o @$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@@$$$$@@$$$$$$$$$$$$@@@@@@$$$@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ == + +++o =@@@$$$$$$$@@@@%**$@@@@$$$$$$@@@@$**%@@@@$$$$$$$@@@= ~+++ + ox== ~*@@@@@@@*o +$@@@@@@$+ o*@@@@@@@*~ ==xo + ++== ==++ + ++**++ =*%%== ==%%*= +x**++ + +=***%%%%*%=**+x ++***%%%%%%***++ x+**=%*%%%%***=+ + xxxx xxxx xxxx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_123.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_123.txt new file mode 100644 index 00000000..db5c78f4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_123.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++==***%*%%%%%%*%***==++ + xx==***%+x x+%***==xx + ==**++ ++**== + ++**++ o=*%$$@@@@$$%*=o ++**++ + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + x+=* ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท *=xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@o ++xx + +++~ @$$$$$@@@*o ~*@@@$$$$$$$@@%x +%@@$$$$$@ ~+++ + == $$$$$$@% %@$$$$@@o +@$$$$$$ == + == @$$$$@+ o ~*~ =@$$$$ ยท~ =+ @$$$$@ == + == ยท$$$$$% ยท= * @$$@ $ + =@$$$$ยท == + == ยท@$$$@o @ ~@ *x $$$$ x~ @ @ x@$$$@ยท == + == ยท@$$$@* +~ ยท@$$@ % $$$$$@ยท == + == ยท@$$$$@ ~ ยท =@$$$$* x o ~@@$$$$@ยท == + == ยท@$$$$$@+ *@@@@$$$$$@$ ~@@@@$$$$$$@ยท == + == ยท@$$$$$$@@%o +$$$$$$$$$$@@$+ $$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ยท+== + xx== +$@@@@@@@@= o%@@@@@@@@%o =@@@@@@@@$+ ==+x + ++=+ +=++ + ++*=o +=%$++ ++$%=+ o==++ + x+==**=*===*%***++xx++*%*%====%*%*++xx++***%*===***===xx + ++++++xx ++++++++ xx++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_124.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_124.txt new file mode 100644 index 00000000..d7a2e4e1 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_124.txt @@ -0,0 +1,41 @@ + + + + + ++++***%%$%%%%$%%***++++ + ++***%+x x+%***++ + ++**++ ++**++ + x+**++ o=*%$$@@@@$$%*=o ++**+x + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + x+** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$$$$@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@~ ++xx + +++~ @$$$$$@@$= +$@@$$$$$$$@@*o o*@@$$$$$@ ~+++ + == $$$$$$@* *@$$$$@@ยท x@$$$$$$ == + == @$$$$@o o = +@$$$% oยท xx @$$$$@ == + == ยท$$$$$% ~+ * @$$@ $ * +@$$$$ยท == + == ยท@$$$@o @ o@ *x $$$$ x~ @ @ x@$$$@ยท == + == ยท@$$$@* xo o@$$@ $ $$$$$@ยท == + == ยท@$$$$@~ ~ ~o *@$$$$% o + x@$$$$$@ยท == + == ยท@$$$$$@= *@@@@$$$$$@$ ~@@@@$$$$$$@ยท == + == ยท@$$$$$$@@$x +$$$$$$$$$$$@@=ยท $$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + x+++ ~%@@@@@@@@@$x =@@@@@@@@@@= x$@@@@@@@@@%~ ++++ + ===x ~~~ ยท~~ยท ~~~ x+== + ===+ x+%% %%+x +=== + ++**=%+xยทยท~o==*===++***%=+~ยทยท~+=%***++===*==o~ยทยทx+%=**++ + ++=====+++ xx++=====+xx +=======++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_125.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_125.txt new file mode 100644 index 00000000..4a14a3f4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_125.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%%%***==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ o=%%$$@@@@$$%%=o ++**xx + x+**oo ~=$@@@@@@@$$$$$$$$@@@@@@@$=~ oo**+x + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@o ++xx + +++~ @$$$$$@@$+ x%@@$$$$$$$@@*ยท ~*@@$$$$$@ ~+++ + == $$$$$$@* *@$$$$@$ ~@$$$$$$ == + == @$$$$@o o + x@$$$% x o~ @$$$$@ == + == ยท$$$$@* ox * $$$@ $ * +@$$$$ยท == + == ยท@$$$@o @ o@ *o $$$$ x~ @ @ x@$$$@ยท == + == ยท@$$$@* ox o@$$@ $ $$$$$@ยท == + == ยท@$$$$@o ~ ox %@$$$$% ~ = +@$$$$$@ยท == + == ยท@$$$$$@* =@@@@$$$$$@@ยท ยท@@@@$$$$$$@ยท == + == ยท@$$$$$$@@$+ +$$$$$$$$$$$@@*o $$$$$$$$$$@ยท == + == ยท@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ยท@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ x@@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@@x ++xx + ==+o ~x+xo ยทx++xยท ox+xo o+== + **++ == =* x+** + +=**==oยท ++*=**==**=*+o o+*=**==**=*+x ยทo=+**=+ + ++==****++++ ++==****==++ ++++****==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_126.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_126.txt new file mode 100644 index 00000000..4ed857cd --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_126.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%%%**==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ ยทx=%$$@@@@@@$$%=xยท ++**xx + x+**~ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ~**+x + ++** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx++ o@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@o ++xx + ++x~ @$$$$$@@%o ~*@@$$$$$$@@$+ =@@$$$$$@ ~x++ + == $$$$$$@= =@$$$$@$ @$$$$$$ == + == @$$$$@ยท o o o@$$$* x @$$$$@ == + == ยท@$$$@* xo * $$$@ % % +@$$$@ยท == + == ยท@$$$@o @ o@ =o $$$$ x~ @ @ +@$$$@ยท == + == ยท@$$$$% ยท+ x@$$@ $ @$$$$@ยท == + == ยท@$$$$@x ~ x+ ~$@$$$$$ ยท* =@$$$$$@ยท == + == ยท@$$$$$@% =@@@@$$$$$@@o @@@@$$$$$$@ยท == + == ยท@$$$$$$@@@*~ =$$$$$$$$$$$@@%x ยท$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o ~+@@@@@@@@@@@@+~ o%@@@@@@@@@@@* ++++ + **x~ ~+***=o ยทx=**=xยท o=***+o ยทx** + **+x ++ ++ ox**oo + ====++ *=**==**+= =+**==**=* ++==== + ++=**%%**=++ ++=**%%**=++ ++=**%%*==++xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_127.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_127.txt new file mode 100644 index 00000000..a6d93b3e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_127.txt @@ -0,0 +1,41 @@ + + + + + +++=*%%%%%%%%%%%%%%*=+++ + +=*%=*xยท ยทx*=%*=+ + ++**++ ++**++ + x+**++ ~+*%$@@@@@@@@$%*+~ ++**+x + ++** o*@@@@@@@$$$$$$$$$$@@@@@@@*o **++ + xx** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + xx** o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xx + =*xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx*= + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + =* o@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + xx++ x@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$@@@@@@@@@@@@@$$$$$$@x ++xx + +++~ @$$$$$@@*~ *@@$$$$$$@@$x x$@$$$$$@ ~+++ + == $$$$$$@+ +@$$$$@% @$$$$$$ == + == @$$$$@ ยท~ ~ ~@$$@* + $$$$$@ == + == ยท@$$$@= +~ * $$$@ % $ x@$$$@ยท == + == ยท@$$$@o @ o@ =~ $$$$ xo @ $ +@$$$@ยท == + == ยท@$$$$% + +@$$@ % @$$$$@ยท == + == ยท@$$$$@+ += o$@$$$$$ ~%~ ยท*@$$$$$@ยท == + == ยท@$$$$$@$ =@@@$$$$$$$@x @@@@$$$$$$@ยท == + == ยท@$$$$$$$@@%x =$$$$$$$$$$$@@$+ยท ~$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@$+~ ยทo*@@@@@@@@@@@@*o ~+$@@@@@@@@@@@% ++++ + **xยท x*%%%*+ o=%%%%=o +*%%%*x ยทo** + ox**xo xx xx ~o**xx + ===*+x *+****==++ ++==****+* o+*=** + ++==**%%%**=++ ++=*%%%%*=++ ++==*%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_128.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_128.txt new file mode 100644 index 00000000..7efbabf4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_128.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++=**%*%%%%%%%%%%*%**=++ + x+==*%=* *=%*==+x + ===*++ ++*=== + x+**++ o=%$@@@@@@@@@@$%=o ++**+x + ++** x%@@@@@@$$$$$$$$$$$$@@@@@@%x **++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==++ + x+** x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x **+x + **xยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* x@$$$$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$@@@@$$$$$$$$$@x *= + xx++ +@$$$$$$@@@@$$$$$@@@@$$$$$$$$$$@@@@@$$$$@@@@@$$$$$@+ ++xx + +++~ @$$$$$@@= +@@$$$$$$@@%~ ~%@$$$$$@ ~+++ + == @$$$$$@o ~~ o@$$$$@* ~~ $@$$$$@ == + == @$$$$@ o~ ยท@$$@= x $$$$$@ == + == ยท@$$$@= * *~ $$$@ % @ x@$$$@ยท == + == ยท@$$$@o @ ~@ + $$$$ xo @ % =@$$$@ยท == + == ยท@$$$$% * =@$$@ % @$$$$@ยท == + == ยท@$$$$@= **ยท +@@$$$$@ o%o o%@$$$$$@ยท == + == ยท@$$$$$@@~ =@@@$$$$$$$@+ @@@@$$$$$$@ยท == + == ยท@$$$$$$$@@$+~ *$$$$$$$$$$$@@@*o x$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%xo~o=@@@@@@$@@@@@$ x+++ + **oยท ~=%$$$%=~ x*$$$$*x ~=%$$$%=~ ** + x+**o~ ~~**xx + xx**+= ++****=*+x xx*=****++ ==**+x + ++==*%%%%%**=+ ++++*%%%%%%*++++ ==**%%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_129.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_129.txt new file mode 100644 index 00000000..837a8bd5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_129.txt @@ -0,0 +1,41 @@ + + + + x++++++x + +==**%%%%%****%%%%%**==+ + ++===*+= =+*===++ + ===* *=== + x+**x+ ยท+*$@@@@@@@@@@@@$*+ยท +x**++ + ++== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท =*++ + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + **oยท =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ยทo** + ++++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ++++ + =* +@$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$@+ *= + x+++ =@$$$$$$@@@@$%%%$$@@@$$$$$$$$$$@@@@$%%%%$@@@@$$$$$@= +++x + +++ยท ยท@$$$$$@$x o$@$$$$$$@@* *@@$$$$@ยท ยท+++ + == @$$$$$@ยท ~xx ยท@$$$$@= ยทox~ $@$$$$@ == + == @$$$$@ +ยท ~ @$$@+ x $$$$$@ == + == ยท@$$$@+ * *~ $$$@ * @ x@$$$@ยท == + == ยท@$$$@x @ ยท$ x @$$$ ox $ = =@$$$@ยท == + == ยท@$$$$$ * *$$$@ * ~@$$$$@ยท == + == ยท@$$$$@* ยท*%~ ~*@@$$$$@ +%xยท +$@$$$$$@ยท == + == ยท@$$$$$@@x +@@@$$$$$$$@* @@@$$$$$$$@ยท == + == ยท@$$$$$$$@@@*xยท ~*$$$$$$$$$$$@@@%+~ +$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ยท$@@@@$$$@@@@@*xoo+$@@@@@$$@@@@@$+oox*@@@@@$$$@@@@@~ x+++ + ** ~=%$$$$*o +%$$$$%+ o*$$$$%*o ** + xx**~~ **++ + ++**+= ++==**==x~ ~x==**==++ =+**+x + ++==*%%%%%%*== x+==*%%%%%%*==+x ==**%%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_130.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_130.txt new file mode 100644 index 00000000..cf834c5f --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_130.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++***%%%==++++==%%%***++++ + ++**=*+o o+*=**++ + x+**== ==**+x + ++** x*$@@@@@@@@@@@@@@$*x **++ + ++*+ o%@@@@@@$$$$$$$$$$$$$$@@@@@@%o +*++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + == *@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$@* == + xx++ *@$$$$$@@@@$*+++=%@@@@$$$$$$$$$@@@$*=++=%$@@@$$$$$@* ++xx + ==+ยท o@$$$$$@%ยท %@$$$$$$@@x +@@$$$$@o ยท+== + == @$$$$$@ ยทo+=o @$$$$@x ~x=x *@$$$$@ == + == $$$$$@ * x @$$@o oo %$$$$$ == + == ยท@$$$@+ $ *x $$$$ = @ o@$$$@ยท == + == ยท@$$$@x @ * ~ @$$$ ~+ * x *@$$$@ยท == + == ยท@$$$$$ * $$$$@ + x@$$$$@ยท == + == ยท@$$$$@$ o**xoo+%@@$$$$@ยท ~=%+oox*@@$$$$$@ยท == + == ยท@$$$$$@@= x@@@$$$$$$$@$~ @@@$$$$$$$@ยท == + == ยท@$$$$$$$@@@$=xo~~o+%$$$$$$$$$$$@@@@*+o~~ox*$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xo+*@@@@@$$$@@@@@o x+++ + ** o*$$@$$%x ยท=%$@@$%=ยท x%$@@$$*x ** + x+** **+x + xx**+= xx****== ==****xx =+**++ + ++==*%%%%***++xx ++==*%*%%*%*==++ xx++*%*%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_131.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_131.txt new file mode 100644 index 00000000..9b79db38 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_131.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++==*%*%==++xxxx++==%*%*==++ + ++**=* *=**++ + ++**+= =+**++ + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +=++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$@$ == + x+++ %@$$$$$@@@$*xo~ox=$@@@$$$$$$$$@@@@%+o~ox+%@@@$$$$$@% ++++ + ==+ยท x@$$$$@@* =@$$$$$$@$~ o@@$$$$@x ยท+== + == @$$$$$$ ยทo+%o $$$$$@~ ยท~x*+ =@$$$$@ == + == $$$$$$ * + @$$@ยท +~ %$$$$$ == + == ยท@$$$@x @ ~ *x $$$$ ~+ o @ o@$$$@ยท == + == ยท@$$$@+ $ x @$$$ ยท= x ยท *@$$$@ยท == + == ยท@$$$$$ = $$$$@~ o~ =@$$$$@ยท == + == ยท@$$$$$$ o==o++*$@$$$$$@o ~+*xx+=%@@$$$$$@ยท == + == ยท@$$$$$$@%ยท x@@$$$$$$$$@@o @@@$$$$$$$@ยท == + == ยท@$$$$$$$@@@@%=+xx+*$$$$$$$$$$$$$@@@$*+xx+=%$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** x*$$@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+++ ++==*%*%%*%*==++ x++=***%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_132.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_132.txt new file mode 100644 index 00000000..fbcdcfb5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_132.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**++ ยทoxx++xxoยท ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==x~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$@@ ==xo + +++x @$$$$$$@@$*o ~=$@@$$$$$$$$@@@%x ยทx%@@@$$$$$@ x+++ + ==x =$$$$$@@o ~@@$$$$$@* %@$$$$$= +== + == @$$$$@* ยท~~+%o %$$$$@ ยทยท~x%= o@$$$$@ == + == $$$$$$ * = @$$@ % ~ *@$$$$ == + == ~@$$$@x @ * *+ $$$$ oo * @ o@$$$@~ == + == ยท@$$$@= * @$$$ * %$$$$@ยท == + == ยท@$$$$@ x ~@$$$@+ x %@$$$$@ยท == + == ยท@$$$$$@~ ooยท=%$@@$$$$$@= ~oยทx%$@@@$$$$$@ยท == + == ยท@$$$$$$@@+ x@$$$$$$$$$@@*ยท @@$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@$%%%%@@$$$$$$$$$$$$@@@@@$%%%$@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o o+++ + ** x*$@@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **+x + ++**+= x+****== ==****+x =+**++ + ++==*%%%%*%*==++ ++==*%*%%%%*==++ xx==*%*%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_133.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_133.txt new file mode 100644 index 00000000..8aee11d5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_133.txt @@ -0,0 +1,41 @@ + + + + ++++==**%%%%***=++++ + ++==*%**+x x+**%*==++ + ++**=* *=**++ + ==== ox=******=xo ==== + **++ ยท=$@@@@@@@@@@@@@@@@@@@@$=ยท ++** + **+o =@@@@@$$$$$$$$$$$$$$$$$$$$@@@@@= o+** + **+~ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ~x** + +++x *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+++ + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + ox== @$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@ ==xo + +++o @$$$$$@@@%x o*@@@$$$$$$$@@$+ยท ยท=$@@$$$$$@ o+++ + == %$$$$$@$ $@$$$$$@x =@$$$$$% == + == @$$$$@+ ~~ ~%~ *@$$$$ ~ ยท== @$$$$@ == + == $$$$$% = * @$$@ % + =@$$$$ == + == ~@$$$@o @ ~@ *x $$$$ xo @ @ x@$$$@~ == + == ยท@$$$@= =ยท @$$@ % $$$$$@ยท == + == ยท@$$$$@ ยท~ +@$$$$= o ~ $@$$$$@ยท == + == ยท@$$$$$@x *@@@@$$$$$@% o@@@@$$$$$$@ยท == + == ยท@$$$$$$@@*~ +@$$$$$$$$$@@$x @$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** o*$$@@$%x ยท=%$@@$%=ยท x%$@@$$*x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+xx ++==*%%%%%%*==++ xx+=*%*%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_134.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_134.txt new file mode 100644 index 00000000..778a14e1 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_134.txt @@ -0,0 +1,41 @@ + + + + ++++=**%%%%%%%%%%**=++++ + +=***%+x x+%***=+ + ++**+= =+**++ + ox**+= o=*%$@@@@@@$%*=o ++**xo + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**+x + x+** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **+x + ox** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท == + xx++ ~@$$$$$$@@@@@@@@@@@@@$$$$$$$$$$$@@@@@@@@@@@@$$$$$$@~ ++xx + +++~ @$$$$$@@$x o%@@$$$$$$$@$= =@@$$$$$@ ~+++ + == $$$$$$@= =@$$$$@$ ~@$$$$$$ == + == @$$$$@~ o x x@$$@% x ~ยท @$$$$@ == + == ยท$$$$@* xo * $$$@ $ % +@$$$$ยท == + == ยท@$$$@o @ o@ =o $$$$ x~ @ยท @ +@$$$@ยท == + == ยท@$$$@% ~x x@$$@ $ @$$$$@ยท == + == ยท@$$$$@x ยท xx ยท%@$$$$% ~ = =@$$$$$@ยท == + == ยท@$$$$$@% *@@@@$$$$$@@~ @@@@$$$$$$@ยท == + == ยท@$$$$$$@@$=ยท +$$$$$$$$$$$@@%o $$$$$$$$$$@ยท == + == ยท@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ยท$@@@@$$$@@@@@*xox=$@@@@$$$$@@@@$=oox*@@@@@$$$@@@@@~ x+++ + ** o*%$$$$*x ยท+%$$$$%+ยท x*$$$$$*o ** + x+**~ **+x + xx**+= +x*=**== ==**==x+ =+**+x + ++==*%*%%%**++xx ++==*%%%%%%*==++ xx++**%%%*%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_135.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_135.txt new file mode 100644 index 00000000..2b1bed79 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_135.txt @@ -0,0 +1,41 @@ + + + ++++ + ++==*%%%*%****%*%%%*==++ + x+==*%== ==%*==+x + ===*xo ox*=== + ++**x+ x*%$@@@@@@@@@@$%*x +x**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + x+== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==+x + xx== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + **oยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทo** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + xx++ +@$$$$$$@@@@@@$@@@@@@$$$$$$$$$$@@@@@@$$@@@@@@$$$$$@+ ++xx + +++~ @$$$$$@@= +@@$$$$$$@@%o o$@$$$$$@ ~+++ + == @$$$$$@o ~ o@$$$$@% ยทยท $$$$$$@ == + == @$$$$@ oo ~@$$@= x $$$$$@ == + == ยท@$$$@= = *ยท $$$@ % $ x@$$$@ยท == + == ยท@$$$@o @ ~@ +ยท $$$$ xo @ % +@$$$@ยท == + == ยท@$$$$% = =@$$@ % @$$$$@ยท == + == ยท@$$$$@= **ยท x@@$$$$$ o%o ~%@$$$$$@ยท == + == ยท@$$$$$@$ยท =@@@$$$$$$$@+ @@@@$$$$$$@ยท == + == ยท@$$$$$$$@@$+ *$$$$$$$$$$$@@$=~ o$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$$@@@@@*xoo+%@@@@@$$@@@@@%+o~x=@@@@@$$@@@@@$ยท x+++ + **o ~=%$$$%*o +%$$$$%+ o*%$$$%=~ ** + x+**~~ ~**+x + ++**== ++==**==xo o+==**==++ =+**++ + ++==*%%%%%**++xx ++==*%%%%%%*==++ xx++**%%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_136.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_136.txt new file mode 100644 index 00000000..66c8c404 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_136.txt @@ -0,0 +1,41 @@ + + + x ++++====++++ + ++==*%*%==++++++++==%*%*==++ + ++**=* *=**++ + x+**+= ยทยท =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++++ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ++++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$@$ == + ++++ $@$$$$$@@@@$*=+=*$@@@@$$$$$$$$$@@@@%=+==%@@@@$$$$$@$ ++++ + ==+ยท +@$$$$$@$~ ยท%@$$$$$$@@x +@@$$$$@x ยท+== + == @$$$$$@ ~x=~ @$$$$@x ยทo=x *@$$$$@ == + == $$$$$@ = x @$$@o ~x $$$$$$ == + == ยท@$$$@+ % *o $$$$ = @ o@$$$@ยท == + == ยท@$$$@x @ * o @$$$ ~x * x *@$$$@ยท == + == ยท@$$$$$ * %$$$@ = x@$$$$@ยท == + == ยท@$$$$@% o**oo~+%@@$$$$@ ยท+%xo~x=@@$$$$$@ยท == + == ยท@$$$$$@@= +@@@$$$$$$$@% @@@$$$$$$$@ยท == + == ยท@$$$$$$$@@@$=o~ยทยท~x%$$$$$$$$$$$@@@$*x~ยทยท~o*$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= == + ++++ %@@@@@@@@@@@$+oยท~x*@@@@@@@@@@@@*oยทยท~+$@@@@@@@@@@@% x+++ + **xยท +*%%%*+ยท o=%%%%=o ยท+%%$%*+ ยทo** + x+**o~ oo oo oo**+x + ++**+=+~ =+******+x x+******+= ~x==**++ + ++==**%%%***++xx ++==***%%***==++ xx++***%%%**==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_137.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_137.txt new file mode 100644 index 00000000..de5f454c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_137.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ยทoxx++xxoยท +x**++ + ===+ o*$@@@@@@@@@@@@@@@@@@$*o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$@@ ==xo + +++x @$$$$$$@@@$*xooox=$@@@$$$$$$$$@@@@%+ooox+%@@@$$$$$$@ x+++ + ==x =$$$$$$@= =@$$$$$$@$~ o@@$$$$$* x== + == @$$$$@$ ยทo+%o $$$$$@~ ox*+ =@$$$$@ == + == $$$$$$ * + @$$@ยท +ยท %$$$$$ == + == ~@$$$@x @ ~ *x $$$$ ~+ o @ o@$$$@~ == + == ยท@$$$@+ $ o @$$$ ยท= x ยท *$$$$@ยท == + == ยท@$$$$$ = $$$$@~ o~ =@$$$$@ยท == + == ยท@$$$$$$ ~==o++*$@$$$$$@o ยทx*ox+=%@@$$$$$@ยท == + == ยท@$$$$$$@% x@@$$$$$$$$@$o @@@$$$$$$$@ยท == + == ยท@$$$$$$$@@@@%=+xx+*$$$$$$$$$$$$$@@@$*+xx+=%$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@%xยท ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++++ + **xยท o=*%%=x ~+*%%*+~ x=%%*=x ยทx** + x+**xo ++ ++ ox**+x + x+**=*xx *+****==++ ++==****+* x+==**+x + ++==********++xx ++==********==++ xx++********==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_138.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_138.txt new file mode 100644 index 00000000..284028b3 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_138.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==oยท ยทo==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + x+**xx x*@@@@@@@@@@@@@@@@@@@@@@*x xx**xx + xx**~~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~**xx + **xยท *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ยทx** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + xx== @$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@ ==xx + +++o @$$$$$$@@@*xยท ยทo=$@@$$$$$$$$@@@%+~ ~+%@@@$$$$$@ o+++ + == %$$$$$@@x o@@$$$$$@* %@$$$$$% == + == @$$$$@% ยท~o+%x %$$$$@ ~~x%* x@$$$$@ == + == $$$$$$ % = @$$@ * ~ *@$$$$ == + == ยท@$$$@x @ * *+ $$$$ ox * @ o@$$$@ยท == + == ยท@$$$@+ % @$$@ * %$$$$@ยท == + == ยท@$$$$@ + ยท@$$$@x o %@$$$$@ยท == + == ยท@$$$$$@ ~xx~=%$@@$$$$$@= ox~x*%@@@$$$$$@ยท == + == ยท@$$$$$$@$x x@@$$$$$$$$@@= @@$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@$%**%$@$$$$$$$$$$$$@@@@$%**%$@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@=~ x$@@@@@@@@@@$x ยท=@@@@@@@@@@@+ ++++ + **+~ o+=+xยท o+==+o ยทx+=+x ~x** + xx**+x == == x+**+x + xx**==+= ~+*=******+* *+******=*x~ =+==**xx + ++==********++ x+++********+++x ++********==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_139.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_139.txt new file mode 100644 index 00000000..c4537217 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_139.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + xx**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xo + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$$$$$@@@@@@@@@@$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@o +++x + +++~ @$$$$$$@@$=o ~+$@@$$$$$$$$@@@%xยท ยทx%@@@$$$$$@ ~+++ + == $$$$$$@@o ~@@$$$$$@= %@$$$$$$ == + == @$$$$@* ~~~x%x %$$$$@ ยท~~o%= o@$$$$@ == + == ยท$$$$$$ * = @$$@ * o *@$$$$ยท == + == ยท@$$$@x @ % *+ $$$$ oo % @ o@$$$@ยท == + == ยท@$$$@= * @$$@ * %$$$$@ยท == + == ยท@$$$$@ x ~@$$$@+ o %@$$$$@ยท == + == ยท@$$$$$@~ ~o~~=%$@@$$$$$@* oo~x%$@@@$$$$$@ยท == + == ยท@$$$$$$@@+ x@$$$$$$$$$@@*ยท @@$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@$$%%$$@$$$$$$$$$$$$@@@@@$%%%$@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@ x== + ++=+ o$@@@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+x ยทoxo~ ~oo~ ~oxoยท o+*= + x+**+x =* == ++**xx + x+==**+*+o ++*=**==**=*+x x+*=**==**=*++ ~+*=**==xx + ++++******==++ ++++********++++ ++==******++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_140.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_140.txt new file mode 100644 index 00000000..be700a44 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_140.txt @@ -0,0 +1,41 @@ + + xx++++xx + ===*%%%%*%****%*%%%%*=== + ++**=%++ ++%=**++ + ===* *=== + x+**+x ~+*$@@@@@@@@@@@@$*+~ x+**+x + ++== =$@@@@@$$$$$$$$$$$$$$@@@@@$= ==++ + ++*= ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ =*++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==+x + =*o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= o*= + ++++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ++++ + == =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + x+++ =@$$$$$$$$@@@@@@@@@$$$$$$$$$$$$$$@@@@@@@@@@$$$$$$$@= ++xx + +++ยท ยท@$$$$$$@@@%+~ ~x*@@@$$$$$$$$@@@$=oยท ~o=$@@@$$$$$@ยท ยท+++ + == @$$$$$@@x x@@$$$$$@% $@$$$$$@ == + == $$$$$@% ~o=%x $$$$$@ ~~x%= x@$$$$$ == + == ยท@$$$$$ * = @$$@ * ยท *$$$$@ยท == + == ยท@$$$@x @ = *+ $$$$ ox * @ o@$$$@ยท == + == ยท@$$$@+ % @$$@ * %$$$$@ยท == + == ยท@$$$$@ + ยท@$$$@x o %@$$$$@ยท == + == ยท@$$$$$@ ~xo~=%$@@$$$$$@= ox~x*%@@@$$$$$@ยท == + == ยท@$$$$$$@$x x@@$$$$$$$$@@* @@$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@$%%%%$@$$$$$$$$$$$$@@@@$$%%%$@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยท+== + ++++ =@@@@@@@@@*ยท x$@@@@@@@@$x *@@@@@@@@@= ++++ + ==++ +=== + xx=*=+ ++%%xx xx%%++ +===xx + ==**=*==++++*=*=**==**=%=*++++*=%=**==**=*=*++++==*=**==xx + ++++==****==++ ++==****==++ ++==****==++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_141.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_141.txt new file mode 100644 index 00000000..f5780d60 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_141.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x++=*%*%*%========%*%*%*=++x + ++**=*+x x+*=**++ + xx**=* *=**xx + ++**o x*$@@@@@@@@@@@@@@$*x o**++ + ++=+ o*@@@@@$$$$$$$$$$$$$$$$@@@@@*o +=++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + =* *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* *= + ++++ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+ยท o@$$$$$$$@@@@@@@@@@@@@$$$$$$$$$$@@@@@@@@@@@@@$$$$$$$@o ยท+== + == @$$$$$$@@%~ ~*@@$$$$$$@@$+ +$@$$$$$$@ == + == $$$$$$@o o@$$$$@* $@$$$$$ == + == ยท@$$$$@ x @$$@+ o $$$$$@ยท == + == ยท@$$$@+ % %o $$$$ = @ x@$$$@ยท == + == ยท@$$$@+ $ o @$$$ ยท= o *$$$$@ยท == + == ยท@$$$$@ x $$$$@x ~ *@$$$$@ยท == + == ยท@$$$$$@~ ยท +%$@@$$$$$@= ~%%@@@$$$$$@ยท == + == ยท@$$$$$$@@*ยท +@@$$$$$$$$@@%o @@$$$$$$$$@ยท == + == ยท@$$$$$$$$@@@@@@@@@@@$$$$$$$$$$$$@@@@@@@@@@@$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$%%@@@@$$$$$$$$@@@@%%$@@@@$$$$$$$@@@* ~+== + ++== o%@@@@@@@$x ยท*@@@@@@@@*ยท x$@@@@@@@%x ==++ + ===+ +=== + ox====o+ ==%%++ ++%%== xo====+x + ++**=**%****%***==++**=*=******=*=**++==***%****%**=**++ + x++++=====++++ ++++====++++ ++++======+++x + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_142.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_142.txt new file mode 100644 index 00000000..1051e2b8 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_142.txt @@ -0,0 +1,41 @@ + + xx++++====++++xx + ++==*%**==++oooo++==**%*==++ + ++**=* *=**=+ + ++**+= ยทยทยทยท =+**++ + ++== x=$@@@@@@@@@@@@@@@@$=x ==++ + ==++ =$@@@@$$$$$$$$$$$$$$$$$$@@@@$= ++== + ===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท +=++ + o ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** o + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+ยท +@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+== + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$$@@@@@@@@@@@@@@@@@@$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == ยท@$$$$@= ยท*@$$$@= +@$$$$$@ยท == + == ยท@$$$@ ~@$@ยท @$$$$@ยท == + == ยท@$$$$x +$$$+ @$$$$@ยท == + == ยท@$$$$@@*==============*@@$$$@@*===============$@$$$$$@ยท == + == ยท@$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + ==+x ~@@@@$$$$$@@@@%+x+=$@@@@$$$$@@@@$=+x+%@@@@$$$$$@@@@o o+== + xx== x*$@@@$%x ~=$@@@@$=~ x%$@@@$*x ==xx + ==== ==== + ====+= xx*=**== ==**=*xx =+==== + ++*****%*%%*****==++==***%*%%*%***==++==*****%%%%*****++ + ++++++++++++ x++++++++++x ++++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_143.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_143.txt new file mode 100644 index 00000000..a5cb02dd --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_143.txt @@ -0,0 +1,41 @@ + + ++============++ + ++**%%=*++o~ยทยทยทยท~o++*=%%**++ + +=**== ==**=+ + ++**++ ยท~~oo~~ยท ++**++ + ===+ +%@@@@@@@@@@@@@@@@@@%+ +=== + ==++ ยท*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท ++== + ===x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x=== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + ox== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==xยท =@$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยทx== + == @$$$$$@$**%$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == $$$$$@ o+*%@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$$ == + == ยท@$$$$@ *$$$$% *$$$$$@ยท == + == ยท@$$$$$@ @$$@ @$$$$@ยท == + == ยท@$$$$@ =$$$$% =$$$$$@ยท == + == ยท@$$$$@ ~x=%@@@@$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$@$==%$@@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@= == + ==++ *@@@@@@@@@@@$+~ o*@@@@@@@@@@@@*o ยท+$@@@@@@@@@@@% x+== + ++==xยท x=%%%*x o=*%%*=o x*%%%*x ยทx==++ + ====oo xx xx ~o==== + ====+=++ =+*==*==++ ++=====*+= xx=+==== + ++==**=*********++++==***%****%***==++++************==++ + ++++++++++xx ++++++++++++ x ++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_144.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_144.txt new file mode 100644 index 00000000..b0f7b896 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_144.txt @@ -0,0 +1,41 @@ + + ++====****====++ + +=***%==+o ox==%***=+ + ===*++ ++*=== + ++**x+ ยทooxxxxooยท +x**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ =@$$$$@= o*@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= +== + == @$$$$@+ x%@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$% =@@$$$$@$+oooooooooooooox%@$$$$$$ == + == ยท@$$$$$@@$=~ *@$$$o @$$$$@ยท == + == ยท@$$$$$@@$*x *$$$@o @$$$$@ยท == + == ยท@$$$$$% +@@$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@ยท == + == ยท@$$$$@+ o*@@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$@= ~=$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@ x== + ++++ o$@@@@@@@@@$+ ยท*@@@@@@@@@@*ยท +$@@@@@@@@@$o ++++ + x+==+x ยทooo~ ~oo~ ~oooยท o+==+x + ++==++ +** **+ x+==++ + ++====+=+x ++*========*+x x+*========*++ x+======++ + ++++==********==++++++==********==++++++==********====++ + ++++++++ ++++++++ ++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_145.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_145.txt new file mode 100644 index 00000000..f096e8c6 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_145.txt @@ -0,0 +1,41 @@ + + ++====****====++ + xx+=***%==x~ ~x==%***=+xx + x+===*++ ++*===+x + ++**+x ~ox++++xo~ x+**++ + ==++ x*$@@@@@@@@@@@@@@@@@@$*x ++== + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + x+== @@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==+x + +++x @$$$$@@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$$% ~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* x== + == @$$$$@* =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$@%ยท %@$$$$@x ~$$$$$$$ == + == ~@$$$$$$$@@@%~ %$$$@ @$$$$@~ == + == ยท@$$$$$@@$+ $$$$$= o$$$$$@ยท == + == ยท@$$$$$$ =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ยท == + == ยท@$$$$@* ~*@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@* o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยทx== + ++++ =@@@@@@@@@* x$@@@@@@@@$x *@@@@@@@@@= ++++ + oo==++ ++==xx + ++===+ ++%%+o o+%%++ +===++ + ++=====%+=++++*=*========***++++***========*=*++++=+%=====++ + xx++==******====++ ++==********==++ ++====******==++xx + ++++ ++++ ++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_146.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_146.txt new file mode 100644 index 00000000..423d3e73 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_146.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + ++==*%**=+~ยท ยท~+=**%*==++ + x+=*=*+x x+*=*=+x + ++**xo ยทox+====+xoยท ox**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+o x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+** + ==+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+== + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$* == + == @$$$$$$ o%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@@=ยท ~$$$$$* +$$$$$$ == + == ~@$$$$$$$@@@@* $$$$@ @$$$$@~ == + == ยท@$$$$$$@*~ =$$$$$$ยท %$$$$$@ยท == + == ยท@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$x ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$$@@@$***%@@@@$$$$$$@@@@%***$@@@$$$$$$$@@@+ o+== + ++== =$@@@@@$*~ +%@@@@@@%+ ~*$@@@@@$=ยท ==++ + ===+ +=== + ++====x+ *=**== ==**=* +x====++ + ++=====**%******=========**%****%**=========******%**=====++ + ++++========++xx ++++========++++ xx++========++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_147.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_147.txt new file mode 100644 index 00000000..baf0b289 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_147.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + ++==*%**++~ยท ยท~++**%*==++ + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$$x x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$o =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@$x $$$$$= o$$$$$$ == + == ยท@$$$$$$$@@@@= $$$$@ @$$$$@ยท == + == ยท@$$$$$$@= %$$$$$@x ~$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@=oo*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@% == + ==+x ~$@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+*@@@@$$$$@@@@@~ x+== + ++== o*$@@@$*x ยท=%$@@$%=ยท x*$@@@$*x ==++ + ==== =+== + xx====+= x+*===+= =+====+x =+====xx + ++====*%%%%**=====++====*%*%%*%*====++=====**%%%%*====++ + xx++++====++++ ++++====++++ ++++====++++x+ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_148.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_148.txt new file mode 100644 index 00000000..a36374d7 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_148.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + ++==*%**++~ยท ยท~++**%*==++ + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ x==++ + **++ +%@@@@@@@@@@@@@@@@@@@@%+ ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + ==+~ x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$$x x%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% x$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$o =$@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@$x $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ยท@$$$$$@@+ ยท$$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@=xx%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@%x ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@% ++== + xx==xยท o=***=x ~+****+~ x=***=x ยทx==++ + ====xo xx x+ ox==== + ======+x =+=*====++ ++====*=+= x+*===== + ++==***%********++++==************==++++********%***==++ + ++++++++++ +x++++++++x+ ++++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_149.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_149.txt new file mode 100644 index 00000000..eaf1e09a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_149.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + ++==*%**++~ยท ยท~++**%*==++ + xx**=*+x x+*=**xx + ++*=xo ยทox+====+xoยท ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ยท =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ยท@$$$$$$$@@@$x $$$$@ @$$$$@ยท == + == ยท@$$$$$@@x ยท%$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@*~ x$@@@@@@@@@@$x ยท*@@@@@@@@@@@= ++++ + ox==+o x+=+xยท o+==+o ยทx+=+xยท ~+==+x + ++==+x == == xx==++ + ++====++ ox*+==**==+= =+=***==+*xo ++====++ + ++==**********==++++++************++++++==**********==++ + ++++++++ xx++++xx ++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_150.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_150.txt new file mode 100644 index 00000000..9f440069 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_150.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + +++=****==oยท ยทo==****=+++ + x+=*=*+x x+*=*=+x + ++**+x ~x++==++x~ xx**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **xx x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$@% ยท=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ ยท*@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ยท@$$$$$@@x %$$$$$@x ~$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + ++++ ~%@@@@@@@@@$x ยท*@@@@@@@@@@*ยท x$@@@@@@@@@%o ++++ + ox==+x ~ooยท ~oo~ ยทooo o+==xo + ++==++ +%% %%+o ++==++ + ++=*===*xx ~o++*=**==**+*++ ++*+**==**=*++ ~ xx*===*=++ + ++==********++xx ++==********==++ xx++********==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_151.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_151.txt new file mode 100644 index 00000000..540e3e13 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_151.txt @@ -0,0 +1,41 @@ + + x+++===******====++x + ++==***%=*xo ox==%***==++ + xx=*=*++ ++*=*=xx + ++**++ ~ox++++xo~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$*o ++** + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++=+ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ *@$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$@* +== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@$=ยท $$$$$* x$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ยท@$$$$$@@+ %@$$$$$o ยท$$$$$$@ยท == + == ยท@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + ++++ %@@@@@@@@@%o =$@@@@@@@@$= ~%@@@@@@@@@%ยท ++++ + ==+x ยทยท ยทยท ยทยทยท x+== + x+==++ ++%% %*++ ++*=xx + xx==**=*++xxxx+=*=**==**=*=+xxxx+=%=**==**=*=+xxoo++*=**==xx + ++++******==++xx ++++********++++ xx++==******=+++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_152.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_152.txt new file mode 100644 index 00000000..34e34224 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_152.txt @@ -0,0 +1,41 @@ + + xx++=====**=====++xx + +++=***%**++~ยท ยท~++**%***++++ + xx==*%=+ +=%*==xx + ++**++ ~~oooo~~ ++**++ + ==== ยท+%@@@@@@@@@@@@@@@@@@%+ยท ==== + **+x ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ==+x ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + ox== ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x $$$$$$%ยท o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+ยท =@$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + == @$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$+ ~$$$$$* +$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ยท@$$$$$$@= *$$$$$$ยท %$$$$$@ยท == + == ยท@$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยท+== + +++= =$@@@@@@@@* x%@@@@@@@@%x *@@@@@@@@@* ++++ + ==++ +=== + xx=*=+ ++%%+x x+%%=+ +=**+x + ++**=*=+++++=**=**++**=%==++++==%=**++**=**=+++++=*=**++ + xx++======++++ ++========++ ++++==**==++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_153.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_153.txt new file mode 100644 index 00000000..b81afbdd --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_153.txt @@ -0,0 +1,41 @@ + + xx++============++xx + ++++**%%=*==xo~~~~ox==**%%**++++ + xx==**== ==**==xx + ++**+= ยท~~~~ยท =+**++ + ===+ x*$@@@@@@@@@@@@@@@@$*x +=== + **++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++** + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ox== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + +++x $@$$$@$o x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==+ยท +@$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@%x o$$$$$% =$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@*~ +$$$$$$ *$$$$$@ยท == + == ยท@$$$$$$ ยท*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@+ยท~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ยท+== + x+== +$@@@@@@@$* o%@@@@@@@@%o *$@@@@@@@$= ==++ + ==++ ++== + ==== ==%%++ ++%%== +=== + ++**=%==++++****==++**=***++++***=**++==****++++==%=**++ + +++=====++++ +++======++x ++++=====+++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_154.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_154.txt new file mode 100644 index 00000000..7f4c1c11 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_154.txt @@ -0,0 +1,41 @@ + + ++++++====++++++ + xx+=***%%%**==++++==**%%%***=+xx + ox==**=*xo ox*=**==xo + ++**+= =+**++ + ==== ~+%$@@@@@@@@@@@@@@$%+~ ==== + ===+ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x +=== + ===+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ox== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xo + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + ox== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + ++++ %@$$$@@+~o=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+ยท x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@=ยท =$$$$$$ %$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@$x o$$$$$% +$$$$$@ยท == + == ยท@$$$$$$ x$@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@ยท == + == ยท@$$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + ++== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%== ==== + ++**=***++==%***==++***%**++++**%***++==***%==++**%=**++ + x+++==++++ x+++====+++x ++++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_155.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_155.txt new file mode 100644 index 00000000..0a9d3d09 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_155.txt @@ -0,0 +1,41 @@ + + ++++++++++++++++ + ++*****%*%=******=%*%*****++ + x ++**=*++ x+*=**++ x + ++**+= =+**++ + ====ox o=%@@@@@@@@@@@@@@%=o xo==== + ===+ ~*@@@@@@$$$$$$$$$$$$$$@@@@@@*~ +=== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=== + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xo + ==+x o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ox== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ *@$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==xยท ~@$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยทx== + == @$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %@$$$$$o ยท$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@$=ยท $$$$$* x$$$$$@ยท == + == ยท@$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@* ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== x%@@@@@@@$= ~*@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====o ==%%++ ++%%== ==== + ++***%**====%***++++==***%====%***==++++***%==+=***=**++ + xx++++++++ ++====++ ++++++++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_156.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_156.txt new file mode 100644 index 00000000..d1c6743b --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_156.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==***%*%*%%%%%%*%*%***==++ + ++**=*== ==*=**++ + ++**=*++ ++*=**++ + ++**++ ~+*%@@@@@@@@@@%*+~ ++**++ + ==== o*@@@@@@@$$$$$$$$$$@@@@@@@*o ==== + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + xx==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ox== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xo + ++++ x@$$$$@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%ยท x$$$$$$@*ยท +@$$$$$@ == + == ยท@$$$$$$$@@@*ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$+ $$$$$x @$$$$@ยท == + == ยท@$$$$$$o x%@$$$$$@@*===============$@$$$$$@ยท == + == ยท@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$@@@% ยท+== + ox== x%@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==+x + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + +++++++x ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_157.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_157.txt new file mode 100644 index 00000000..03caabe0 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_157.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==****%%*%%%%%%*%%****==++ + ++****=*++ ++*=****++ + ++=*==+= =+====++ + ++**++ ~+=%$$@@@@$$%=+~ ++**++ + ++==oo +%@@@@@@@@$$$$$$@@@@@@@@%+ oo==++ + ++== o$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$o ==++ + ++==~ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~==++ + ox==+~ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ~+==xo + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ++++ ~@$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++x+ + ==+~ @$$$$$ยท +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$$= *@$$$$$@$xoooooooooooooox*@$$$$$@ == + == $$$$$$$@@@%x $$$$$o @$$$$$ == + == ยท@$$$$$$@@@%x $$$$@o @$$$$@ยท == + == ยท@$$$$$@= =@$$$$$@$xooooooooooooooo*@$$$$$@ยท == + == ยท@$$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== +%@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ++=+ +=++ + ++*=oo ==%%++ ++%%== ==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_158.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_158.txt new file mode 100644 index 00000000..b35f4a8c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_158.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++++==******%%%%******==++++ + ++==**=***++ ++******==++ + ++==**=*+o o+*=**==++ + ++====x ยทo++====++oยท x====++ + ++==++ +%@@@@@@@@@@@@@@@@@@@@%+ ++==++ + ++==xo +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ ox==++ + ++==+~ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ~+==++ + ==+x =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= x+== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==xยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยทx== + ++== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==++ + +++x @$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == *$$$$@% =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ยท =$@$$$$$@@%**************%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ยท@$$$$$@$o ยท$$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ยท+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+=*=***++==%***++xx==*%**====**%*==xx++***%==++***=**+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_159.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_159.txt new file mode 100644 index 00000000..88548ed3 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_159.txt @@ -0,0 +1,41 @@ + + ++xxxx++ + xx++==****************==++xx + ++==**==*%=*++oo oo++*=%*==**==++ + xx++**=*+= =+*=*=++xx + ++====++ ~oooo~ ++====++ + ++===+ +%@@@@@@@@@@@@@@@@@@%+ +===++ + ++==+x ยท*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท x+==++ + x+==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x+==++ + ===+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=== + ++== ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ==++ + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+ยท =@$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยทx== + == @$$$$$$ x%@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@$x o$$$$$* +$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@*ยท =$$$$$$ยท %$$$$$@ยท == + == ยท@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ยท+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==+x + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%*=++++%***++x+==*%**++++**%*==++++***%++++==%=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_160.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_160.txt new file mode 100644 index 00000000..2e212c62 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_160.txt @@ -0,0 +1,41 @@ + + + xx++====********====++xx + ++==**=**%****++++****%**=**==++ + ++=====*+x ++%=====++ + ++====== ======++ + ++====xx o=%$@@@@@@@@@@@@$%=o xx====++ + ++===+ ยท*$@@@@@$$$$$$$$$$$$$$@@@@@$*ยท +===++ + xx===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +===xx + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ *@$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+ยท ~@$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยท+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %$$$$$@x ยท$$$$$$$ == + == ยท@$$$$$$$@@@@x $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@*ยท $$$$$= x$$$$$@ยท == + == ยท@$$$$$$ ~*@@$$$$$@@$$$$$$$$$$$$$$%$@@$$$$$@ยท == + == ยท@$$$$$% ~=@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$@ยท == + == ยท@$$$$$@* ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ===+ +=== + ===+ ++%% %%++ +=== + ++**=%+++x++**%*=+++===*==+xx+==*===+++=*%**++++++****++ + ++++====++ ++++==++ ++====+++x + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_161.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_161.txt new file mode 100644 index 00000000..d96fee85 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_161.txt @@ -0,0 +1,41 @@ + + + xx++++============++++xx + ++====**=**%%%****%%%**=**====++ + ++====**=*o~ ~o*=**====++ + ======++ ++====== + x+====++ ~+*%$@@@@@@@@$%*+~ ++====++ + xx==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ====xx + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + =+== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + x+==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + ++++ x@$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*ยท +@$$$$$@ == + == ยท@$$$$$$@@@@*ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$+ $$$$$+ @$$$$@ยท == + == ยท@$$$$$$x x%@$$$$$@@*++++++++++++++=$@$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + xx++ ยท%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++++ + ==+x ยท~~ ยท~~ยท ~~~ x+== + ==++ x+%% %%++ ++== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ x+++====+++x ++++====++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_162.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_162.txt new file mode 100644 index 00000000..1bac9f41 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_162.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++++++ + ++++==****=***%%%%***=****==++++ + ++++=====%==o~ ~o==%=====++++ + ++====+= =+====++ + =====+ ~x=**%%%%**=x~ +===== + ====++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++==== + ====o~ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*ยท ~o==== + ++==xยท =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ยทx==++ + xx==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+==xx + =+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++= + ox==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx==xo + ++++ @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == %$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$x o%@$$$$$@@=++++++++++++++=$@$$$$$@ == + == $$$$$$$@@$=ยท $$$$@x @$$$$$ == + == ยท@$$$$$$@@@@*ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$@% x@$$$$$@*~ ยท+@$$$$$@ยท == + == ยท@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ยท@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + x+++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x ++++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**==o~ ++*+**++**+*xo ox*+**++**=*++ ~o=+**++ + ++==****++++ ++==****==++ x+++=***==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_163.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_163.txt new file mode 100644 index 00000000..e2828f81 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_163.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%***====++ + ++=====*+x x+*=====++ + ++====xx ยทox+====+xoยท xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + o ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+==xo + ++== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$@= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+== + == *$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ ยท=@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o@$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ยท@$$$$$@$x %$$$$$@x o$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%+=$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@* ++++ + **x~ ~+===+o x====x o+=*=+~ ~+** + **+x ++ ++ x+** + ==**++ ~o*+**==**+= =+**==**+*o~ ++==== + +==****===++ ++=******=++ +++==****==+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_164.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_164.txt new file mode 100644 index 00000000..93246f70 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_164.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=**%=+xo~~~~ox+=%**=****++++ + xx++===*== =+*===++xx + ++====++ ยท~~~~ยท ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +===+x + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$@$o x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==xยท +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยทx== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@%x x$$$$$% =$$$$$$ == + == ยท@$$$$$$$@@@@= $@$$@ @$$$$@ยท == + == ยท@$$$$$@@*~ +$$$$$% *$$$$$@ยท == + == ยท@$$$$$$ ยท=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@xยท~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@@$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@$ยท x+++ + **~ยท ~=%$$$%=o +*$$$$*+ o=%$$$%=~ ** + xx**o~ ~ยท**xx + ox**+* ++****=*+x x+*=****++ *+**xx + ++==*%%%%%**== x++=*%%%%%%*=++x ==**%%%%%*==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_165.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_165.txt new file mode 100644 index 00000000..35acb525 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_165.txt @@ -0,0 +1,41 @@ + + + + x+++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* *=*=*=++ + x+====+= =+====+x + ++===+ ยท+%$@@@@@@@@@@@@@@$%+ยท +===++ + x+==++ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x ++==+x + xx==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++==xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+== + ++== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+ยท x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*ยท =$$$$$$ยท *$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@%x o$$$$$* =$$$$$@ยท == + == ยท@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@x o+++ + ** +%@@@@@%+ o*$@@@@$*o +%@@@@@%+ **xo + x+** **++ + x+**+= ox****== ==****xo =+**++ + ++**%%%%%%%*==+x ++==*%%%%%%%==++ ++==*%%%%%%%**=+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_166.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_166.txt new file mode 100644 index 00000000..1cb8aec8 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_166.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%%%********%%%*****==++ + ++==**=*=+ ++*=**==++ + ++**+= =+**++ + xx====ox ~=%$@@@@@@@@@@@@$%=~ xo====xx + x+===+ ยท=@@@@@@$$$$$$$$$$$$$$@@@@@@=ยท +===+x + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + x+== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==+x + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ =@$$$$@*++$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+ยท ~@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยท+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@x %$$$$$@x ~$$$$$$$ == + == ยท@$$$$$$$@@@@x $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@=~ $$$$$= o@$$$$@ยท == + == ยท@$$$$$$ ~=@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ยท == + == ยท@$$$$$$ ยท*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@* ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== +$@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==$%++ ++$$== oo==++ + x+==*%=*====****++++++*%**====**%*++++++**=*====*=%*==+x + ++++++ ++++++++ ++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_167.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_167.txt new file mode 100644 index 00000000..f61e8743 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_167.txt @@ -0,0 +1,41 @@ + + + + x ++++++===+++++++ x + xx++*******%*%%*%%%*%*******++xx + xx+=****== ==****=+xx + ++**+*x x*+**++ + ====x+ x*%$@@@@@@@@@@$%*x +x==== + ==== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==== + ==++ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + x+==oยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx==xx + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + ox== +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+ยท @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + == @$$$$@$ ยท=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ o@$$$$$@ == + == ยท@$$$$$$$@@@$o $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@*x $$$$@= ~@$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@%***************@@$$$$$@ยท == + == ยท@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$= +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + xx== *@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%ยท +++x + ==++ ยท~ยท ~~ ยท~ยท x+== + ==++ x+%% %%+x ++== + ++**=*++xox+==%*==++===%==xoox==%===++==*%==+xoo++*=**++ + +++++===++ +++==+++ +++===++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_168.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_168.txt new file mode 100644 index 00000000..c37a57b0 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_168.txt @@ -0,0 +1,41 @@ + + + + xx+++++++++++++x + ++==*****%%%%%%%%%%*****==++ + ++**=*=* *=*=**++ + ++**=*++ ++*=**++ + ++**x+ ~+*$$@@@@@@@@$$*+~ +x**++ + ==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ==== + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + ox==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$$$ o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%ยท o@$$$$$@*ยท +@$$$$$@ == + == ยท@$$$$$$$@@@*~ $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$= $$$$@+ @$$$$@ยท == + == ยท@$$$$$$x x%@$$$$$@@*===============$@$$$$$@ยท == + == ยท@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@*o ยท+$@@@@@@@@@@$+ยท o*@@@@@@@@@@@* ++++ + **+~ ~+=*=+o ยทx=**=xยท o+=*=+~ ยท+** + **xx ++ ++ o+**xx + ====++ *=**==**+= =+**==**=* ++==== + +==**%*==+++ ++=******=++ ++=***%%*==+ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_169.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_169.txt new file mode 100644 index 00000000..39e0d902 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_169.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++****%%%%%%%%%%%%****++++ + ++***%*%x~ ~x%*%***++ + ++**==++ ++==**++ + ++**++ ยทx*%$$@@@@@@$$%*xยท ++**++ + ++==~ o*@@@@@@@@$$$$$$$$@@@@@@@@*o ~==++ + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ==xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + x+++ o@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ==x~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x== + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ยท=@$$$$$@ == + == ยท@$$$$$$@@@@=ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$=~ $$$$@x @$$$$@ยท == + == ยท@$$$$$$x o%@$$$$$@@=+++++++++++++++$@$$$$$@ยท == + == ยท@$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@$$$@@@@@=x~o+%@@@@@$$@@@@@%+o~x=@@@@@$$$@@@@$ x+++ + **~ยท ~=%$$$%=o x*$$$$*x o=%$$$%=~ ** + xx**o~ ~~**+x + x+**+= ++******+x x+*=****++ *+**+x + ++==*%%%%%%*== xx==*%%%%%%*==xx ==*%%%%%%*==++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_170.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_170.txt new file mode 100644 index 00000000..cd5c33eb --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_170.txt @@ -0,0 +1,41 @@ + + + + +x++++x+ + ++==***%%%%%%%%%%***==++ + x+==**=*+o o+*=**==+x + ==**++ ++**== + ++**++ x=%$$@@@@@@$$%=x ++**++ + ++**oo ~=@@@@@@@@$$$$$$$$@@@@@@@@=~ oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + ++=* ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ *=++ + ==x~ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ~x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท@$$$$$$@@@$= $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@@*~ $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ == + +++o =@@@$$$$$$$@@@@%**$@@@@$$$$$$@@@@$**%@@@@$$$$$$$@@@= ~+++ + xx== ~*@@@@@@@*o +$@@@@@@$+ o*@@@@@@@*~ ==xo + ++== ==++ + ++**++ =*%%== ==%%*= +x**++ + +=**%%%%%*%=**+x ++***%%%%%%***++ ++**=%*%%%%***=+ + xxxx xxxx xxxx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_171.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_171.txt new file mode 100644 index 00000000..fdaa337a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_171.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++==***%*%%%%%%*%***==++ + xx==***%+x x+%***==+x + ==**++ ++**== + ++**++ o=*%$$@@@@$$%*=o ++**++ + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + xx=* ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท *=+x + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@o @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ยท*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$~ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ยท+== + xx== +$@@@@@@@@= o%@@@@@@@@%o =@@@@@@@@$+ ==+x + ++=+ +=++ + ++==o +=%$++ ++$%=+ o==++ + x+==**=*===*%***++xx++*%*%====%*%*++xx++***%*===***===++ + ++++++xx ++++++++ xx+==+++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_172.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_172.txt new file mode 100644 index 00000000..99d6bf35 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_172.txt @@ -0,0 +1,41 @@ + + + + + ++++***%%$%%%%$%%***++++ + ++***%+x x+%***++ + ++**++ ++**++ + x+**++ o=*%$$@@@@$$%*=o ++**+x + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ +%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@o @$$$$@ยท == + == ยท@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ยท*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$~ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + ++++ ~%@@@@@@@@@$x =@@@@@@@@@@= x$@@@@@@@@@%~ ++++ + ===x ~~~ ยท~~ยท ~~~ x+== + ===+ x+%% %%+x +=== + ++**=%+xยทยท~o==*===++***%=+o~ยท~+=%***++===*==o~ยทยทx+%=**++ + +++====+++ xx++====++xx ++======++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_173.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_173.txt new file mode 100644 index 00000000..3c8dff4a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_173.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%%%***==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ o=%%$$@@@@$$%%=o ++**xx + x+**oo ~=$@@@@@@@$$$$$$$$@@@@@@@$=~ oo**+x + xx** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **xx + xx** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ยท@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ x@@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@@x ++xx + ==+o ~x+xo ยทx++xยท ox+xo o+== + **++ == =* x+** + +=**==oยท ++*=**==**=*+o o+*=**==**=*+x ยทo*+**=+ + ++==****++++ ++==****==++ ++++****==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_174.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_174.txt new file mode 100644 index 00000000..bd1a0a09 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_174.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%%%**==++ + +=***%+o o+****=+ + +=**++ ++**=+ + xx**++ ยทx=%$$@@@@@@$$%=xยท ++**xx + x+**~ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ~**+x + ++** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% +@$$$$$@*~ยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@ == + == ยท@$$$$$$@@@$=ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$=~ $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o ~+@@@@@@@@@@@@+~ o%@@@@@@@@@@@* ++++ + **x~ ~+***=o ยทx=**=xยท o=***+o ยทx** + **+x ++ ++ ox**xx + ====++ *=**==**+= =+**==**=* ++==== + ++=**%%**=++ ++=**%%**=++ ++=**%%*==++xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_175.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_175.txt new file mode 100644 index 00000000..1765bcfb --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_175.txt @@ -0,0 +1,41 @@ + + + + + +++=*%%%%%%%%%%%%%%*=+++ + +=*%=*xยท ยทx*=%*=+ + ++**++ ++**++ + x+**++ ~+*%$@@@@@@@@$%*+~ ++**+x + ++** o*@@@@@@@$$$$$$$$$$@@@@@@@*o **++ + xx** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + xx** o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xx + =*xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx*= + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + =* o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o *= + xx++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++xx + +++~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ยท+@$$$$$@ == + == ยท@$$$$$$@@@@*~ $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$=ยท $$$$@x @$$$$@ยท == + == ยท@$$$$$$x o%@$$$$$@@=++++++++++++++=$@$$$$$@ยท == + == ยท@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@$+~ ยทo*@@@@@@@@@@@@*o ~+$@@@@@@@@@@@% ++++ + **xยท x*%%%*+ o=%%%%=o +*%%%*x ยทo** + xx**xo xx xx ~x**xx + ===*+x *+****==++ ++==****+* o+*=** + ++==**%%%**=++ ++=*%%%%*=++ ++==*%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_176.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_176.txt new file mode 100644 index 00000000..5dbc4d24 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_176.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++=**%*%%%%%%%%%%*%**=++ + x+==*%=* *=%*==+x + ===*++ ++*=== + x+**++ o=%$@@@@@@@@@@$%=o ++**+x + ++** x%@@@@@@$$$$$$$$$$$$@@@@@@%x **++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==++ + x+=* x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x **+x + **xยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* x@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x *= + xx++ +@$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++xx + +++~ @$$$$$ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == @$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$~ o$$$$$$@= x@$$$$$@ == + == ยท@$$$$$$$@@@%o $$$$@ยท @$$$$@ยท == + == ยท@$$$$$$@@%x $$$$@+ ยท@$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@*==============*@@$$$$$@ยท == + == ยท@$$$$@% x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%xo~o=@@@@@@$@@@@@$ x+++ + **oยท ~=%$$$%=~ x*$$$$*x ~=%$$$%=~ ** + x+**o~ ~~**xx + xx**+= ++=***=*+x x+*=***=++ ==**xx + ++==*%%%%%**== ++++*%%%%%%*++++ ==**%%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_177.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_177.txt new file mode 100644 index 00000000..828afeb6 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_177.txt @@ -0,0 +1,41 @@ + + + + x++++++x + +===*%%%%%****%%%%%*===+ + ++===*+= =+*===++ + ===* *=== + x+**x+ ยท+*$@@@@@@@@@@@@$*+ยท xx**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท =*++ + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + **oยท =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ยทo** + ++++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ++++ + =* +@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + x+++ =@$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= +++x + +++ยท ยท@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ยท+++ + == @$$$$@$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ยท%$$$$$@+ o$$$$$$@ == + == ยท@$$$$$$$@@@$o $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@*o $$$$$= o@$$$$@ยท == + == ยท@$$$$$$ยท =@@$$$$$@@%**************%@@$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ยท$@@@@$$$@@@@@*xoo+$@@@@@$$@@@@@$+oox*@@@@@$$$@@@@@~ x+++ + ** ~=%$$$$*o +%$$$$%+ o*$$$$%*o ** + xx**~~ **++ + ++**+= ++==**==x~ ~x==**==++ =+**+x + ++==*%%%%%%*== x+==*%%%%%%*==++ ==**%%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_178.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_178.txt new file mode 100644 index 00000000..30b75b9a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_178.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++***%%%==++++==%%%***++++ + ++**=*+o o+*=**++ + x+**== ==**+x + ++** x*$@@@@@@@@@@@@@@$*x **++ + ++*+ o%@@@@@@$$$$$$$$$$$$$$@@@@@@%o +*++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + == *@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + xx++ *@$$$$@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + ==+ยท o@$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ยท+== + == @$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@= *$$$$$$~ %$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@$+ ~$$$$$* +@$$$$@ยท == + == ยท@$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@ยท == + == ยท@$$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@%ยท o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xo+*@@@@@$$$@@@@@o x+++ + ** o*$$@$$%x ยท=%$@@$%=ยท x%$@@$$*x ** + x+** **+x + xx**+= xx****== ==***=xx =+**++ + ++==*%%%%***++xx ++==*%*%%*%*==++ xx++*%*$%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_179.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_179.txt new file mode 100644 index 00000000..157ef6e5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_179.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++==*%*%==++xxxx++==%*%*==++ + ++**=* *=**++ + ++**+= =+**++ + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +=++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + x+++ %@$$$@@x ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% +++x + ==+ยท x@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+== + == @$$$$$$ ยท=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*~ +$$$$$% *$$$$$$ == + == ยท@$$$$$$$@@@@= $@$$@ @$$$$@ยท == + == ยท@$$$$$$@%o x$$$$$% =$$$$$@ยท == + == ยท@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$o +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** x*$$@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+++ ++==*%*%%*%*==++ +++=*%*%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_180.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_180.txt new file mode 100644 index 00000000..01fa033f --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_180.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**++ ยทoxx++xxoยท ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==x~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x =$$$$@$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$= x== + == @$$$$$$ o*@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$= ยท$$$$$* x@$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@ยท == + == ยท@$$$$$@@+ *$$$$$$o %$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o o+++ + ** x*$@@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **++ + x+**+= x+****== ==****xx =+**++ + ++***%%%%*%*==++ ++==*%*%%*%*==++ x+==*%*%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_181.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_181.txt new file mode 100644 index 00000000..ea015b62 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_181.txt @@ -0,0 +1,41 @@ + + + + ++++=***%%%%**==++++ + ++==*%**+x x+**%*==++ + ++**=* *=**++ + ==== ox=******=xo ==== + **++ ยท=$@@@@@@@@@@@@@@@@@@@@$=ยท ++** + **+o =@@@@@$$$$$$$$$$$$$$$$$$$$@@@@@= o+** + **x~ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ~x** + +++x *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+++ + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++o @$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@$ +%@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$% == + == @$$$$$$~ +$@$$$$$@@%==============*@@$$$$$@ == + == $$$$$$$@@%x $$$$@+ ยท@$$$$$ == + == ~@$$$$$$$@@@%o $$$$@ยท @$$$$@~ == + == ยท@$$$$$@$~ o$$$$$$@= x@$$$$$@ยท == + == ยท@$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** o*$$@@$%x ยท=%$@@$%=ยท x%$@@$$*x ** + x+** **+x + ++**+= xx****== ==****xx =+**++ + ++==*%%%%*%*=+xx ++==*%%%%%%*==++ xx++*%*%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_182.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_182.txt new file mode 100644 index 00000000..3545521c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_182.txt @@ -0,0 +1,41 @@ + + + + ++++=**%%%%%%%%%%**=++++ + +=***%+x x+%***=+ + ++**+= =+**++ + ox**+= o=*%$@@@@@@$%*=o =+**xo + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**++ + x+** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **+x + ox** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@x @$$$$@ยท == + == ยท@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@ยท == + == ยท@$$$$@% ยท=@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$~ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ยท$@@@@$$$@@@@@*xox=$@@@@$$$$@@@@$=oox*@@@@@$$$@@@@@~ x+++ + ** o*%$$$$*x ยท+%$$$$%+ยท x*$$$$$*o ** + x+**~ **+x + x+**+= +x*=**==x ==**==x+ =+**++ + ++==*%*%%%**++xx ++==*%%%%%%*==++ xx++**%%%*%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_183.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_183.txt new file mode 100644 index 00000000..5368c629 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_183.txt @@ -0,0 +1,41 @@ + + + ++++ + ++==*%%%*%****%*%%%*==++ + x+==*%== ==%*==+x + ===*xo ox*=== + ++**x+ x*%$@@@@@@@@@@$%*x +x**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==+x + xx== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + **oยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทo** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ** +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + xx++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++xx + +++~ @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+++ + == @$$$$$$ ยท=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ x@$$$$$@ == + == ยท@$$$$$$$@@@$o $$$$@ยท @$$$$@ยท == + == ยท@$$$$$$@@%o $$$$$+ ~@$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@%**************%@@$$$$$@ยท == + == ยท@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$$@@@@@*xoo+%@@@@@$$@@@@@%+o~x=@@@@@$$@@@@@$ยท x+++ + **o ~=%$$$%*o +%$$$$%+ o*%$$$%=~ ** + x+**~~ ~**+x + x+**== ++==**==+o ox==**==++ =+**++ + ++==*%%%%%**++xx ++==*%%%%%%*==++ x+++**%%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_184.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_184.txt new file mode 100644 index 00000000..f92a332b --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_184.txt @@ -0,0 +1,41 @@ + + + x ++++====++++ + ++==*%*%==++++++++==%*%*==++ + ++**=* *=**++ + x+**+= ยทยท =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++++ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ++++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$@@x ยท+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+ยท +@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+== + == @$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@*o +$$$$$% *$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@%o x$$$$$% =$$$$$@ยท == + == ยท@$$$$$$ =@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$o +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= == + ++++ %@@@@@@@@@@@$+oยท~x*@@@@@@@@@@@@*oยทยท~+$@@@@@@@@@@@% x+++ + **xยท +*%%%*+ยท o=%%%%=o ยท+%%$%*+ ยทo** + x+**o~ oo oo oo**++ + x+**+=+~ =+******+x x+******+= ~x=+**++ + ++==**%%%***++xx ++==***%%***==++ xx++***%%%**==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_185.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_185.txt new file mode 100644 index 00000000..66aaba24 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_185.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ยทoxx++xxoยท +x**++ + ===+ o*$@@@@@@@@@@@@@@@@@@$*o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@= ยท$$$$$* x$$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ยท@$$$$$@@+ *$$$$$$o ยท$$$$$$@ยท == + == ยท@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@%xยท ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++++ + **xยท o=*%%=x ~+*%%*+~ x=%%%=x ยทx** + x+**xo ++ ++ ox**+x + x+**=*xx *+****=*++ ++==****+* x+==**++ + ++==********++xx ++==********==++ xx++********==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_186.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_186.txt new file mode 100644 index 00000000..d49b01d1 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_186.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==oยท ยทo==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + x+**xx x*@@@@@@@@@@@@@@@@@@@@@@*x xx**+x + xx**~~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~**xx + **xยท *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ยทo** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + xx== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@ == + == $$$$$$$@@@=~ $$$$$x @$$$$$ == + == ยท@$$$$$$@@@@= $$$$@~ @$$$$@ยท == + == ยท@$$$$$@% +@$$$$$@%~ยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@ยท == + == ยท@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@=~ x$@@@@@@@@@@$x ยท=@@@@@@@@@@@+ ++++ + **+~ o+=+xยท o+==+o ยทx+=+x ~x** + xx**+x == == x+**+x + xx**==+= ~x*=******+* *+******=*x~ =+==**xx + ++==********++ x+++********+++x ++********==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_187.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_187.txt new file mode 100644 index 00000000..a3fe2f9c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_187.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + xx**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%oยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@ == + == ยท$$$$$$$@@@$= $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@$*o $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$=xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@ x== + ++=+ o$@@@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+x ยทoxo~ ~oo~ ~oxoยท o+*= + x+**+x =* *= ++**xx + x+==**+*+o ++*=**==**=*+x x+*=**==**=*++ ~+*=**==xx + ++++******==++ ++++********++++ ++==******++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_188.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_188.txt new file mode 100644 index 00000000..383a1143 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_188.txt @@ -0,0 +1,41 @@ + + xx++++xx + ===*%%%%*%****%*%%%%*=== + ++**=%++ ++%=*=++ + ==** **== + x+**+x ~+*$@@@@@@@@@@@@$*+~ x+**+x + ++== =$@@@@@$$$$$$$$$$$$$$@@@@@$= ==++ + ++*= ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ =*++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==+x + **o =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= o*= + ++++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ++++ + == =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= == + x+++ =@$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++xx + +++ยท ยท@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ยท+++ + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == $$$$$$@@x %$$$$$@x ~$$$$$$$ == + == ยท@$$$$$$$@@@$x $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@*~ $$$$$= o$$$$$@ยท == + == ยท@$$$$$$ =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ยท == + == ยท@$$$$@% =@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@* =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยท+== + ++++ =@@@@@@@@@*ยท x$@@@@@@@@$x *@@@@@@@@@= ++++ + ==++ +=== + xx=*=+ ++%%xx xx%%++ +===xx + ==**=*==++++*=*=**==**=%=*++++*=%***==**=*=*++++==*=**==xx + ++++==****==++ ++==****==++ ++==****==++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_189.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_189.txt new file mode 100644 index 00000000..6fedc559 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_189.txt @@ -0,0 +1,41 @@ + + xx++++++++xx + x+==*%*%*%========%*%*%*=++x + ++**=*+x x+*=**++ + xx**=* ==**xx + ++**o x*$@@@@@@@@@@@@@@$*x o**++ + ++=+ o*@@@@@$$$$$$$$$$$$$$$$@@@@@*o +=++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + x+== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==+x + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + =* *@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* *= + ++++ *@$$$@@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+ยท o@$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ยท+== + == @$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@= *$$$$$$~ %$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@$+ ~$$$$$* +$$$$$@ยท == + == ยท@$$$$$$ o%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ยท == + == ยท@$$$$@$ o%@@@@$$$$$$$$$$@$$$$$$$$$$$$@$$$$$$$$@ยท == + == ยท@$$$$$$% o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @@$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$%%@@@@$$$$$$$$@@@@%%$@@@@$$$$$$$@@@* ~+== + ++== o%@@@@@@@$x ยท*@@@@@@@@*ยท x$@@@@@@@%x ==++ + ===+ +=== + ox====o+ ==%%++ ++%%== +o====+x + ++**=**%****%***==++**=*=******=*=**++==***%****%*****++ + xx+++=====++++ ++++====++++ ++++======+++x + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_190.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_190.txt new file mode 100644 index 00000000..95209a1f --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_190.txt @@ -0,0 +1,41 @@ + + xx++++====++++xx + ++==*%**==++oooo++==**%*==++ + ++**=* *=**++ + ++**+= ยทยทยทยท =+**++ + ++== x=$@@@@@@@@@@@@@@@@$=x ==++ + ==++ =$@@@@$$$$$$$$$$$$$$$$$$@@@@$= ++== + ===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +=== + ++=+ ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท +=++ + ox** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ **xo + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$@$x +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+ยท +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+== + == @$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@%o x$$$$$% =$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@%o +$$$$$% *$$$$$@ยท == + == ยท@$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$x ยท+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + ==+x ~@@@@$$$$$@@@@%+x+=$@@@@$$$$@@@@$=+x+%@@@@$$$$$@@@@o o+== + xx== x*$@@@$%x ~=$@@@@$=~ x%$@@@$*x ==xx + ==== ==== + ====+= xx*=**== ==**=*xx =+==== + ++*****%*%%*****==++==***%*%%*%***==++==*****%%%%*****++ + ++++++++++++ x++++++++++x ++++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_191.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_191.txt new file mode 100644 index 00000000..7da0266e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_191.txt @@ -0,0 +1,41 @@ + + ++====****====++ + ==**%%=*++ooยทยทยทยท~o++*=%%**++ + +=**== ==**=+ + ++**++ ยท~~oo~~ยท ++**++ + ===+ +%@@@@@@@@@@@@@@@@@@%+ +=== + ===+ ยท*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท ++== + ===x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x=== + ++=+ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xx + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + == $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + +++x $$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==xยท =@$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยทx== + == @$$$$$$ x$@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@ == + == $$$$$$$@$x o$$$$$% +$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@=ยท =$$$$$$ %$$$$$@ยท == + == ยท@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@+~o=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@= == + ==++ *@@@@@@@@@@@$+~ o*@@@@@@@@@@@@*o ยท+$@@@@@@@@@@@% x+== + ++==xยท x=%%%*x o=*%%*=o x*%%%*x ยทx==++ + ====oo xx xx ~o==== + ====+=++ =+*==*==++ ++=====*+= ox=+==== + ++==**=*********++++==***%****%***==++++*********=**==++ + ++++++++++xx ++++++++++++ xx++++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_192.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_192.txt new file mode 100644 index 00000000..f03d9280 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_192.txt @@ -0,0 +1,41 @@ + + ++====****====++ + +=***%==+o o+==%***=+ + ===*++ ++*=== + ++**x+ ยทooxxxxooยท ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ x+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ =@$$$@$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$@= +== + == @$$$$$$ o%@@$$$$$@@$$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$=ยท ยท$$$$$* x$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$@@+ *$$$$$$o $$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@=x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@ x== + ++++ o$@@@@@@@@@$+ ยท*@@@@@@@@@@*ยท +$@@@@@@@@@$o ++++ + x+==+x ยทooo~ ~oo~ ~oooยท o+==+x + ++==++ +** **+ x+==++ + ++====+=+x ++*========*+x x+*========*++ x+======++ + ++++==********==++++++==********==++++++==********====++ + ++++++++ ++++++++ ++++++++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_193.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_193.txt new file mode 100644 index 00000000..eea662ac --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_193.txt @@ -0,0 +1,41 @@ + + ++====****====++ + x++=***%==x~ ~x==%***=+xx + x+===*++ ++*===+x + ++**+x ~ox++++xo~ x+**++ + ==++ x*$@@@@@@@@@@@@@@@@@@$*x ++== + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x x+** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + x+== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$@% ~=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@=~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ยท@$$$$$@@x %@$$$$$x ~$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*++%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยทx== + ++++ =@@@@@@@@@* x$@@@@@@@@$x *@@@@@@@@@= ++++ + ox==++ ++==xx + ++===+ ++%%+o o+%%++ +===++ + ++=====%+=++++*=*========***++++***========*=*++++=+%=====++ + xx++==******====++ ++==********==++ ++====******==++xx + ++++ ++++ ++++xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_194.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_194.txt new file mode 100644 index 00000000..1d121344 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_194.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + x+==*%**=+~ยท ยท~+=**%*==++ + x+=*=*+x x+*=*=+x + ++**xo ยทox+====+xoยท ox**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x o+** + ==+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+== + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% =@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ยท@$$$$$@@x %$$$$$@x ~$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@$ == + ==+o +@@@$$$$$$$@@@$***%@@@@$$$$$$@@@@%***$@@@$$$$$$$@@@+ o+== + ++== =$@@@@@$*~ +%@@@@@@%+ ~*$@@@@@$=ยท ==++ + ===+ +=== + ++====x+ *=**== =+**=* +x====+x + ++=====**%*****==========**%****%**=========******%**=====+x + ++++========++xx ++++========++++ xx++========++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_195.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_195.txt new file mode 100644 index 00000000..7f66e314 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_195.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + x+==*%**++~ยท ยท~+=**%*==++ + xx**=%+x x+%=**xx + ++*=x ~ox+====+xo~ ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ยท =$@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ยท@$$$$$$$@@@$x $$$$@ @$$$$@ยท == + == ยท@$$$$$@@o ยท%$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@% == + ==+x ~$@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+*@@@@$$$$@@@@@~ x+== + ++== o*$@@@$*x ยท=%$@@$%=ยท x*$@@@$*x ==++ + ==== =+== + xx====+= x+*===== ======+x =+====xx + ++====*%%%%**=====++====*%*%%*%*====++=====**%%%%*====++ + xx++++====++++ ++++====++++ ++++====++++x+ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_196.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_196.txt new file mode 100644 index 00000000..9f6a2941 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_196.txt @@ -0,0 +1,41 @@ + + ++++==********===+++ + x+==*%**++~ยท ยท~++**%*==++ + xx**=%+x x+%=**xx + ++==x ~ox+====+xo~ x==++ + **++ +%@@@@@@@@@@@@@@@@@@@@%+ ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + ==+~ x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x ~+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++x @$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ยท =$@$$$$$@@%%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ยท@$$$$$@@o ยท$$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ==++ *@@@@@@@@@@@%x ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@% ++== + xx==+ยท o=***=x ~+****+~ x=***=x ยทx==++ + ====xo xx x+ ox==== + ======+x =+=*====++ ++====*=+= x+*===== + ++==***%********++++==************==++++********%***==++ + ++++++++++ +x++++++++x+ ++++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_197.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_197.txt new file mode 100644 index 00000000..d7b48f2c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_197.txt @@ -0,0 +1,41 @@ + + x+++==********===+xx + x+==*%**++~ยท ยท~++**%*==++ + xx**=*+x x+*=**xx + ++**xo ยทox+====+xoยท ox=*++ + **++ x%@@@@@@@@@@@@@@@@@@@@%x ++** + **+o +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ o+** + =*+o x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x o+*= + ++++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++++ + ox== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xo + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% =@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ยท =$@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ยท@$$$$$$$@@@$x $$$$@ @$$$$@ยท == + == ยท@$$$$$@@o ยท%$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@*~ x$@@@@@@@@@@$x ยท*@@@@@@@@@@@= ++++ + ox==+o x+=+xยท o+==+o ยทx+=+xยท ~+==+x + ++==+x == == xx==++ + ++====++ ox*+==**==+= =+=***==+*xo ++====++ + ++==**********==++++++************++++++==**********==++ + ++++++++ xx++++xx ++++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_198.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_198.txt new file mode 100644 index 00000000..4195da5e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_198.txt @@ -0,0 +1,41 @@ + + xx++==********===+xx + x++=****==xยท ยทx==****=+++ + x+=*=*+x x+*=*=+x + ++**+x ~x++==++x~ x+**++ + **++ x*@@@@@@@@@@@@@@@@@@@@*x ++** + **+x x%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%x xx** + ==+o o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o o+== + ++++ +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ++++ + xx== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==xx + ==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% ยท=@@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* x== + == @$$$$$$ ยท*@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ยท@$$$$$@@x %$$$$$@x ~$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*+=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + ++++ ~%@@@@@@@@@$x ยท*@@@@@@@@@@*ยท x$@@@@@@@@@%o ++++ + ox==+x ~ooยท ~oo~ ยทooo x+==xo + ++==++ +%% %%+o ++==++ + ++=*===*xx ~ ++*=**==**+*++ ++*=**==**=*++ ~ xx*===*=++ + ++==********++xx ++==********==++ xx++********==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_199.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_199.txt new file mode 100644 index 00000000..d926eed9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_199.txt @@ -0,0 +1,41 @@ + + x+++===******====++x + x+==***%=*xo ox==%***==++ + xx=*=*++ ++*=*=xx + ++**++ ~ox++++xo~ ++**++ + **++ o*$@@@@@@@@@@@@@@@@@@$*o ++** + **+x o%@@@@$$$$$$$$$$$$$$$$$$$$@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==+ *@$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$@* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@$=ยท $$$$$* x$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$@@+ %@$$$$$o ยท$$$$$$@ยท == + == ยท@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==+ยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + ++++ %@@@@@@@@@%o =$@@@@@@@@$= ~%@@@@@@@@@%ยท ++++ + ==+x ยทยท ยทยท ยทยทยท x+== + x+==++ ++%% %*++ ++*=xx + xx==**=*++xxxx+=*=**==**=%=+xxxx+=*=**==**=*=+xxoo++*=**==xx + ++++******==++xx ++++********++++ xx++==******==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_200.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_200.txt new file mode 100644 index 00000000..86ebca6d --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_200.txt @@ -0,0 +1,41 @@ + + xx++=====**=====++xx + +++=***%**++~ยท ยท~++**%***++++ + xx==*%=+ +=%*==xx + ++**++ ~~oooo~~ ++**++ + ==== ยท+%@@@@@@@@@@@@@@@@@@%+ยท ==== + **+x ~*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*~ x+** + ==+x ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท x+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + ox== ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x $$$$$@%ยท o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==+ยท =@$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + == @$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$+ ~$$$$$* +$$$$$$ == + == ~@$$$$$$$@@@@+ $$$$@ @$$$$@~ == + == ยท@$$$$$$@= *$$$$$$ยท %$$$$$@ยท == + == ยท@$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@@@@@@@$$$$$$$$@@@@@@@@@@$$$$$$$$$@@$ ยท+== + +++= =$@@@@@@@@* x%@@@@@@@@%x *@@@@@@@@@* ++++ + ==++ +=== + xx=*=+ ++%%+x x+%%++ +=**+x + ++**=*=+++++=**=**++**=%==++++==%=**++**=**=+++++=*=**++ + xx++======++++ ++========++ ++++==**==++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_201.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_201.txt new file mode 100644 index 00000000..65a82d3b --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_201.txt @@ -0,0 +1,41 @@ + + xx++============++xx + ++++**%%**==xo~~~~ox==**%%**++++ + xx==**== ==**==xx + ++**+= ยท~~~~ยท =+**++ + ===+ x*$@@@@@@@@@@@@@@@@$*x +=== + **++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++** + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++=+ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ +=++ + xx== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ox== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==xo + +++x $@$$$@$o x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==+ยท +@$$$$$ +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@%x o$$$$$% =$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@*~ +$$$$$$ *$$$$$@ยท == + == ยท@$$$$$$ ยท*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@+ยท~=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ยท+== + x+== +$@@@@@@@$* o%@@@@@@@@%o *$@@@@@@@$= ==++ + ==++ ++== + ===+ ==%%++ ++%%== +=== + ++**=%==++++****==++**=***++++***=**++==****++++==%***++ + ++======++++ x++======+++ ++++======++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_202.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_202.txt new file mode 100644 index 00000000..d0f7f0f4 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_202.txt @@ -0,0 +1,41 @@ + + ++++++====++++++ + xx+=***%%%**==++++==**%%%***=+xx + ox==**=*x ox*=**==xo + ++**+= =+**++ + ==== ~+%$@@@@@@@@@@@@@@$%+~ ==== + ===+ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x +=== + ===+ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ +=== + ++=+ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% +=++ + ox== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==xo + ==+x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+== + ox== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==xo + ++++ %@$$$@@+~o=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+ยท x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@=ยท =$$$$$$ %$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@$x o$$$$$% +$$$$$@ยท == + == ยท@$$$$$$ x$@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@ยท == + == ยท@$$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + ++== +$@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xx + ===+ +=== + ==== ==%%++ ++%%+= ==== + ++**=***++==%***==++***%**++++**%***++==***%==++**%=**++ + x+++==++++ x+++====+++x ++++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_203.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_203.txt new file mode 100644 index 00000000..d3e83734 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_203.txt @@ -0,0 +1,41 @@ + + ++++++++++++++++ + ++*****%*%=******=%*%*****++ + ++**=*++ x+*=**++ x + ++**+= =+**++ + ====ox o=%@@@@@@@@@@@@@@%=o xo==== + ===+ ~*@@@@@@$$$$$$$$$$$$$$@@@@@@*~ +=== + ===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +=== + ++== *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ==++ + ox== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xo + ==+x o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o x+== + ox== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xo + ++++ *@$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==xยท ~@$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยทx== + == @$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %@$$$$$o ยท$$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@$=ยท $$$$$* x$$$$$@ยท == + == ยท@$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@* ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== x%@@@@@@@$= ~*@@@@@@@@*~ =$@@@@@@@$+ ==xx + ===+ +=== + ====o ==%%++ ++%%== ==== + ++***%**====%***++++==***%====%***==++++***%====***=**++ + xx++++++++ ++====++ ++++=+++xx + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_204.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_204.txt new file mode 100644 index 00000000..4333a6ac --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_204.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==***%*%*%%%%%%*%*%***==++ + ++**=*== ==*=**++ + ++**=*++ ++*=**++ + ++**++ ~+*%@@@@@@@@@@%*+~ ++**++ + ==== o*@@@@@@@$$$$$$$$$$@@@@@@@*o ==== + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + ++== x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ==++ + xx==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==xx + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ox== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xo + ++++ x@$$$$@$*%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%ยท x$$$$$$@*ยท +@$$$$$@ == + == ยท@$$$$$$$@@@*ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$+ $$$$$x @$$$$@ยท == + == ยท@$$$$$$o x%@$$$$$@@*===============$@$$$$$@ยท == + == ยท@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ *@@@$$$$$$$@@@@$$$@@@@$$$$$$$$@@@@$$$@@@@$$$$$$$@@@% ยท+== + xx== x%@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==+x + ++=+ +=++ + ++==ox ==%%++ ++%%== o==++ + ++***%**====%***++++==***%====%***==++++***%====**%***++ + +++++++x ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_205.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_205.txt new file mode 100644 index 00000000..ce0f1cf9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_205.txt @@ -0,0 +1,41 @@ + + xx++++++++++++xx + ++==****%%*%%%%%%*%%****==++ + ++****=*++ ++*=****++ + ++====+= =+====++ + ++**++ ~x=%$$@@@@$$%=+~ ++**++ + ++==oo +%@@@@@@@@$$$$$$@@@@@@@@%+ oo==++ + ++== o$@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@$o ==++ + ++==~ %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% ~==++ + ox==+~ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ~+==xo + ++++ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ++++ ~@$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + ==+~ @$$$$$ยท +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ =$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$$= *@$$$$$@$xoooooooooooooox*@$$$$$@ == + == $$$$$$$@@@%x $$$$$o @$$$$$ == + == ยท@$$$$$$@@@%x $$$$@o @$$$$@ยท == + == ยท@$$$$$@= =@$$$$$@$xooooooooooooooo*@$$$$$@ยท == + == ยท@$$$$@$ +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ยท +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== +%@@@@@@@$= ~%@@@@@@@@%~ =$@@@@@@@$+ ==xo + ++=+ +=++ + ++*=ox ==%%++ ++%%== o==++ + x+***%**====%***++++==***%====%***==++++***%====**%***++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_206.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_206.txt new file mode 100644 index 00000000..6c1c344c --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_206.txt @@ -0,0 +1,41 @@ + + ++++++++++++ + ++++==*******%%*******==++++ + ++==**=***++ ++***=**==++ + ++==**=*+o o+*=**==++ + ++====x ยทo++====++oยท ====++ + ++==++ +%@@@@@@@@@@@@@@@@@@@@%+ ++==++ + ++==xo +$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$+ ox==++ + ++==+~ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x ~+==++ + ==+x =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= x=== + ++== x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ==++ + ==xยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยทx== + ++== @$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==++ + +++x @$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + == *$$$$@% =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ยท =$@$$$$$@@%**************%@@$$$$$@ == + == $$$$$$$@@*o $$$$$= ~$$$$$$ == + == ~@$$$$$$$@@@$x $$$$@ @$$$$@~ == + == ยท@$$$$$@$o ยท$$$$$$@+ o$$$$$$@ยท == + == ยท@$$$$$$ =@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@@$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@$ ยท+== + xx== +$@@@@@@@$= o%@@@@@@@@%o =$@@@@@@@$= ==xx + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+=*=***++==%***++xx==*%**====**%*==xx++***%==++***=**+x + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_207.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_207.txt new file mode 100644 index 00000000..167764b9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_207.txt @@ -0,0 +1,41 @@ + + ++xxxx++ + xx++==****************==++xx + x+==**==*%=*++oo oo++*=%*==**==++ + xx++**=*+= =+*=**++xx + ++====++ ~oooo~ ++====++ + ++===+ +%@@@@@@@@@@@@@@@@@@%+ +===++ + ++==+x ยท*@@@@@$$$$$$$$$$$$$$$$$$@@@@@*ยท x+==++ + x+==+x %@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@% x+==+x + ==++ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o +=== + ++== ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ==++ + ==+~ *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ~+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ x+++ + ==xยท =@$$$@$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ยท+== + == @$$$$$$ x%@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@$x o$$$$$* +$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@*ยท =$$$$$$ยท %$$$$$@ยท == + == ยท@$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท $@@$$$$$$$$$@@@@$@@@@@$$$$$$$$@@@@@$@@@@$$$$$$$$$@@$ ยท+== + xx== =$@@@@@@@@* o%@@@@@@@@%o *@@@@@@@@$= ==+x + ++=+ +=++ + ++== ==%%++ ++%%== ==++ + x+**=%*=++++%***++++==*%**++++**%*==++++***%++++==%=**++ + ++++++++ ++++++++ ++++++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_208.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_208.txt new file mode 100644 index 00000000..3d3c0e0e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_208.txt @@ -0,0 +1,41 @@ + + + xx++====********====++xx + ++==**=**%****++++****%**=**==++ + ++=====%+x x+*=====++ + ++====== ======++ + ++====xx o=%$@@@@@@@@@@@@$%=o xx====++ + ++===+ ยท*$@@@@@$$$$$$$$$$$$$$@@@@@$*ยท +===++ + xx===+ o$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$o +===xo + ==++ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ++== + ++== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==++ + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ *@$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++++ + ==+ยท ~@$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยท+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@+ %$$$$$@x ยท$$$$$$$ == + == ยท@$$$$$$$@@@@x $@$$@ @$$$$@ยท == + == ยท@$$$$$$@@*ยท $$$$$= x$$$$$@ยท == + == ยท@$$$$$$ ~*@@$$$$$@@$$$$$$$$$$$$$$%$@@$$$$$@ยท == + == ยท@$$$$$% ~=@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$@ยท == + == ยท@$$$$$@* ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยท+== + xx== *@@@@@@@@@%~ +$@@@@@@@@$+ ~%@@@@@@@@@* ==xx + ===+ ++== + ===+ ++%% $$++ +=== + ++**=%+++x++****=+++===*==++x+==*===+++=*%**++++++****++ + ++++====++ ++====++ ++====+++x + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_209.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_209.txt new file mode 100644 index 00000000..ad526811 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_209.txt @@ -0,0 +1,41 @@ + + + xx++++============++++xx + ++====**=**%%%****%%%**=**====++ + ++====**=*o~ ~o*=**====++ + ======++ ++====== + x+====++ ~+*%$@@@@@@@@$%*+~ ++====++ + xx==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ====xx + ==== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==== + =+== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + x+==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==+x + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ==xx + ++++ x@$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$@$ o%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*ยท +@$$$$$@ == + == ยท@$$$$$$@@@@*ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$+ $$$$$+ @$$$$@ยท == + == ยท@$$$$$$x x%@$$$$$@@*++++++++++++++=$@$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + xx++ ยท%@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%~ ++++ + ==+x ยท~~ ยท~~ยท ~~~ x+== + ==++ x+%% %%++ +=== + ++**=%++ooox==*===++**=%++xoox++%=**++===*==xooo++%=**++ + ++++==++++ ++++====+++x ++++==++++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_210.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_210.txt new file mode 100644 index 00000000..d2748c70 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_210.txt @@ -0,0 +1,41 @@ + + + xx++++++++++++++++++ + ++++==****=***%%%%***=****==++++ + ++++=====%==o~ ~o==%=====++++ + ++====+= =+====++ + =====+ ~x=**%%%%**=x~ +===== + ====++ o*$@@@@@@@@@@@@@@@@@@@@$*o ++==== + ====o~ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@*ยท ~o==== + ++==xยท =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ยทx==++ + xx==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+==xx + =+++ *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* +++= + ox==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx==xo + ++++ @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ++++ + ==+o @$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+== + == %$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$x x%@$$$$$@@=++++++++++++++=$@$$$$$@ == + == $$$$$$$@@$=ยท $$$$@x @$$$$$ == + == ยท@$$$$$$@@@@*ยท $$$$@~ @$$$$@~ == + == ยท@$$$$$@% x@$$$$$@*~ ยท+@$$$$$@ยท == + == ยท@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ยท@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + x+++ x$@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@$x ++++ + ==+o ox++o ~x++x~ o++xo o+== + **+x ** ** x+** + ++**==o~ ++*+**++**+*xo ox*=**++**=*++ ~o=+**=+ + ++==***=++++ ++==****==++ x+++=***==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_211.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_211.txt new file mode 100644 index 00000000..cb731675 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_211.txt @@ -0,0 +1,41 @@ + + + ++++++++++++++++ + ++++==****************==++++ + ++===****%++~~ ~~++%***====++ + ++=====*+x x+*=====++ + ++====xx ยทox+====+xoยท xx====++ + ====++ x%$@@@@@@@@@@@@@@@@@@$%x ++==== + ++==+x x$@@@@$$$$$$$$$$$$$$$$$$$$@@@@$x x+==++ + ++==+o x$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$x o+==++ + ox==++ =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ++==xo + +++= x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x =+++ + ox==+ยท $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ยท+==xo + ++== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+x @$$$$@= =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+== + == *$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$* == + == @$$$$$$ ยท=@@$$$$$@@$%%%%%%%%%%%%%%%@@$$$$$@ == + == $$$$$$$@@*~ $$$$$= o@$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ยท@$$$$$@$x %$$$$$@x o$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@%+=$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o +$@@@@@@@@@@$+ o%@@@@@@@@@@@* ++++ + **+~ ~+===+o x====x o+=*=+~ ~+** + **+x ++ ++ xx** + ==**++ ~o*+**==**+= =+**==**=*o~ ++==== + +==****==+++ ++=******=++ ++==*****==+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_212.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_212.txt new file mode 100644 index 00000000..c082df71 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_212.txt @@ -0,0 +1,41 @@ + + + ++++++++ + ++++==************==++++ + ++++****=***=+xo~~~~ox+=%**=****++++ + xx++===*== =+*===++xx + ++====++ ยท~~~~ยท ++====++ + ++===+ x*$@@@@@@@@@@@@@@@@$*x +===++ + ++==++ =@@@@@$$$$$$$$$$$$$$$$$$@@@@@= ++==++ + x+===+ *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* +===xx + ==++ ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ++== + ++== @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==++ + ==+o *@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* o+== + ++== $@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ==++ + +++x $@$$$@$o x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ x+++ + ==xยท +@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยทx== + == @$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@%x x$$$$$% =$$$$$$ == + == ยท@$$$$$$$@@@@= $@$$@ @$$$$@ยท == + == ยท@$$$$$@@*~ +$$$$$% *$$$$$@ยท == + == ยท@$$$$$$ ยท=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@xยท~=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@* == + +++x $@@@@@$$@@@@@*x~o+%@@@@@$$@@@@@%+o~o=$@@@@$$$@@@@$ยท x+++ + **~ยท ~=%$$$%=o +*$$$$*+ o=%$$$%=~ ** + xx**o~ ~ยท**xx + xx**+* ++****=*+x x+*=****++ *+**xx + ++==*%%%%%**== x++=*%%%%%%*++++ ==**%%%%%*==++ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_213.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_213.txt new file mode 100644 index 00000000..61e12187 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_213.txt @@ -0,0 +1,41 @@ + + + + x+++====********====++++ + ++==**=**%**++++++++**%**=**==++ + ++=*=*=* *=*=*=++ + x+====+= =+====++ + ++===+ ยท+%$@@@@@@@@@@@@@@$%+ยท +===++ + x+==++ x%@@@@@$$$$$$$$$$$$$$$$@@@@@%x ++==+x + xx==++ +@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ ++==xx + ==++ %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ++== + ++== $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ==++ + ==+x +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ x+== + ++== %@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ==++ + ++++ %@$$$@@+~o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+ยท x@$$$$$ ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+== + == @$$$$$$ ~*@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*ยท =$$$$$$ยท *$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@%x o$$$$$* =$$$$$@ยท == + == ยท@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$~ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@$ == + +++o o@@@@$$$$$@@@@%*+=*$@@@$$$$$$@@@$*=+=%@@@@$$$$$@@@@x o+++ + ** +%@@@@@%+ o*$@@@@$*o +%@@@@@%+ **xo + x+** **++ + x+**++ ox****== ==****xo =+**++ + +=**%%%%%%%*==+x ++==*%%%%%%%==++ x+==*%%%%%%%**=+ + + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_214.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_214.txt new file mode 100644 index 00000000..7eb9faca --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_214.txt @@ -0,0 +1,41 @@ + + + + ++++++========++++++ + ++==*****%%%********%%%*****==++ + ++==**=*=+ ++*=**==++ + ++**+= =+**++ + xx====ox ~=%$@@@@@@@@@@@@$%=~ xo====xx + x+===+ ยท=@@@@@@$$$$$$$$$$$$$$@@@@@@=ยท +===+x + ===+ ~%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%~ +=== + ++== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + x+== =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ==+x + ==+x ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ x+== + xx== =@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ==xx + ++++ =@$$$$@*++$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= ++++ + ==+ยท ~@$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ยท+== + == @$$$$$$ +$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@x %$$$$$@x ~$$$$$$$ == + == ยท@$$$$$$$@@@@x $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@=~ $$$$$= o@$$$$@ยท == + == ยท@$$$$$$ ~=@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ยท == + == ยท@$$$$$$ ยท*@@@@$$$$$$$$$$@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@* ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$@@$$$$$$$$$$$$$$$@@ == + ==+~ %@@@$$$$$$$$@@@$$$@@@@$$$$$$$$@@@@$$$@@@$$$$$$$$@@@% ยท+== + xx== +$@@@@@@@$+ ~*@@@@@@@@*~ +$@@@@@@@$+ ==xx + ++=+ +=++ + ++**oo ==%%++ ++$$== oo==++ + x+==*%*%====****++++++*%**====**%*++++++**=*====*=%*==++ + ++++++ ++++++++ ++++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_215.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_215.txt new file mode 100644 index 00000000..5ca0d40a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_215.txt @@ -0,0 +1,41 @@ + + + + x ++++++====++++++ x + xx++*******%*%%**%%*%*******++xx + xx+=****== ==****=+xx + ++**+*x x*+**++ + ====x+ x*%$@@@@@@@@@@$%*x +x==== + ==== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==== + ==++ *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ++== + ++== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==++ + xx==xยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx==xx + ==++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++== + xx== +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ==xo + ++++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++++ + ==+ยท @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ยท+== + == @$$$$@$ ยท=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ o@$$$$$@ == + == ยท@$$$$$$$@@@$o $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@*x $$$$@= ~@$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@%***************@@$$$$$@ยท == + == ยท@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$= +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท $@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + xx== *@@@@@@@@@%o =@@@@@@@@@@= o%@@@@@@@@@%ยท +++x + ==++ ยท~ยท ~~ ยท~ยท x+== + ==++ x+%% %%+x ++== + ++**=*++xox+==%*==++===%==xoox==%===++==*%==+xoo++*=**++ + +++++===++ +++==+++ +++===++++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_216.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_216.txt new file mode 100644 index 00000000..0b06bcd2 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_216.txt @@ -0,0 +1,41 @@ + + + + x+++++++++++++xx + ++==*****%%%%%%%%%%*****==++ + ++**=*=* *=*=**++ + ++**=*++ ++*=**++ + ++**x+ ~+*$$@@@@@@@@$$*+~ +x**++ + ==== o%@@@@@@@$$$$$$$$$$@@@@@@@%o ==== + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ==++ + ox==xยท x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x ยทx==xo + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++++ + ==+~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+== + == $$$$$$$ o*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@%ยท o@$$$$$@*ยท +@$$$$$@ == + == ยท@$$$$$$$@@@*~ $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$= $$$$@+ @$$$$@ยท == + == ยท@$$$$$$x x%@$$$$$@@*===============$@$$$$$@ยท == + == ยท@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == o@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@*o ยท+$@@@@@@@@@@$+ยท o*@@@@@@@@@@@* ++++ + **+~ ~+=*=+o ยทx=**=xยท o+=*=+~ ยท+** + **xx ++ ++ o+**xx + ====++ *=**==**+= =+**==**+= ++==== + +==**%*=*=++ ++=******=++ +++=**%%*=== + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_217.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_217.txt new file mode 100644 index 00000000..3b406fd5 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_217.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++****%%%%%%%%%%%%****++++ + ++***%*%x~ ~x%*%***++ + ++**==++ ++==**++ + ++**++ ยทx*%$$@@@@@@$$%*xยท ++**++ + ++==~ o*@@@@@@@@$$$$$$$$@@@@@@@@*o ~==++ + ++== =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ==++ + ++== ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ ==++ + ==xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o == + ++++ o@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ==x~ @$$$$$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x== + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ยท=@$$$$$@ == + == ยท@$$$$$$@@@@=ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$=~ $$$$@x @$$$$@ยท == + == ยท@$$$$$$x o%@$$$$$@@=+++++++++++++++$@$$$$$@ยท == + == ยท@$$$$@% o*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@$$$@@@@@=x~o+%@@@@@$$@@@@@%+o~x=@@@@@$$$@@@@$ x+++ + **~ยท ~=%$$$%=o x*$$$$*x o=%$$$%=~ ** + xx**o~ ~~**xx + x+**+= ++******+x x+*=****++ *+**+x + ++==*%%%%%%*== xx==*%%%%%%*==xx ==*%%%%%%*==++ + + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_218.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_218.txt new file mode 100644 index 00000000..7c43145a --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_218.txt @@ -0,0 +1,41 @@ + + + + +x++++++ + ++==***%%%%%%%%%%***==++ + x+==**=*+o o+*=**==+x + ==**++ ++**== + ++**++ x=%$$@@@@@@$$%=x ++**++ + ++**oo ~=@@@@@@@@$$$$$$$$@@@@@@@@=~ oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + ++=* ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ *=++ + ==+~ o@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@o ~x== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + ++++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท@$$$$$$@@@$= $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@@*~ $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ยท == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == $@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@@@$$$$$$$$$$$$$$@@ == + +++o =@@@$$$$$$$@@@@%**$@@@@$$$$$$@@@@$**%@@@@$$$$$$$@@@= ~+++ + ox== ~*@@@@@@@*o +$@@@@@@$+ o*@@@@@@@*~ ==xo + ++== ==++ + ++**++ =*%%== ==%%*= +x**++ + +=**%%%%%*%=**+x ++***%%%%%%***++ x+**=%*%%%%***=+ + xxxx xxxx xxxx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_219.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_219.txt new file mode 100644 index 00000000..427ca641 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_219.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++==***%*%%%%%%*%***==++ + xx==***%+x x+%***==xx + ==**++ ++**== + ++**++ o=*%$$@@@@$$%*=o ++**++ + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**++ + ++== +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ ==++ + x+=* ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท *=xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@o @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ยท*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$~ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ == + ==+ยท %@@$$$$$$$$$@@@@$$@@@@$$$$$$$$@@@@$$@@@@$$$$$$$$$@@% ยท+== + xx== +$@@@@@@@@= o%@@@@@@@@%o =@@@@@@@@$+ ==+x + ++=+ +=++ + ++*=o +=%$++ ++$%=+ o==++ + xx==**=*===*%***++xx++*%*%====%*%*++xx++***%*===***===++ + ++++++xx ++++++++ xx++==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_220.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_220.txt new file mode 100644 index 00000000..bf319ce3 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_220.txt @@ -0,0 +1,41 @@ + + + + + ++++***%%$%%%%$%%***++++ + ++***%+x x+%***++ + ++**++ ++**++ + x+**++ o=*%$$@@@@$$%*=o ++**+x + ++**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ +%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@o @$$$$@ยท == + == ยท@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ยท*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$~ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$@@@@@@@@@$$$$$$$$$$@@@@@@@@@$$$$$$$$$@@@ ยทx== + x+++ ~%@@@@@@@@@$x =@@@@@@@@@@= x$@@@@@@@@@%~ +++x + ===x ~~~ ยท~~ยท ~~~ x+== + ===+ x+%% %%+x +=== + ++**=%+xยทยท~o==*===++***%=+o~~o+=%***++===*==o~ยทยทx+%=**++ + ++=====+++ xx++====++xx +=======++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_221.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_221.txt new file mode 100644 index 00000000..8d7db5aa --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_221.txt @@ -0,0 +1,41 @@ + + + + + ++==***%%%%%%%%%%***==++ + +=***%+o o+%***=+ + +=**++ ++**=+ + xx**++ o=%%$$@@@@$$%%=o ++**xx + x+**oo ~=$@@@@@@@$$$$$$$$@@@@@@@$=~ oo**+x + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **xx + xx** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xx + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@@$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ == + xx++ o@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%o~~~~~~~~~~~~~~~=@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$+xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==x ยท@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@~ == + ++++ x@@@@@@@@@@@= o%@@@@@@@@@@%o =@@@@@@@@@@@x ++xx + ==+o ~x+xo ยทx++xยท ox+xo o+== + **++ == =* x+** + +=**==oยท ++*=**==**=*+o o+*=**==**=*+x ยทo==**=+ + ++==****++++ ++==****==++ ++++****==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_222.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_222.txt new file mode 100644 index 00000000..7551522e --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_222.txt @@ -0,0 +1,41 @@ + + + + + ++==**%%%%%%%%%%%%**==++ + +=***%+o o+****=+ + +=**++ ++**=+ + xx**++ ยทx=%$$@@@@@@$$%=xยท ++**xx + x+**~ ~*@@@@@@@@$$$$$$$$@@@@@@@@*~ ~**+x + ++** +@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + xx++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ++xx + ++x~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~x++ + == $$$$$$% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% +@$$$$$@*~ยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@ == + == ยท@$$$$$$@@@$=ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$=~ $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@ยท == + == ยท@$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@x == + ++++ =@@@@@@@@@@@%o ~+@@@@@@@@@@@@+~ o%@@@@@@@@@@@* ++++ + **+~ ~+***=o ยทx=**=xยท o=***+o ยทx** + **+x ++ ++ ox**xx + ====++ *=**==**+= =+**==**=* ++==== + ++=**%%**=++ ++=**%%**=++ ++=**%%%==++xx + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_223.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_223.txt new file mode 100644 index 00000000..7ba0c681 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_223.txt @@ -0,0 +1,41 @@ + + + + + +++=*%%%%%%%%%%%%%%*=+++ + +=*%=*xยท ยทx*=%*=+ + ++**++ ++**++ + x+**++ ~+*%$@@@@@@@@$%*+~ ++**+x + ++** o*@@@@@@@$$$$$$$$$$@@@@@@@*o **++ + xx** =@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@= **xx + xx** o$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$o **xx + =*xยท o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ยทx*= + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + =* o@$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o *= + xx++ x@$$$$@$%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ++xx + +++~ @$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@% x@$$$$$@*~ ยท+@$$$$$@ == + == ยท@$$$$$$@@@@*ยท $$$$@~ @$$$$@ยท == + == ยท@$$$$$$@@$=ยท $$$$@x @$$$$@ยท == + == ยท@$$$$$$x o%@$$$$$@@=++++++++++++++=$@$$$$$@ยท == + == ยท@$$$$@% o%@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$x o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@$+~ ยทo*@@@@@@@@@@@@*o ~+$@@@@@@@@@@@% ++++ + **xยท x*%%%*+ o=%%%%=o +*%%%*x ยทo** + ox**xo xx xx ~o**xx + ===*+x *+****==++ ++==****+* o+*=== + ++==**%%%**=++ ++=**%%%*=++ ++=**%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_224.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_224.txt new file mode 100644 index 00000000..814ec867 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_224.txt @@ -0,0 +1,41 @@ + + + + ++++ + ++=**%*%%%%%%%%%%*%**=++ + x+==*%=* *=%*==+x + ===*++ ++*=== + x+**++ o=%$@@@@@@@@@@$%=o ++**+x + ++** x%@@@@@@$$$$$$$$$$$$@@@@@@%x **++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==++ + x+=* x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x **+x + **xยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทx** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + =* x@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x *= + xx++ +@$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ +++x + +++~ @$$$$$ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == @$$$$@$ ~*@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$~ o$$$$$$@= x@$$$$$@ == + == ยท@$$$$$$$@@@%o $$$$@ยท @$$$$@ยท == + == ยท@$$$$$$@@%x $$$$@+ ยท@$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@*==============*@@$$$$$@ยท == + == ยท@$$$$@% x%@@@@$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == =@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$@@@@@@=o~o+%@@@@@@@@@@@@%xo~o=@@@@@@$@@@@@$ x+++ + **oยท ~=%$$$%=~ x*$$$$*x ~=%$$$%=~ ** + x+**o~ ~~**xx + xx**+= ++=***=*+x xx*=***=++ ==**+x + ++==*%%%%%**== ++++*%%%%%%*++++ ==**%%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_225.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_225.txt new file mode 100644 index 00000000..5c501115 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_225.txt @@ -0,0 +1,41 @@ + + + + x++++++x + +===*%%%%%****%%%%%*===+ + ++===*+= =+*===+x + ===* *=== + ++**xx ยท+*$@@@@@@@@@@@@$*+ยท xx**++ + ++== +$@@@@@@$$$$$$$$$$$$@@@@@@$+ ==++ + ++== ยท%@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@%ยท ==++ + x+== =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= ==++ + **oยท =@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@= ยทo** + ++++ ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท ++++ + =* +@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + x+++ =@$$$$@%==$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= +++x + +++ยท ยท@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท ยท+++ + == @$$$$@$ =$@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ยท%$$$$$@+ o$$$$$$@ == + == ยท@$$$$$$$@@@$o $$$$@ @$$$$@ยท == + == ยท@$$$$$$@@*o $$$$$= o@$$$$@ยท == + == ยท@$$$$$$ยท =@@$$$$$@@%**************%@@$$$$$@ยท == + == ยท@$$$$$$ =$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$$= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ยท$@@@@$$$@@@@@*xoo+$@@@@@$$@@@@@$+oox*@@@@@$$$@@@@@~ x+++ + ** ~=%$$$$*o +%$$$$%+ o*$$$$%*o ** + xx**~~ **++ + ++**+= ++==**==x~ ~x==**==++ =+**+x + ++==*%%%%%%*== x+==*%%%%%%*==+x ==*%%%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_226.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_226.txt new file mode 100644 index 00000000..d27b6331 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_226.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++++***%%%==++++==%%%***++++ + ++**=*+o o+*=**++ + x+**== ==**+x + ++** x*$@@@@@@@@@@@@@@$*x **++ + ++*+ o%@@@@@@$$$$$$$$$$$$$$@@@@@@%o +*++ + ++=+ x$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$x +=++ + ++== %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ==++ + ** %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% ** + +++x x@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@x x+++ + == *@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* == + xx++ *@$$$$@=ox*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@* ++xx + ==+ยท o@$$$$$ o*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o ยท+== + == @$$$$$$ o%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@= *$$$$$$~ %$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@$+ ~$$$$$* +@$$$$@ยท == + == ยท@$$$$$$ x%@@$$$$$@@@$$$$$$$$$$$$$$@@@$$$$$@ยท == + == ยท@$$$$@$ o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@%ยท o%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xo+*@@@@@$$$@@@@@o x+++ + ** o*$$@$$%x ยท=%$@@$%=ยท x%$@@$$*x ** + x+** **+x + xx**+= xx****== ==****xx =+**++ + ++==*%%%%***++xx ++==*%*%%*%*==++ xx++*%*%%%%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_227.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_227.txt new file mode 100644 index 00000000..c94758b1 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_227.txt @@ -0,0 +1,41 @@ + + + + ++++++++++++ + ++==*%*%==++xxxx++==%*%*==++ + ++**=* *=**++ + ++**+= =+**++ + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++=+ =@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@= +=++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ %@$$$@@x ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ++++ + ==+ยท x@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@x ยท+== + == @$$$$$$ ยท=@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$@@*~ +$$$$$% *$$$$$$ == + == ยท@$$$$$$$@@@@= $@$$@ @$$$$@ยท == + == ยท@$$$$$$@%o x$$$$$% =$$$$$@ยท == + == ยท@$$$$$$ +$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$o +$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** x*$$@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **++ + ++**+= xx****== ==****+x =+**++ + ++==*%%%%*%*=+++ ++==*%*%%*%*==++ +++=*%*%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_228.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_228.txt new file mode 100644 index 00000000..dafe7019 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_228.txt @@ -0,0 +1,41 @@ + + + + ++++===***==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**++ ยทoxx++xxoยท ++**++ + ===+ o=$@@@@@@@@@@@@@@@@@@$=o +=== + **++ o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o ++** + ==+x ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++=+ x@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@x +=++ + xx== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xx + ==x~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~x== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@% ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x =$$$$@$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$= x== + == @$$$$$$ o*@@$$$$$@@@$$$$$$$$$$$$$$$@@$$$$$@ == + == $$$$$$$@$= ยท$$$$$* x@$$$$$ == + == ยท@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ยท@$$$$$@@+ *$$$$$$o %$$$$$@ยท == + == ยท@$$$$$$ x%@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == %@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$$@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o o+++ + ** x*$@@@$%x ~=%$@@$%=~ x%$@@@$%x ** + x+** **++ + ++**+= x+****== ==****+x =+**xx + ++==*%%%%*%*==++ ++==*%*%%%%*==++ ++==*%*%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_229.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_229.txt new file mode 100644 index 00000000..a7e73094 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_229.txt @@ -0,0 +1,41 @@ + + + + ++++==**%%%%***=++++ + ++==*%=*+x x+**%*==++ + ++**=* *=**++ + ==== ox=******=xo ==== + **++ ยท=$@@@@@@@@@@@@@@@@@@@@$=ยท ++** + **+o =@@@@@$$$$$$$$$$$$$$$$$$$$@@@@@= o+** + **x~ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ~x** + +++x *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* x+++ + xx== +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ==xx + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + ox== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xo + +++o @$$$$$+ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@$ +%@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$% == + == @$$$$$$~ +$@$$$$$@@%==============*@@$$$$$@ == + == $$$$$$$@@%x $$$$@+ ยท@$$$$$ == + == ~@$$$$$$$@@@%o $$$$@ยท @$$$$@~ == + == ยท@$$$$$@$~ o$$$$$$@= x@$$$$$@ยท == + == ยท@$$$$@$ ~*@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ ยท*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@$**$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$$@@@@@$$$$$$$$$$$$$@% == + +++x ~@@@@@$$$@@@@@%+xx=$@@@@$$$$@@@@$=xx+%@@@@$$$$@@@@@o x+++ + ** o*$$@@$%x ยท=%$@@$%=ยท x%$@@$$*x ** + x+** **+x + ++**+= xx****== ==****xx =+**+x + ++==*%%%%*%*=+xx ++==*%%%%%%*==++ xx++*%*%%%%***++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_230.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_230.txt new file mode 100644 index 00000000..b27d48bc --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_230.txt @@ -0,0 +1,41 @@ + + + + ++++=**%%%%%%%%%%**=++++ + +=***%+x x+%***=+ + ++**+= =+**++ + ox**+= o=*%$@@@@@@$%*=o =+**xo + x+**oo ยท=$@@@@@@@$$$$$$$$@@@@@@@$=ยท oo**+x + x+** x$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$x **+x + ox** ยท$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ยท **xo + ==+~ ~@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ยท@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ยท == + xx++ ~@$$$$@@$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ ++xx + +++~ @$$$$$ x$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$@% +$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%x~~~~~~~~~~~~~~o*@$$$$$@ == + == ยท$$$$$$$@@@$+ $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@@*o $$$$@x @$$$$@ยท == + == ยท@$$$$$@+ ~*@$$$$$@$+xxxxxxxxxxxxxxx%@$$$$$@ยท == + == ยท@$$$$@% ยท=@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$~ ยท=$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x ยท$@@@@$$$@@@@@*xox=$@@@@$$$$@@@@$=oox*@@@@@$$$@@@@@~ x+++ + ** o*%$$$$*x ยท+%$$$$%+ยท x*$$$$$*o ** + x+**~ **+x + x+**+= +x*=**==x ==**==x+ =+**+x + ++==*%*%%%**++xx ++==*%%%%%%*==++ xx++**%%%*%*==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_231.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_231.txt new file mode 100644 index 00000000..7280be71 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_231.txt @@ -0,0 +1,41 @@ + + + ++++ + ++==*%%%*%****%*%%%*==++ + x+==*%== ==%*==+x + ===*xo ox*=== + ++**x+ x*%$@@@@@@@@@@$%*x +x**++ + ++*= +$@@@@@@$$$$$$$$$$$$@@@@@@$+ =*++ + ++== *@@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@@* ==+x + xx== +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ==xx + **oยท +@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@+ ยทo** + ++++ @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ++++ + ** +@$$$$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ *= + xx++ +@$$$$@%=*$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ++xx + +++~ @$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == @$$$$$$ ยท=@@@@$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ == + == @$$$$$@$o ~$$$$$$@+ x@$$$$$@ == + == ยท@$$$$$$$@@@$o $$$$@ยท @$$$$@ยท == + == ยท@$$$$$$@@%o $$$$$+ ~@$$$$@ยท == + == ยท@$$$$$$~ +$@$$$$$@@%**************%@@$$$$$@ยท == + == ยท@$$$$@$ +$@@@$$$$$$$$$@@@@@@@@@@@@@@@@$$$$$$$@ยท == + == ยท@$$$$$@= +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == *@$$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$$@* == + +++x $@@@@@$$@@@@@*xoo+%@@@@@$$@@@@@%+o~x=@@@@@$$@@@@@$ยท x+++ + **o ~=%$$$%*o +%$$$$%+ o*%$$$%=~ ** + x+**~~ ~**+x + ++**== ++==***=xo o+==**==++ =+**++ + ++==*%%%%%**++xx ++==*%%%%%%*==++ xx++**%%%%%**=++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_232.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_232.txt new file mode 100644 index 00000000..9ae60cde --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_232.txt @@ -0,0 +1,41 @@ + + + ++++====++++ + ++==*%*%==++++++++==%*%*==++ + ++**=* *=**++ + x+**+= ยทยท =+**+x + ++== o=%@@@@@@@@@@@@@@@@%=o ==++ + ===+ +$@@@@@$$$$$$$$$$$$$$$$@@@@@$+ +=== + ++++ =@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$@@@@= ++++ + ++=+ $@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$ +=++ + ** $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ** + +++o =@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@= o+++ + == $@$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ == + ++++ $@$$$@@x ยท+$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + ==+ยท +@$$$$$ =$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@+ ยท+== + == @$$$$$$ =$@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ == + == $$$$$$$@*o +$$$$$% *$$$$$$ == + == ยท@$$$$$$$@@@@+ $$$$@ @$$$$@ยท == + == ยท@$$$$$$@%o x$$$$$% =$$$$$@ยท == + == ยท@$$$$$$ =@@@$$$$$@@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@$o +$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == +@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@@@@@$$$$$$$$$$$$@@= == + ++++ %@@@@@@@@@@@$+oยท~x*@@@@@@@@@@@@*oยทยท~+$@@@@@@@@@@@% x+++ + **xยท +*%%%*+ยท o=%%%%=o ยท+%%$%*+ ยทo** + x+**o~ oo oo oo**+x + ++**+=+~ =+******+x x+******+= ~x=+**++ + ++==**%%%***++xx ++==***%%***==++ xx++***%%%**==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_233.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_233.txt new file mode 100644 index 00000000..5d6e28d0 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_233.txt @@ -0,0 +1,41 @@ + + + ++++==****==++++ + ==***%==xo ox==%***== + ===*++ ++*=== + ++**x+ ยทoxx++xxoยท +x**++ + ===+ o*$@@@@@@@@@@@@@@@@@@$*o +=== + **+x o%@@@@@$$$$$$$$$$$$$$$$$$@@@@@%o x+** + ==+o ~$@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@$~ o+== + ++++ +@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@+ ++++ + ox== o@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@o ==xo + ==+~ %@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@% ~+== + ox== @@$$$@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ==xo + +++x @$$$$@* ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ x+++ + ==x *$$$$$$ ~*@@@@$$$$$$$$$$@@@@@@@@@@@@@@$$$$$$$$* x== + == @$$$$$$ ~*@@$$$$$@@$%%%%%%%%%%%%%%$@@$$$$$@ == + == $$$$$$$@@= ยท$$$$$* x$$$$$$ == + == ~@$$$$$$$@@@@x $$$$@ @$$$$@~ == + == ยท@$$$$$@@+ *$$$$$$o ยท$$$$$$@ยท == + == ยท@$$$$$$ x$@@@$$$$$$@@@@@@@@@@@@@@@@@@@$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$@@*x+%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == x@@$$$$$$$$$$$@@@@@@@$$$$$$$$$$$$@@@@@@@$$$$$$$$$$$@@+ == + ++++ *@@@@@@@@@@@%xยท ~=@@@@@@@@@@@@=~ x%@@@@@@@@@@@* ++++ + **xยท o=*%%=x ~+*%%*+~ x=%%*=x ยทx** + x+**xo ++ ++ ox**+x + x+**=*xx *+****==++ ++==****=* x+==**+x + ++==********++xx ++==********==++ xx++********==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_234.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_234.txt new file mode 100644 index 00000000..b4e624e0 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_234.txt @@ -0,0 +1,41 @@ + + + +++=**%%%%%%%%**=+++ + ++***%==oยท ยทo==%***++ + ++**+* *+**++ + ==== o+=*%$$$$%*=+o ==== + x+**xx x*@@@@@@@@@@@@@@@@@@@@@@*x xx**+x + xx**~~ ~%@@@@$$$$$$$$$$$$$$$$$$$$$$@@@@%~ ~~**xx + **oยท *@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@@* ยทx** + ==+o %@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@% o+== + xx== *@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@* ==xx + ==xยท @@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@ ยทx== + xx== @$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ==xx + +++o @$$$$$o ~*@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ o+++ + == %$$$$@% ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$% == + == @$$$$$$+ o*@$$$$$@$=+++++++++++++++%@$$$$$@ == + == $$$$$$$@@@=~ $$$$$x @$$$$$ == + == ยท@$$$$$$@@@@= $$$$@~ @$$$$@ยท == + == ยท@$$$$$@% +@$$$$$@%~ยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@ยท == + == ยท@$$$$@% x%@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@%%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + == ~@@$$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@o == + ++++ +@@@@@@@@@@@=~ x$@@@@@@@@@@$x ยท=@@@@@@@@@@@+ ++++ + **+~ o+=+xยท o+==+o ยทx+=+x ~x** + xx**+x == == x+**+x + xx**==+= ~x*=******+* *+******=*x~ =+==**xx + ++==********++ x+++********+++x ++********==++ + diff --git a/vendor/libghostty-vt/src/build/framegen/frames/frame_235.txt b/vendor/libghostty-vt/src/build/framegen/frames/frame_235.txt new file mode 100644 index 00000000..7a372192 --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/frames/frame_235.txt @@ -0,0 +1,41 @@ + + + ++==**%%*%%%%%%*%%**==++ + ==***%xx xx%***== + ++**++ ++**++ + x+**++ x=*$$@@@@@@$$*=x ++**+x + ++**~o ~*$@@@@@@@$$$$$$$$@@@@@@@$*~ oo**++ + ++** +$@@@$$$$$$$$$$$$$$$$$$$$$$$$@@@$+ **++ + xx** ~$@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@$~ **xx + ==+~ ~@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@@~ ~+== + ++++ $@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@$ ++++ + == ~@$$$$$@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@~ == + x+++ o@$$$$@@%$@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@o +++x + +++~ @$$$$$ x%@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ ~+++ + == $$$$$$$ x$@@@$$$$$$$@@@@@@@@@@@@@@@@@@$$$$$$$ == + == @$$$$$@* +@$$$$$@%oยทยทยทยทยทยทยทยทยทยทยทยทยทยท~=@$$$$$@ == + == ยท$$$$$$$@@@$= $$$$@~ @$$$$$ยท == + == ยท@$$$$$$@@$*o $$$$@x @$$$$@ยท == + == ยท@$$$$$$+ ~*@$$$$$@$=xxxxxxxxxxxxxx+%@$$$$$@ยท == + == ยท@$$$$$$ ~*@@@@$$$$$$$$@@@@@@@@@@@@@@@@@$$$$$$@ยท == + == ยท@$$$$$$o ยท=@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$@@@@@@@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ยท@$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ยท == + == ~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$~ == + == @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ == + ==xยท @@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$@@@@@@@@$$$$$$$$$$$@@ x== + ++++ o$@@@@@@@@@@+ ~*@@@@@@@@@@*~ +@@@@@@@@@@$x ++++ + ==+x ยทoxo~ ~oo~ ~oxoยท o+*= + x+**+x =* *= ++**xx + x+==**+*+o ++*=**==**=*+x x+*=**==**=*++ ~+*=**==xx + ++++******==++ ++++********++++ ++==******+++x + diff --git a/vendor/libghostty-vt/src/build/framegen/main.c b/vendor/libghostty-vt/src/build/framegen/main.c new file mode 100644 index 00000000..2139b15d --- /dev/null +++ b/vendor/libghostty-vt/src/build/framegen/main.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SEPARATOR '\x01' +#define CHUNK_SIZE 16384 +#define MAX_FRAMES 1024 +#define PATH_SEP '/' + +static int is_frame_file(const char *name) { + size_t len = strlen(name); + return len > 4 && strcmp(name + len - 4, ".txt") == 0; +} + +static int compare_names(const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + +static char *read_file(const char *path, size_t *out_size) { + FILE *f = fopen(path, "rb"); + if (!f) { + fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno)); + return NULL; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + char *buf = malloc(size); + if (!buf) { + return NULL; + } + + if (fread(buf, 1, size, f) != (size_t)size) { + fprintf(stderr, "Failed to read %s\n", path); + return NULL; + } + + fclose(f); + *out_size = size; + return buf; +} + +int main(int argc, char **argv) { + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *frames_dir = argv[1]; + const char *output_file = argv[2]; + + // Use opendir/readdir instead of scandir for Windows compatibility + DIR *dir = opendir(frames_dir); + if (!dir) { + fprintf(stderr, "Failed to scan directory %s: %s\n", frames_dir, strerror(errno)); + return 1; + } + + char *names[MAX_FRAMES]; + int n = 0; + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (!is_frame_file(entry->d_name)) continue; + if (n >= MAX_FRAMES) { + fprintf(stderr, "Too many frame files (max %d)\n", MAX_FRAMES); + closedir(dir); + return 1; + } + names[n] = strdup(entry->d_name); + if (!names[n]) { + fprintf(stderr, "Failed to allocate memory\n"); + closedir(dir); + return 1; + } + n++; + } + closedir(dir); + + if (n == 0) { + fprintf(stderr, "No frame files found in %s\n", frames_dir); + return 1; + } + + qsort(names, n, sizeof(char *), compare_names); + + size_t total_size = 0; + char **frame_contents = calloc(n, sizeof(char*)); + size_t *frame_sizes = calloc(n, sizeof(size_t)); + + for (int i = 0; i < n; i++) { + char path[4096]; + snprintf(path, sizeof(path), "%s%c%s", frames_dir, PATH_SEP, names[i]); + + frame_contents[i] = read_file(path, &frame_sizes[i]); + if (!frame_contents[i]) { + return 1; + } + + total_size += frame_sizes[i]; + if (i < n - 1) total_size++; + } + + char *joined = malloc(total_size); + if (!joined) { + fprintf(stderr, "Failed to allocate joined buffer\n"); + return 1; + } + + size_t offset = 0; + for (int i = 0; i < n; i++) { + memcpy(joined + offset, frame_contents[i], frame_sizes[i]); + offset += frame_sizes[i]; + if (i < n - 1) { + joined[offset++] = SEPARATOR; + } + } + + uLongf compressed_size = compressBound(total_size); + unsigned char *compressed = malloc(compressed_size); + if (!compressed) { + fprintf(stderr, "Failed to allocate compression buffer\n"); + return 1; + } + + z_stream stream = {0}; + stream.next_in = (unsigned char*)joined; + stream.avail_in = total_size; + stream.next_out = compressed; + stream.avail_out = compressed_size; + + // Use -MAX_WBITS for raw DEFLATE (no zlib wrapper) + int ret = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { + fprintf(stderr, "deflateInit2 failed: %d\n", ret); + return 1; + } + + ret = deflate(&stream, Z_FINISH); + if (ret != Z_STREAM_END) { + fprintf(stderr, "deflate failed: %d\n", ret); + deflateEnd(&stream); + return 1; + } + + compressed_size = stream.total_out; + deflateEnd(&stream); + + FILE *out = fopen(output_file, "wb"); + if (!out) { + fprintf(stderr, "Failed to create %s: %s\n", output_file, strerror(errno)); + return 1; + } + + if (fwrite(compressed, 1, compressed_size, out) != compressed_size) { + fprintf(stderr, "Failed to write compressed data\n"); + return 1; + } + + fclose(out); + + return 0; +} diff --git a/vendor/libghostty-vt/src/build/gtk.zig b/vendor/libghostty-vt/src/build/gtk.zig new file mode 100644 index 00000000..7adb3cdb --- /dev/null +++ b/vendor/libghostty-vt/src/build/gtk.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +pub const Targets = packed struct { + x11: bool = false, + wayland: bool = false, +}; + +/// Returns the targets that GTK4 was compiled with. +pub fn targets(b: *std.Build) Targets { + // Run pkg-config. We allow it to fail so that zig build --help + // works without all dependencies. The build will fail later when + // GTK isn't found anyways. + var code: u8 = undefined; + const output = b.runAllowFail( + &.{ "pkg-config", "--variable=targets", "gtk4" }, + &code, + .Ignore, + ) catch return .{}; + + const x11 = std.mem.indexOf(u8, output, "x11") != null; + const wayland = std.mem.indexOf(u8, output, "wayland") != null; + + return .{ + .x11 = x11, + .wayland = wayland, + }; +} diff --git a/vendor/libghostty-vt/src/build/main.zig b/vendor/libghostty-vt/src/build/main.zig new file mode 100644 index 00000000..1f36d375 --- /dev/null +++ b/vendor/libghostty-vt/src/build/main.zig @@ -0,0 +1,34 @@ +//! Build logic for Ghostty. A single "build.zig" file became far too complex +//! and spaghetti, so this package extracts the build logic into smaller, +//! more manageable pieces. + +pub const gtk = @import("gtk.zig"); +pub const Config = @import("Config.zig"); +pub const GitVersion = @import("GitVersion.zig"); + +// Artifacts +pub const GhosttyBench = @import("GhosttyBench.zig"); +pub const GhosttyDist = @import("GhosttyDist.zig"); +pub const GhosttyDocs = @import("GhosttyDocs.zig"); +pub const GhosttyExe = @import("GhosttyExe.zig"); +pub const GhosttyFrameData = @import("GhosttyFrameData.zig"); +pub const GhosttyLib = @import("GhosttyLib.zig"); +pub const GhosttyLibVt = @import("GhosttyLibVt.zig"); +pub const GhosttyResources = @import("GhosttyResources.zig"); +pub const GhosttyI18n = @import("GhosttyI18n.zig"); +pub const GhosttyXcodebuild = @import("GhosttyXcodebuild.zig"); +pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig"); +pub const GhosttyWebdata = @import("GhosttyWebdata.zig"); +pub const GhosttyZig = @import("GhosttyZig.zig"); +pub const HelpStrings = @import("HelpStrings.zig"); +pub const SharedDeps = @import("SharedDeps.zig"); +pub const UnicodeTables = @import("UnicodeTables.zig"); + +// Steps +pub const LibtoolStep = @import("LibtoolStep.zig"); +pub const LipoStep = @import("LipoStep.zig"); +pub const MetallibStep = @import("MetallibStep.zig"); +pub const XCFrameworkStep = @import("XCFrameworkStep.zig"); + +// Helpers +pub const requireZig = @import("zig.zig").requireZig; diff --git a/vendor/libghostty-vt/src/build/mdgen/ghostty_1_footer.md b/vendor/libghostty-vt/src/build/mdgen/ghostty_1_footer.md new file mode 100644 index 00000000..a63a85fd --- /dev/null +++ b/vendor/libghostty-vt/src/build/mdgen/ghostty_1_footer.md @@ -0,0 +1,64 @@ +# FILES + +_\$XDG_CONFIG_HOME/ghostty/config.ghostty_ + +: Location of the default configuration file. + +_\$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty_ + +: **On macOS**, location of the default configuration file. This location takes +precedence over the XDG environment locations. + +_\$LOCALAPPDATA/ghostty/config.ghostty_ + +: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched +for configuration files. + +# ENVIRONMENT + +**TERM** + +: Defaults to `xterm-ghostty`. Can be configured with the `term` configuration option. + +**GHOSTTY_RESOURCES_DIR** + +: Where the Ghostty resources can be found. + +**XDG_CONFIG_HOME** + +: Default location for configuration files. + +**$HOME/Library/Application Support/com.mitchellh.ghostty** + +: **MACOS ONLY** default location for configuration files. This location takes +precedence over the XDG environment locations. + +**LOCALAPPDATA** + +: **WINDOWS ONLY:** alternate location to search for configuration files. + +**GHOSTTY_LOG** + +: The `GHOSTTY_LOG` environment variable can be used to control which +destinations receive logs. Ghostty currently defines two destinations: + +: - `stderr` - logging to `stderr`. +: - `macos` - logging to macOS's unified log (has no effect on non-macOS platforms). + +: Combine values with a comma to enable multiple destinations. Prefix a +destination with `no-` to disable it. Enabling and disabling destinations +can be done at the same time. Setting `GHOSTTY_LOG` to `true` will enable all +destinations. Setting `GHOSTTY_LOG` to `false` will disable all destinations. + +# BUGS + +See GitHub issues: + +# AUTHOR + +Mitchell Hashimoto +Ghostty contributors + +# SEE ALSO + +**ghostty(5)** diff --git a/vendor/libghostty-vt/src/build/mdgen/ghostty_1_header.md b/vendor/libghostty-vt/src/build/mdgen/ghostty_1_header.md new file mode 100644 index 00000000..c94ae68c --- /dev/null +++ b/vendor/libghostty-vt/src/build/mdgen/ghostty_1_header.md @@ -0,0 +1,23 @@ +% GHOSTTY(1) Version @@VERSION@@ | Ghostty terminal emulator + +# NAME + +**ghostty** - Ghostty terminal emulator + +# DESCRIPTION + +Ghostty is a cross-platform, GPU-accelerated terminal emulator that aims to push +the boundaries of what is possible with a terminal emulator by exposing modern, +opt-in features that enable CLI tool developers to build more feature rich, +interactive applications. + +There are a number of excellent terminal emulator options that exist today. +The unique goal of Ghostty is to have a platform for experimenting with modern, +optional, non-standards-compliant features to enhance the capabilities of CLI +applications. We aim to be the best in this category, and competitive in the +rest. + +While aiming for this ambitious goal, Ghostty is a fully standards compliant +terminal emulator that aims to remain compatible with all existing shells and +software. You can use this as a drop-in replacement for your existing terminal +emulator. diff --git a/vendor/libghostty-vt/src/build/mdgen/ghostty_5_footer.md b/vendor/libghostty-vt/src/build/mdgen/ghostty_5_footer.md new file mode 100644 index 00000000..d2cf024d --- /dev/null +++ b/vendor/libghostty-vt/src/build/mdgen/ghostty_5_footer.md @@ -0,0 +1,43 @@ +# FILES + +_\$XDG_CONFIG_HOME/ghostty/config.ghostty_ + +: Location of the default configuration file. + +_\$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty_ + +: **On macOS**, location of the default configuration file. This location takes +precedence over the XDG environment locations. + +_\$LOCALAPPDATA/ghostty/config.ghostty_ + +: **On Windows**, if _\$XDG_CONFIG_HOME_ is not set, _\$LOCALAPPDATA_ will be searched +for configuration files. + +# ENVIRONMENT + +**XDG_CONFIG_HOME** + +: Default location for configuration files. + +**$HOME/Library/Application Support/com.mitchellh.ghostty** + +: **MACOS ONLY** default location for configuration files. This location takes +precedence over the XDG environment locations. + +**LOCALAPPDATA** + +: **WINDOWS ONLY:** alternate location to search for configuration files. + +# BUGS + +See GitHub issues: + +# AUTHOR + +Mitchell Hashimoto +Ghostty contributors + +# SEE ALSO + +**ghostty(1)** diff --git a/vendor/libghostty-vt/src/build/mdgen/ghostty_5_header.md b/vendor/libghostty-vt/src/build/mdgen/ghostty_5_header.md new file mode 100644 index 00000000..ce3196eb --- /dev/null +++ b/vendor/libghostty-vt/src/build/mdgen/ghostty_5_header.md @@ -0,0 +1,122 @@ +% GHOSTTY(5) Version @@VERSION@@ | Ghostty terminal emulator configuration file + +# NAME + +**ghostty** - Ghostty terminal emulator configuration file + +# DESCRIPTION + +To configure Ghostty, you must use a configuration file. GUI-based configuration +is on the roadmap but not yet supported. The configuration file must be placed +at `$XDG_CONFIG_HOME/ghostty/config.ghostty`, which defaults to `~/.config/ghostty/config.ghostty` +if the [XDG environment is not set](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). + +**If you are using macOS, the configuration file can also be placed at +`$HOME/Library/Application Support/com.mitchellh.ghostty/config.ghostty`.** This is the +default configuration location for macOS. It will be searched before any of the +XDG environment locations listed above. + +The file format is documented below as an example: + + # The syntax is "key = value". The whitespace around the equals doesn't matter. + background = 282c34 + foreground= ffffff + + # Blank lines are ignored! + + keybind = ctrl+z=close_surface + keybind = ctrl+d=new_split:right + + # Colors can be changed by setting the 16 colors of `palette`, which each color + # being defined as regular and bold. + # + # black + palette = 0=#1d2021 + palette = 8=#7c6f64 + # red + palette = 1=#cc241d + palette = 9=#fb4934 + # green + palette = 2=#98971a + palette = 10=#b8bb26 + # yellow + palette = 3=#d79921 + palette = 11=#fabd2f + # blue + palette = 4=#458588 + palette = 12=#83a598 + # purple + palette = 5=#b16286 + palette = 13=#d3869b + # aqua + palette = 6=#689d6a + palette = 14=#8ec07c + # white + palette = 7=#a89984 + palette = 15=#fbf1c7 + +You can view all available configuration options and their documentation by +executing the command `ghostty +show-config --default --docs`. Note that this will +output the full default configuration with docs to stdout, so you may want to +pipe that through a pager, an editor, etc. + +Note: You'll see a lot of weird blank configurations like `font-family =`. This +is a valid syntax to specify the default behavior (no value). The `+show-config` +outputs it so it's clear that key is defaulting and also to have something to +attach the doc comment to. + +You can also see and read all available configuration options in the source +Config structure. The available keys are the keys verbatim, and their possible +values are typically documented in the comments. You also can search for +the public config files of many Ghostty users for examples and inspiration. + +## Configuration Errors + +If your configuration file has any errors, Ghostty does its best to ignore +them and move on. Configuration errors will be logged. + +## Debugging Configuration + +You can verify that configuration is being properly loaded by looking at the +debug output of Ghostty. + +In the debug output, you should see in the first 20 lines or so messages about +loading (or not loading) a configuration file, as well as any errors it may have +encountered. Configuration errors are also shown in a dedicated window on both +macOS and Linux (GTK). Ghostty does not treat configuration errors as fatal and +will fall back to default values for erroneous keys. + +You can also view the full configuration Ghostty is loading using `ghostty ++show-config` from the command-line. Use the `--help` flag to additional options +for that command. + +## Logging + +Ghostty can write logs to a number of destinations. On all platforms, logging to +`stderr` is available. Depending on the platform and how Ghostty was launched, +logs sent to `stderr` may be stored by the system and made available for later +retrieval. + +On Linux if Ghostty is launched by the default `systemd` user service, you can use +`journald` to see Ghostty's logs: `journalctl --user --unit app-com.mitchellh.ghostty.service`. + +On macOS logging to the macOS unified log is available and enabled by default. +--Use the system `log` CLI to view Ghostty's logs: `sudo log stream --level debug +--predicate 'subsystem=="com.mitchellh.ghostty"'`. + +Ghostty's logging can be configured in two ways. The first is by what +optimization level Ghostty is compiled with. If Ghostty is compiled with `Debug` +optimizations debug logs will be output to `stderr`. If Ghostty is compiled with +any other optimization the debug logs will not be output to `stderr`. + +Ghostty also checks the `GHOSTTY_LOG` environment variable. It can be used +to control which destinations receive logs. Ghostty currently defines two +destinations: + +- `stderr` - logging to `stderr`. +- `macos` - logging to macOS's unified log (has no effect on non-macOS platforms). + +Combine values with a comma to enable multiple destinations. Prefix a +destination with `no-` to disable it. Enabling and disabling destinations +can be done at the same time. Setting `GHOSTTY_LOG` to `true` will enable all +destinations. Setting `GHOSTTY_LOG` to `false` will disable all destinations. diff --git a/vendor/libghostty-vt/src/build/mdgen/main_ghostty_1.zig b/vendor/libghostty-vt/src/build/mdgen/main_ghostty_1.zig new file mode 100644 index 00000000..2bb413d9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/mdgen/main_ghostty_1.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const gen = @import("mdgen.zig"); + +pub fn main() !void { + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + const alloc = gpa.allocator(); + + var buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const writer = &stdout_writer.interface; + try gen.substitute(alloc, @embedFile("ghostty_1_header.md"), writer); + try gen.genActions(writer); + try gen.genConfig(writer, true); + try gen.substitute(alloc, @embedFile("ghostty_1_footer.md"), writer); + try writer.flush(); +} diff --git a/vendor/libghostty-vt/src/build/mdgen/main_ghostty_5.zig b/vendor/libghostty-vt/src/build/mdgen/main_ghostty_5.zig new file mode 100644 index 00000000..2123b0bc --- /dev/null +++ b/vendor/libghostty-vt/src/build/mdgen/main_ghostty_5.zig @@ -0,0 +1,16 @@ +const std = @import("std"); +const gen = @import("mdgen.zig"); + +pub fn main() !void { + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + const alloc = gpa.allocator(); + + var buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const writer = &stdout_writer.interface; + try gen.substitute(alloc, @embedFile("ghostty_5_header.md"), writer); + try gen.genConfig(writer, false); + try gen.genKeybindActions(writer); + try gen.substitute(alloc, @embedFile("ghostty_5_footer.md"), writer); + try writer.flush(); +} diff --git a/vendor/libghostty-vt/src/build/mdgen/mdgen.zig b/vendor/libghostty-vt/src/build/mdgen/mdgen.zig new file mode 100644 index 00000000..530c8964 --- /dev/null +++ b/vendor/libghostty-vt/src/build/mdgen/mdgen.zig @@ -0,0 +1,117 @@ +const std = @import("std"); +const help_strings = @import("help_strings"); +const build_config = @import("../../build_config.zig"); +const Config = @import("../../config/Config.zig"); +const Action = @import("../../cli/ghostty.zig").Action; +const KeybindAction = @import("../../input/Binding.zig").Action; + +pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: *std.Io.Writer) !void { + const output = try alloc.alloc(u8, std.mem.replacementSize( + u8, + input, + "@@VERSION@@", + build_config.version_string, + )); + defer alloc.free(output); + + _ = std.mem.replace(u8, input, "@@VERSION@@", build_config.version_string, output); + try writer.writeAll(output); +} + +pub fn genConfig(writer: *std.Io.Writer, cli: bool) !void { + try writer.writeAll( + \\ + \\# CONFIGURATION OPTIONS + \\ + \\ + ); + + @setEvalBranchQuota(5000); + inline for (@typeInfo(Config).@"struct".fields) |field| { + if (field.name[0] == '_') continue; + + try writer.writeAll("**`"); + if (cli) try writer.writeAll("--"); + try writer.writeAll(field.name); + try writer.writeAll("`**\n\n"); + if (@hasDecl(help_strings.Config, field.name)) { + var iter = std.mem.splitScalar(u8, @field(help_strings.Config, field.name), '\n'); + var first = true; + while (iter.next()) |s| { + try writer.writeAll(if (first) ": " else " "); + try writer.writeAll(s); + try writer.writeAll("\n"); + first = false; + } + try writer.writeAll("\n\n"); + } + } +} + +pub fn genActions(writer: *std.Io.Writer) !void { + try writer.writeAll( + \\ + \\# COMMAND LINE ACTIONS + \\ + \\ + ); + + inline for (@typeInfo(Action).@"enum".fields) |field| { + const action = std.meta.stringToEnum(Action, field.name).?; + + switch (action) { + .help => try writer.writeAll("**`--help`**\n\n"), + .version => try writer.writeAll("**`--version`**\n\n"), + else => { + try writer.writeAll("**`+"); + try writer.writeAll(field.name); + try writer.writeAll("`**\n\n"); + }, + } + + if (@hasDecl(help_strings.Action, field.name)) { + var iter = std.mem.splitScalar(u8, @field(help_strings.Action, field.name), '\n'); + var first = true; + while (iter.next()) |s| { + try writer.writeAll(if (first) ": " else " "); + try writer.writeAll(s); + try writer.writeAll("\n"); + first = false; + } + try writer.writeAll("\n\n"); + } + } +} + +pub fn genKeybindActions(writer: *std.Io.Writer) !void { + try writer.writeAll( + \\ + \\# KEYBIND ACTIONS + \\ + \\ + ); + + const info = @typeInfo(KeybindAction); + std.debug.assert(info == .@"union"); + + @setEvalBranchQuota(5000); + inline for (info.@"union".fields) |field| { + if (field.name[0] == '_') continue; + + try writer.writeAll("**`"); + try writer.writeAll(field.name); + try writer.writeAll("`**\n\n"); + + if (@hasDecl(help_strings.KeybindAction, field.name)) { + var iter = std.mem.splitScalar(u8, @field(help_strings.KeybindAction, field.name), '\n'); + var first = true; + while (iter.next()) |s| { + try writer.writeAll(if (first) ": " else " "); + try writer.writeAll(s); + try writer.writeAll("\n"); + first = false; + } + try writer.writeAll("\n\n"); + } + } +} diff --git a/vendor/libghostty-vt/src/build/uucode_config.zig b/vendor/libghostty-vt/src/build/uucode_config.zig new file mode 100644 index 00000000..2bb0d450 --- /dev/null +++ b/vendor/libghostty-vt/src/build/uucode_config.zig @@ -0,0 +1,109 @@ +const std = @import("std"); +const assert = std.debug.assert; +const config = @import("config.zig"); +const config_x = @import("config.x.zig"); +const d = config.default; +const wcwidth = config_x.wcwidth; +const grapheme_break_no_control = config_x.grapheme_break_no_control; + +const Allocator = std.mem.Allocator; + +fn computeWidth( + alloc: std.mem.Allocator, + cp: u21, + data: anytype, + backing: anytype, + tracking: anytype, +) Allocator.Error!void { + _ = alloc; + _ = cp; + _ = backing; + _ = tracking; + + // This condition is needed as Ghostty currently has a singular concept for + // the `width` of a code point, while `uucode` splits the concept into + // `wcwidth_standalone` and `wcwidth_zero_in_grapheme`. The two cases where + // we want to use the `wcwidth_standalone` despite the code point occupying + // zero width in a grapheme (`wcwidth_zero_in_grapheme`) are emoji + // modifiers and prepend code points. For emoji modifiers we want to + // support displaying them in isolation as color patches, and if prepend + // characters were to be width 0 they would disappear from the output with + // Ghostty's current width 0 handling. Future work will take advantage of + // the new uucode `wcwidth_standalone` vs `wcwidth_zero_in_grapheme` split. + if (data.wcwidth_zero_in_grapheme and !data.is_emoji_modifier and data.grapheme_break_no_control != .prepend) { + data.width = 0; + } else { + data.width = @min(2, data.wcwidth_standalone); + } +} + +const width = config.Extension{ + .inputs = &.{ + "wcwidth_standalone", + "wcwidth_zero_in_grapheme", + "is_emoji_modifier", + "grapheme_break_no_control", + }, + .compute = &computeWidth, + .fields = &.{ + .{ .name = "width", .type = u2 }, + }, +}; + +fn computeIsSymbol( + alloc: Allocator, + cp: u21, + data: anytype, + backing: anytype, + tracking: anytype, +) Allocator.Error!void { + _ = alloc; + _ = cp; + _ = backing; + _ = tracking; + const block = data.block; + data.is_symbol = data.general_category == .other_private_use or + block == .arrows or + block == .dingbats or + block == .emoticons or + block == .miscellaneous_symbols or + block == .enclosed_alphanumerics or + block == .enclosed_alphanumeric_supplement or + block == .miscellaneous_symbols_and_pictographs or + block == .transport_and_map_symbols; +} + +const is_symbol = config.Extension{ + .inputs = &.{ "block", "general_category" }, + .compute = &computeIsSymbol, + .fields = &.{ + .{ .name = "is_symbol", .type = bool }, + }, +}; + +pub const tables = [_]config.Table{ + .{ + .name = "runtime", + .extensions = &.{}, + .fields = &.{ + d.field("is_emoji_presentation"), + d.field("case_folding_full"), + }, + }, + .{ + .name = "buildtime", + .extensions = &.{ + wcwidth, + grapheme_break_no_control, + width, + is_symbol, + }, + .fields = &.{ + width.field("width"), + wcwidth.field("wcwidth_zero_in_grapheme"), + grapheme_break_no_control.field("grapheme_break_no_control"), + is_symbol.field("is_symbol"), + d.field("is_emoji_vs_base"), + }, + }, +}; diff --git a/vendor/libghostty-vt/src/build/wasm_patch_growable_table.zig b/vendor/libghostty-vt/src/build/wasm_patch_growable_table.zig new file mode 100644 index 00000000..c40c0a9c --- /dev/null +++ b/vendor/libghostty-vt/src/build/wasm_patch_growable_table.zig @@ -0,0 +1,269 @@ +//! Build tool that patches a WASM binary to make the function table +//! growable by removing its maximum size limit. +//! +//! Zig's WASM linker doesn't support `--growable-table`, so the table +//! is emitted with max == min. This tool finds the table section (id 4) +//! and changes the limits flag from 0x01 (has max) to 0x00 (no max), +//! removing the max field. +//! +//! Usage: wasm_growable_table + +const std = @import("std"); +const testing = std.testing; +const Allocator = std.mem.Allocator; + +pub fn main() !void { + // This is a one-off patcher, so we leak all our memory on purpose + // and let the OS clean it up when we exit. + var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; + const alloc = gpa.allocator(); + + // Parse args: program input output + const args = try std.process.argsAlloc(alloc); + defer std.process.argsFree(alloc, args); + if (args.len != 3) { + std.log.err("usage: wasm_growable_table ", .{}); + std.process.exit(1); + unreachable; + } + + // Patch the file. + const output: []const u8 = try patchTableGrowable( + alloc, + try std.fs.cwd().readFileAlloc( + alloc, + args[1], + std.math.maxInt(usize), + ), + ); + + // Write our output + const out_file = try std.fs.cwd().createFile(args[2], .{}); + defer out_file.close(); + try out_file.writeAll(output); +} + +/// Patch the WASM binary's table section to remove the maximum size +/// limit, making the table growable. If the table already has no max +/// or no table section is found, the input is returned unchanged. +/// +/// The WASM table section (id=4) encodes table limits as: +/// - flags=0x00, min (LEB128) โ€” no max, growable +/// - flags=0x01, min (LEB128), max (LEB128) โ€” bounded, not growable +/// +/// This function rewrites the section to use flags=0x00, dropping the +/// max field entirely. +fn patchTableGrowable( + alloc: Allocator, + input: []const u8, +) (error{InvalidWasm} || std.Io.Writer.Error)![]const u8 { + if (input.len < 8) return error.InvalidWasm; + + // Start after the 8-byte WASM header (magic + version). + var pos: usize = 8; + + while (pos < input.len) { + const section_id = input[pos]; + pos += 1; + const section_size = readLeb128(input, &pos); + const section_start = pos; + + // We're looking for section 4 (the table section). + if (section_id != 4) { + pos = section_start + section_size; + continue; + } + + _ = readLeb128(input, &pos); // table count + pos += 1; // elem_type (0x70 = funcref) + const flags = input[pos]; + + // flags bit 0 indicates whether a max is present. + if (flags & 1 == 0) { + // Already no max, nothing to patch. + return input; + } + + // Record positions of each field so we can reconstruct + // the section without the max value. + const flags_pos = pos; + pos += 1; // skip flags byte + const min_start = pos; + _ = readLeb128(input, &pos); // min + const max_start = pos; + _ = readLeb128(input, &pos); // max + const max_end = pos; + const section_end = section_start + section_size; + + // Build the new section payload with the max removed: + // [table count + elem_type] [flags=0x00] [min] [trailing data] + var payload: std.Io.Writer.Allocating = .init(alloc); + try payload.writer.writeAll(input[section_start..flags_pos]); + try payload.writer.writeByte(0x00); // flags: no max + try payload.writer.writeAll(input[min_start..max_start]); + try payload.writer.writeAll(input[max_end..section_end]); + + // Reassemble the full binary: + // [everything before this section] [section id] [new size] [new payload] [everything after] + const before_section = input[0 .. section_start - 1 - uleb128Size(section_size)]; + var result: std.Io.Writer.Allocating = .init(alloc); + try result.writer.writeAll(before_section); + try result.writer.writeByte(4); // table section id + try result.writer.writeUleb128(@as(u32, @intCast(payload.written().len))); + try result.writer.writeAll(payload.written()); + try result.writer.writeAll(input[section_end..]); + return result.written(); + } + + // No table section found; return input unchanged. + return input; +} + +/// Decode an unsigned LEB128 value from `bytes` starting at `pos.*`, +/// advancing `pos` past the encoded bytes. +fn readLeb128(bytes: []const u8, pos: *usize) u32 { + var result: u32 = 0; + var shift: u5 = 0; + while (true) { + const byte = bytes[pos.*]; + pos.* += 1; + result |= @as(u32, byte & 0x7f) << shift; + if (byte & 0x80 == 0) return result; + shift +%= 7; + } +} + +/// Return the number of bytes needed to encode `value` as unsigned LEB128. +fn uleb128Size(value: u32) usize { + var v = value; + var size: usize = 0; + while (true) { + v >>= 7; + size += 1; + if (v == 0) return size; + } +} + +/// Minimal valid WASM module with a bounded table (min=1, max=1). +/// Sections: type(1), table(4), export(7). +const test_wasm_bounded_table = [_]u8{ + 0x00, 0x61, 0x73, 0x6d, // magic + 0x01, 0x00, 0x00, 0x00, // version + // Type section (id=1): 1 type, () -> () + 0x01, 0x04, 0x01, 0x60, + 0x00, 0x00, + // Table section (id=4): 1 table, funcref, flags=1, min=1, max=1 + 0x04, 0x05, + 0x01, 0x70, 0x01, 0x01, + 0x01, + // Export section (id=7): 0 exports + 0x07, 0x01, 0x00, +}; + +/// Same module but the table already has no max (flags=0). +const test_wasm_growable_table = [_]u8{ + 0x00, 0x61, 0x73, 0x6d, // magic + 0x01, 0x00, 0x00, 0x00, // version + // Type section (id=1) + 0x01, 0x04, 0x01, 0x60, + 0x00, 0x00, + // Table section (id=4): 1 table, funcref, flags=0, min=1 + 0x04, 0x04, + 0x01, 0x70, 0x00, 0x01, + // Export section (id=7): 0 exports + 0x07, 0x01, 0x00, +}; + +/// Module with no table section at all. +const test_wasm_no_table = [_]u8{ + 0x00, 0x61, 0x73, 0x6d, // magic + 0x01, 0x00, 0x00, 0x00, // version + // Type section (id=1) + 0x01, 0x04, 0x01, 0x60, + 0x00, 0x00, + // Export section (id=7): 0 exports + 0x07, 0x01, + 0x00, +}; + +test "patches bounded table to remove max" { + // We use a non-checking allocator because the patched result is + // intentionally leaked (matches the real main() usage). + const result = try patchTableGrowable( + std.heap.page_allocator, + &test_wasm_bounded_table, + ); + + // Result should differ from input (max was removed). + try testing.expect(!std.mem.eql( + u8, + result, + &test_wasm_bounded_table, + )); + + // Find the table section in the output and verify flags=0x00. + var pos: usize = 8; + while (pos < result.len) { + const id = result[pos]; + pos += 1; + const size = readLeb128(result, &pos); + if (id == 4) { + _ = readLeb128(result, &pos); // table count + pos += 1; // elem_type + // flags should now be 0x00 (no max). + try testing.expectEqual(@as(u8, 0x00), result[pos]); + return; + } + pos += size; + } + return error.TableSectionNotFound; +} + +test "already growable table is returned unchanged" { + const result = try patchTableGrowable( + testing.allocator, + &test_wasm_growable_table, + ); + try testing.expectEqual( + @as([*]const u8, &test_wasm_growable_table), + result.ptr, + ); +} + +test "no table section returns input unchanged" { + const result = try patchTableGrowable( + testing.allocator, + &test_wasm_no_table, + ); + try testing.expectEqual(@as([*]const u8, &test_wasm_no_table), result.ptr); +} + +test "too short input returns InvalidWasm" { + try testing.expectError( + error.InvalidWasm, + patchTableGrowable(testing.allocator, "short"), + ); +} + +test "readLeb128 single byte" { + const bytes = [_]u8{0x05}; + var pos: usize = 0; + try testing.expectEqual(@as(u32, 5), readLeb128(&bytes, &pos)); + try testing.expectEqual(@as(usize, 1), pos); +} + +test "readLeb128 multi byte" { + // 300 = 0b100101100 โ†’ LEB128: 0xAC 0x02 + const bytes = [_]u8{ 0xAC, 0x02 }; + var pos: usize = 0; + try testing.expectEqual(@as(u32, 300), readLeb128(&bytes, &pos)); + try testing.expectEqual(@as(usize, 2), pos); +} + +test "uleb128Size" { + try testing.expectEqual(@as(usize, 1), uleb128Size(0)); + try testing.expectEqual(@as(usize, 1), uleb128Size(0x7f)); + try testing.expectEqual(@as(usize, 2), uleb128Size(0x80)); + try testing.expectEqual(@as(usize, 2), uleb128Size(300)); + try testing.expectEqual(@as(usize, 5), uleb128Size(std.math.maxInt(u32))); +} diff --git a/vendor/libghostty-vt/src/build/webgen/main_actions.zig b/vendor/libghostty-vt/src/build/webgen/main_actions.zig new file mode 100644 index 00000000..b0de6537 --- /dev/null +++ b/vendor/libghostty-vt/src/build/webgen/main_actions.zig @@ -0,0 +1,9 @@ +const std = @import("std"); +const helpgen_actions = @import("../../input/helpgen_actions.zig"); + +pub fn main() !void { + var buffer: [2048]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const stdout = &stdout_writer.interface; + try helpgen_actions.generate(stdout, .markdown, true, std.heap.page_allocator); +} diff --git a/vendor/libghostty-vt/src/build/webgen/main_commands.zig b/vendor/libghostty-vt/src/build/webgen/main_commands.zig new file mode 100644 index 00000000..65f14452 --- /dev/null +++ b/vendor/libghostty-vt/src/build/webgen/main_commands.zig @@ -0,0 +1,53 @@ +const std = @import("std"); +const Action = @import("../../cli/ghostty.zig").Action; +const help_strings = @import("help_strings"); + +pub fn main() !void { + var buffer: [2048]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const stdout = &stdout_writer.interface; + try genActions(stdout); +} + +// Note: as a shortcut for defining inline editOnGithubLinks per cli action the user +// is directed to the folder view on Github. This includes a README pointing them to +// the files to edit. +pub fn genActions(writer: *std.Io.Writer) !void { + // Write the header + try writer.writeAll( + \\--- + \\title: Reference + \\description: Reference of all Ghostty action subcommands. + \\editOnGithubLink: https://github.com/ghostty-org/ghostty/tree/main/src/cli + \\--- + \\Ghostty includes a number of utility actions that can be accessed as subcommands. + \\Actions provide utilities to work with config, list keybinds, list fonts, demo themes, + \\and debug. + \\ + ); + + inline for (@typeInfo(Action).@"enum".fields) |field| { + const action = std.meta.stringToEnum(Action, field.name).?; + + switch (action) { + .help, .version => try writer.writeAll("## " ++ field.name ++ "\n"), + else => try writer.writeAll("## " ++ field.name ++ "\n"), + } + + if (@hasDecl(help_strings.Action, field.name)) { + var iter = std.mem.splitScalar(u8, @field(help_strings.Action, field.name), '\n'); + var first = true; + while (iter.next()) |s| { + try writer.writeAll(s); + try writer.writeAll("\n"); + first = false; + } + try writer.writeAll("\n```\n"); + switch (action) { + .help, .version => try writer.writeAll("ghostty --" ++ field.name ++ "\n"), + else => try writer.writeAll("ghostty +" ++ field.name ++ "\n"), + } + try writer.writeAll("```\n\n"); + } + } +} diff --git a/vendor/libghostty-vt/src/build/webgen/main_config.zig b/vendor/libghostty-vt/src/build/webgen/main_config.zig new file mode 100644 index 00000000..1363fadc --- /dev/null +++ b/vendor/libghostty-vt/src/build/webgen/main_config.zig @@ -0,0 +1,134 @@ +const std = @import("std"); +const Config = @import("../../config/Config.zig"); +const help_strings = @import("help_strings"); + +pub fn main() !void { + var buffer: [2048]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&buffer); + const stdout = &stdout_writer.interface; + try genConfig(stdout); +} + +pub fn genConfig(writer: *std.Io.Writer) !void { + // Write the header + try writer.writeAll( + \\--- + \\title: Reference + \\description: Reference of all Ghostty configuration options. + \\editOnGithubLink: https://github.com/ghostty-org/ghostty/edit/main/src/config/Config.zig + \\--- + \\ + \\This is a reference of all Ghostty configuration options. These + \\options are ordered roughly by how common they are to be used + \\and grouped with related options. I recommend utilizing your + \\browser's search functionality to find the option you're looking + \\for. + \\ + \\In the future, we'll have a more user-friendly way to view and + \\organize these options. + \\ + \\ + ); + + @setEvalBranchQuota(50_000); + const fields = @typeInfo(Config).@"struct".fields; + inline for (fields, 0..) |field, i| { + if (field.name[0] == '_') continue; + if (!@hasDecl(help_strings.Config, field.name)) continue; + + // Write the field name. + try writer.writeAll("## `"); + try writer.writeAll(field.name); + try writer.writeAll("`\n"); + + // For all subsequent fields with no docs, they are grouped + // with the previous field. + if (i + 1 < fields.len) { + inline for (fields[i + 1 ..]) |next_field| { + if (next_field.name[0] == '_') break; + if (@hasDecl(help_strings.Config, next_field.name)) break; + + try writer.writeAll("## `"); + try writer.writeAll(next_field.name); + try writer.writeAll("`\n"); + } + } + + // Newline after our headers + try writer.writeAll("\n"); + + var iter = std.mem.splitScalar( + u8, + @field(help_strings.Config, field.name), + '\n', + ); + + // We do some really rough markdown "parsing" here so that + // we can fix up some styles for what our website expects. + var block: ?enum { + /// Plaintext, do nothing. + text, + + /// Code block, wrap in triple backticks. We use indented + /// code blocks in our comments but the website parser only + /// supports triple backticks. + code, + + /// Callouts. We detect these based on paragraphs starting + /// with "Note:", "Warning:", etc. (case-insensitive). + callout_note, + callout_warning, + } = null; + + while (iter.next()) |s| { + // Empty line resets our block + if (std.mem.eql(u8, s, "")) { + try endBlock(writer, block); + block = null; + + try writer.writeAll("\n"); + continue; + } + + // If we don't have a block figure out our type. + const first: bool = block == null; + if (block == null) { + if (std.mem.startsWith(u8, s, " ")) { + block = .code; + try writer.writeAll("```\n"); + } else if (std.ascii.startsWithIgnoreCase(s, "note:")) { + block = .callout_note; + try writer.writeAll("\n"); + } else if (std.ascii.startsWithIgnoreCase(s, "warning:")) { + block = .callout_warning; + try writer.writeAll("\n"); + } else { + block = .text; + } + } + + try writer.writeAll(switch (block.?) { + .text => s, + .callout_note => if (first) s["note:".len..] else s, + .callout_warning => if (first) s["warning:".len..] else s, + + .code => if (std.mem.startsWith(u8, s, " ")) + s[4..] + else + s, + }); + try writer.writeAll("\n"); + } + try endBlock(writer, block); + try writer.writeAll("\n"); + } +} + +fn endBlock(writer: *std.Io.Writer, block: anytype) !void { + if (block) |v| switch (v) { + .text => {}, + .code => try writer.writeAll("```\n"), + .callout_note => try writer.writeAll("\n"), + .callout_warning => try writer.writeAll("\n"), + }; +} diff --git a/vendor/libghostty-vt/src/build/xcframework.zig b/vendor/libghostty-vt/src/build/xcframework.zig new file mode 100644 index 00000000..8713a1c9 --- /dev/null +++ b/vendor/libghostty-vt/src/build/xcframework.zig @@ -0,0 +1,3 @@ +/// Target for xcframework builds. This is a separate file so that +/// our runtime code doesn't need to import build code. +pub const Target = enum { native, universal }; diff --git a/vendor/libghostty-vt/src/build/zig.zig b/vendor/libghostty-vt/src/build/zig.zig new file mode 100644 index 00000000..3ee8ffe7 --- /dev/null +++ b/vendor/libghostty-vt/src/build/zig.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +/// Require a specific version of Zig to build this project. +pub fn requireZig(comptime required_zig: []const u8) void { + // Fail compilation if the current Zig version doesn't meet requirements. + const current_vsn = builtin.zig_version; + const required_vsn = std.SemanticVersion.parse(required_zig) catch unreachable; + if (current_vsn.major != required_vsn.major or + current_vsn.minor != required_vsn.minor or + current_vsn.patch < required_vsn.patch) + { + @compileError(std.fmt.comptimePrint( + "Your Zig version v{f} does not meet the required build version of v{f}", + .{ current_vsn, required_vsn }, + )); + } +} diff --git a/vendor/libghostty-vt/src/build_config.zig b/vendor/libghostty-vt/src/build_config.zig new file mode 100644 index 00000000..c19f7372 --- /dev/null +++ b/vendor/libghostty-vt/src/build_config.zig @@ -0,0 +1,105 @@ +//! Build options, available at comptime. Used to configure features. This +//! will reproduce some of the fields from builtin and build_options just +//! so we can limit the amount of imports we need AND give us the ability +//! to shim logic and values into them later. +const std = @import("std"); +const builtin = @import("builtin"); +const options = @import("build_options"); +const assert = std.debug.assert; +const apprt = @import("apprt.zig"); +const font = @import("font/main.zig"); +const rendererpkg = @import("renderer.zig"); +const BuildConfig = @import("build/Config.zig"); + +pub const ReleaseChannel = BuildConfig.ReleaseChannel; + +/// The semantic version of this build. +pub const version = options.app_version; +pub const version_string = options.app_version_string; + +/// The release channel for this build. +pub const release_channel = std.meta.stringToEnum(ReleaseChannel, @tagName(options.release_channel)).?; + +/// The optimization mode as a string. +pub const mode_string = mode: { + const m = @tagName(builtin.mode); + if (std.mem.lastIndexOfScalar(u8, m, '.')) |i| break :mode m[i..]; + break :mode m; +}; + +/// The artifact we're producing. This can be used to determine if we're +/// building a standalone exe, an embedded lib, etc. +pub const artifact = Artifact.detect(); + +/// Our build configuration. We re-export a lot of these back at the +/// top-level so its a bit cleaner to use throughout the code. See the doc +/// comments in BuildConfig for details on each. +const config = BuildConfig.fromOptions(); +pub const exe_entrypoint = config.exe_entrypoint; +pub const flatpak = options.flatpak; +pub const snap = options.snap; +pub const app_runtime: apprt.Runtime = config.app_runtime; +pub const font_backend: font.Backend = config.font_backend; +pub const renderer: rendererpkg.Backend = config.renderer; +pub const i18n: bool = config.i18n; + +/// The bundle ID for the app. This is used in many places and is currently +/// hardcoded here. We could make this configurable in the future if there +/// is a reason to do so. +/// +/// On macOS, this must match the App bundle ID. We can get that dynamically +/// via an API but I don't want to pay the cost of that at runtime. +/// +/// On GTK, this should match the various folders with resources. +/// +/// There are many places that don't use this variable so simply swapping +/// this variable is NOT ENOUGH to change the bundle ID. I just wanted to +/// avoid it in Zig coe as much as possible. +pub const bundle_id = "com.mitchellh.ghostty"; + +/// True if we should have "slow" runtime safety checks. The initial motivation +/// for this was terminal page/pagelist integrity checks. These were VERY +/// slow but very thorough. But they made it so slow that the terminal couldn't +/// be used for real work. We'd love to have an option to run a build with +/// safety checks that could be used for real work. This lets us do that. +pub const slow_runtime_safety = std.debug.runtime_safety and switch (builtin.mode) { + .Debug => true, + .ReleaseSafe, + .ReleaseSmall, + .ReleaseFast, + => false, +}; + +pub const Artifact = enum { + /// Standalone executable + exe, + + /// Embeddable library + lib, + + /// The WASM-targeted module. + wasm_module, + + pub fn detect() Artifact { + if (builtin.target.cpu.arch.isWasm()) { + assert(builtin.output_mode == .Obj); + assert(builtin.link_mode == .Static); + return .wasm_module; + } + + return switch (builtin.output_mode) { + .Exe => .exe, + .Lib => .lib, + else => { + @compileLog(builtin.output_mode); + @compileError("unsupported artifact output mode"); + }, + }; + } +}; + +/// True if runtime safety checks are enabled. +pub const is_debug = switch (builtin.mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, +}; diff --git a/vendor/libghostty-vt/src/cli.zig b/vendor/libghostty-vt/src/cli.zig new file mode 100644 index 00000000..008ff1eb --- /dev/null +++ b/vendor/libghostty-vt/src/cli.zig @@ -0,0 +1,14 @@ +const diags = @import("cli/diagnostics.zig"); + +pub const args = @import("cli/args.zig"); +pub const action = @import("cli/action.zig"); +pub const ghostty = @import("cli/ghostty.zig"); +pub const CompatibilityHandler = args.CompatibilityHandler; +pub const compatibilityRenamed = args.compatibilityRenamed; +pub const DiagnosticList = diags.DiagnosticList; +pub const Diagnostic = diags.Diagnostic; +pub const Location = diags.Location; + +test { + @import("std").testing.refAllDecls(@This()); +} diff --git a/vendor/libghostty-vt/src/cli/CommaSplitter.zig b/vendor/libghostty-vt/src/cli/CommaSplitter.zig new file mode 100644 index 00000000..d5093992 --- /dev/null +++ b/vendor/libghostty-vt/src/cli/CommaSplitter.zig @@ -0,0 +1,483 @@ +//! Iterator to split a string into fields by commas, taking into account +//! quotes and escapes. +//! +//! Supports the same escapes as in Zig literal strings. +//! +//! Quotes must begin and end with a double quote (`"`). It is an error to not +//! end a quote that was begun. To include a double quote inside a quote (or to +//! not have a double quote start a quoted section) escape it with a backslash. +//! +//! Single quotes (`'`) are not special, they do not begin a quoted block. +//! +//! Zig multiline string literals are NOT supported. +//! +//! Quotes and escapes are not stripped or decoded, that must be handled as a +//! separate step! +//! +//! On Windows, backslash is only treated as an escape character inside quoted +//! strings. Outside quotes, backslash is a literal character (path separator). +const CommaSplitter = @This(); + +const builtin = @import("builtin"); + +/// Whether backslash acts as an escape character outside quoted strings. +/// On Windows, backslash is the path separator so it is always literal +/// outside quotes. +const escape_outside_quotes = builtin.os.tag != .windows; + +pub const Error = error{ + UnclosedQuote, + UnfinishedEscape, + IllegalEscape, +}; + +/// the string that we are splitting +str: []const u8, +/// how much of the string has been consumed so far +index: usize, + +/// initialize a splitter with the given string +pub fn init(str: []const u8) CommaSplitter { + return .{ + .str = str, + .index = 0, + }; +} + +/// return the next field, null if no more fields +pub fn next(self: *CommaSplitter) Error!?[]const u8 { + if (self.index >= self.str.len) return null; + + // where the current field starts + const start = self.index; + // state of state machine + const State = enum { + normal, + quoted, + escape, + hexescape, + unicodeescape, + }; + // keep track of the state to return to when done processing an escape + // sequence. + var last: State = .normal; + // used to count number of digits seen in a hex escape + var hexescape_digits: usize = 0; + // sub-state of parsing hex escapes + var unicodeescape_state: enum { + start, + digits, + } = .start; + // number of digits in a unicode escape seen so far + var unicodeescape_digits: usize = 0; + // accumulator for value of unicode escape + var unicodeescape_value: usize = 0; + + loop: switch (State.normal) { + .normal => { + if (self.index >= self.str.len) return self.str[start..]; + switch (self.str[self.index]) { + ',' => { + self.index += 1; + return self.str[start .. self.index - 1]; + }, + '"' => { + self.index += 1; + continue :loop .quoted; + }, + '\\' => { + self.index += 1; + if (comptime escape_outside_quotes) { + last = .normal; + continue :loop .escape; + } + continue :loop .normal; + }, + else => { + self.index += 1; + continue :loop .normal; + }, + } + }, + .quoted => { + if (self.index >= self.str.len) return error.UnclosedQuote; + switch (self.str[self.index]) { + '"' => { + self.index += 1; + continue :loop .normal; + }, + '\\' => { + self.index += 1; + last = .quoted; + continue :loop .escape; + }, + else => { + self.index += 1; + continue :loop .quoted; + }, + } + }, + .escape => { + if (self.index >= self.str.len) return error.UnfinishedEscape; + switch (self.str[self.index]) { + 'n', 'r', 't', '\\', '\'', '"' => { + self.index += 1; + continue :loop last; + }, + 'x' => { + self.index += 1; + hexescape_digits = 0; + continue :loop .hexescape; + }, + 'u' => { + self.index += 1; + unicodeescape_state = .start; + unicodeescape_digits = 0; + unicodeescape_value = 0; + continue :loop .unicodeescape; + }, + else => return error.IllegalEscape, + } + }, + .hexescape => { + if (self.index >= self.str.len) return error.UnfinishedEscape; + switch (self.str[self.index]) { + '0'...'9', 'a'...'f', 'A'...'F' => { + self.index += 1; + hexescape_digits += 1; + if (hexescape_digits == 2) continue :loop last; + continue :loop .hexescape; + }, + else => return error.IllegalEscape, + } + }, + .unicodeescape => { + if (self.index >= self.str.len) return error.UnfinishedEscape; + switch (unicodeescape_state) { + .start => { + switch (self.str[self.index]) { + '{' => { + self.index += 1; + unicodeescape_value = 0; + unicodeescape_state = .digits; + continue :loop .unicodeescape; + }, + else => return error.IllegalEscape, + } + }, + .digits => { + switch (self.str[self.index]) { + '}' => { + self.index += 1; + if (unicodeescape_digits == 0) return error.IllegalEscape; + continue :loop last; + }, + '0'...'9' => |d| { + self.index += 1; + unicodeescape_digits += 1; + unicodeescape_value <<= 4; + unicodeescape_value += d - '0'; + }, + 'a'...'f' => |d| { + self.index += 1; + unicodeescape_digits += 1; + unicodeescape_value <<= 4; + unicodeescape_value += d - 'a'; + }, + 'A'...'F' => |d| { + self.index += 1; + unicodeescape_digits += 1; + unicodeescape_value <<= 4; + unicodeescape_value += d - 'A'; + }, + else => return error.IllegalEscape, + } + if (unicodeescape_value > 0x10ffff) return error.IllegalEscape; + continue :loop .unicodeescape; + }, + } + }, + } +} + +/// Return any remaining string data, whether it has a comma or not. +pub fn rest(self: *CommaSplitter) ?[]const u8 { + if (self.index >= self.str.len) return null; + defer self.index = self.str.len; + return self.str[self.index..]; +} + +test "splitter 1" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("a,b,c"); + try testing.expectEqualStrings("a", (try s.next()).?); + try testing.expectEqualStrings("b", (try s.next()).?); + try testing.expectEqualStrings("c", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 2" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init(""); + try testing.expect(null == try s.next()); +} + +test "splitter 3" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("a"); + try testing.expectEqualStrings("a", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 4" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\x5a"); + try testing.expectEqualStrings("\\x5a", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 5" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("'a',b"); + try testing.expectEqualStrings("'a'", (try s.next()).?); + try testing.expectEqualStrings("b", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 6" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("'a,b',c"); + try testing.expectEqualStrings("'a", (try s.next()).?); + try testing.expectEqualStrings("b'", (try s.next()).?); + try testing.expectEqualStrings("c", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 7" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\"a,b\",c"); + try testing.expectEqualStrings("\"a,b\"", (try s.next()).?); + try testing.expectEqualStrings("c", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 8" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init(" a , b "); + try testing.expectEqualStrings(" a ", (try s.next()).?); + try testing.expectEqualStrings(" b ", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 9" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\x"); + try testing.expectError(error.UnfinishedEscape, s.next()); +} + +test "splitter 10" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\x5"); + try testing.expectError(error.UnfinishedEscape, s.next()); +} + +test "splitter 11" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\u"); + try testing.expectError(error.UnfinishedEscape, s.next()); +} + +test "splitter 12" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\u{"); + try testing.expectError(error.UnfinishedEscape, s.next()); +} + +test "splitter 13" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\u{}"); + try testing.expectError(error.IllegalEscape, s.next()); +} + +test "splitter 14" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\u{h1}"); + try testing.expectError(error.IllegalEscape, s.next()); +} + +test "splitter 15" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\u{10ffff}"); + try testing.expectEqualStrings("\\u{10ffff}", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 16" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\u{110000}"); + try testing.expectError(error.IllegalEscape, s.next()); +} + +test "splitter 17" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\d"); + try testing.expectError(error.IllegalEscape, s.next()); +} + +test "splitter 18" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\\n\\r\\t\\\"\\'\\\\"); + try testing.expectEqualStrings("\\n\\r\\t\\\"\\'\\\\", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 19" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\"abc'def'ghi\""); + try testing.expectEqualStrings("\"abc'def'ghi\"", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 20" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("\",\",abc"); + try testing.expectEqualStrings("\",\"", (try s.next()).?); + try testing.expectEqualStrings("abc", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 21" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("'a','b', 'c'"); + try testing.expectEqualStrings("'a'", (try s.next()).?); + try testing.expectEqualStrings("'b'", (try s.next()).?); + try testing.expectEqualStrings(" 'c'", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 22" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("abc\"def"); + try testing.expectError(error.UnclosedQuote, s.next()); +} + +test "splitter 23" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("title:\"Focus Split: Up\",description:\"Focus the split above, if it exists.\",action:goto_split:up"); + try testing.expectEqualStrings("title:\"Focus Split: Up\"", (try s.next()).?); + try testing.expectEqualStrings("description:\"Focus the split above, if it exists.\"", (try s.next()).?); + try testing.expectEqualStrings("action:goto_split:up", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter 24" { + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("a,b,c,def"); + try testing.expectEqualStrings("a", (try s.next()).?); + try testing.expectEqualStrings("b", (try s.next()).?); + try testing.expectEqualStrings("c,def", s.rest().?); + try testing.expect(null == try s.next()); +} + +test "splitter 25" { + if (comptime !escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("a,\\u{10,df}"); + try testing.expectEqualStrings("a", (try s.next()).?); + try testing.expectError(error.IllegalEscape, s.next()); +} + +// Windows-specific tests: backslash is literal outside quotes. + +test "splitter: windows paths" { + if (comptime escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + var s: CommaSplitter = .init("light:C:\\Users\\foo\\theme,dark:C:\\Users\\bar\\theme"); + try testing.expectEqualStrings("light:C:\\Users\\foo\\theme", (try s.next()).?); + try testing.expectEqualStrings("dark:C:\\Users\\bar\\theme", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter: backslash literal outside quotes on windows" { + if (comptime escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + // Backslash followed by characters that would be escapes on Unix + // are treated as literal on Windows outside quotes. + var s: CommaSplitter = .init("\\n\\r\\t"); + try testing.expectEqualStrings("\\n\\r\\t", (try s.next()).?); + try testing.expect(null == try s.next()); +} + +test "splitter: backslash still escapes inside quotes on windows" { + if (comptime escape_outside_quotes) return error.SkipZigTest; + const std = @import("std"); + const testing = std.testing; + + // Inside quotes, backslash escapes work on all platforms. + var s: CommaSplitter = .init("\"hello\\nworld\""); + try testing.expectEqualStrings("\"hello\\nworld\"", (try s.next()).?); + try testing.expect(null == try s.next()); +} diff --git a/vendor/libghostty-vt/src/cli/Pager.zig b/vendor/libghostty-vt/src/cli/Pager.zig new file mode 100644 index 00000000..247c998e --- /dev/null +++ b/vendor/libghostty-vt/src/cli/Pager.zig @@ -0,0 +1,93 @@ +//! A pager wraps output to an external pager program (like `less`) when +//! stdout is a TTY. The pager command is resolved as: +//! +//! `$GHOSTTY_PAGER` > `$PAGER` > `less` +//! +//! Setting either env var to an empty string disables paging. +//! If stdout is not a TTY, writes go directly to stdout. +const Pager = @This(); +const std = @import("std"); +const Allocator = std.mem.Allocator; +const internal_os = @import("../os/main.zig"); + +/// The pager child process, if one was spawned. +child: ?std.process.Child = null, + +/// The buffered file writer used for both the pager pipe and direct +/// stdout paths. +file_writer: std.fs.File.Writer = undefined, + +/// Initialize the pager. If stdout is a TTY, this spawns the pager +/// process. Otherwise, output goes directly to stdout. +pub fn init(alloc: Allocator) Pager { + return .{ .child = initPager(alloc) }; +} + +/// Writes to the pager process if available; otherwise, stdout. +pub fn writer(self: *Pager, buffer: []u8) *std.Io.Writer { + if (self.child) |child| { + self.file_writer = child.stdin.?.writer(buffer); + } else { + self.file_writer = std.fs.File.stdout().writer(buffer); + } + return &self.file_writer.interface; +} + +/// Deinitialize the pager. Waits for the spawned process to exit. +pub fn deinit(self: *Pager) void { + if (self.child) |*child| { + // Flush any remaining buffered data, close the pipe so the + // pager sees EOF, then wait for it to exit. + self.file_writer.interface.flush() catch {}; + if (child.stdin) |stdin| { + stdin.close(); + child.stdin = null; + } + _ = child.wait() catch {}; + } + + self.* = undefined; +} + +fn initPager(alloc: Allocator) ?std.process.Child { + const stdout_file: std.fs.File = .stdout(); + if (!stdout_file.isTty()) return null; + + // Resolve the pager command: $GHOSTTY_PAGER > $PAGER > `less`. + // An empty value for either env var disables paging. + const ghostty_var = internal_os.getenv(alloc, "GHOSTTY_PAGER") catch null; + defer if (ghostty_var) |v| v.deinit(alloc); + const pager_var = internal_os.getenv(alloc, "PAGER") catch null; + defer if (pager_var) |v| v.deinit(alloc); + + const cmd: ?[]const u8 = cmd: { + if (ghostty_var) |v| break :cmd if (v.value.len > 0) v.value else null; + if (pager_var) |v| break :cmd if (v.value.len > 0) v.value else null; + break :cmd "less"; + }; + + if (cmd == null) return null; + + var child: std.process.Child = .init(&.{cmd.?}, alloc); + child.stdin_behavior = .Pipe; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + child.spawn() catch return null; + return child; +} + +test "pager: non-tty" { + var pager: Pager = .init(std.testing.allocator); + defer pager.deinit(); + try std.testing.expect(pager.child == null); +} + +test "pager: default writer" { + var pager: Pager = .{}; + defer pager.deinit(); + try std.testing.expect(pager.child == null); + var buf: [4096]u8 = undefined; + const w = pager.writer(&buf); + try w.writeAll("hello"); +} diff --git a/vendor/libghostty-vt/src/cli/README.md b/vendor/libghostty-vt/src/cli/README.md new file mode 100644 index 00000000..7a1d9940 --- /dev/null +++ b/vendor/libghostty-vt/src/cli/README.md @@ -0,0 +1,13 @@ +# Subcommand Actions + +This is the cli specific code. It contains cli actions and tui definitions and +argument parsing. + +This README is meant as developer documentation and not as user documentation. +For user documentation, see the main README or [ghostty.org](https://ghostty.org/docs). + +## Updating documentation + +Each cli action is defined in it's own file. Documentation for each action is defined +in the doc comment associated with the `run` function. For example the `run` function +in `list_keybinds.zig` contains the help text for `ghostty +list-keybinds`. diff --git a/vendor/libghostty-vt/src/cli/action.zig b/vendor/libghostty-vt/src/cli/action.zig new file mode 100644 index 00000000..41173a9f --- /dev/null +++ b/vendor/libghostty-vt/src/cli/action.zig @@ -0,0 +1,277 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +pub const DetectError = error{ + /// Multiple actions were detected. You can specify at most one + /// action on the CLI otherwise the behavior desired is ambiguous. + MultipleActions, + + /// An unknown action was specified. + InvalidAction, +}; + +/// Detect the action from CLI args. +pub fn detectArgs(comptime E: type, alloc: Allocator) !?E { + var iter = try std.process.argsWithAllocator(alloc); + defer iter.deinit(); + return try detectIter(E, &iter); +} + +/// Detect the action from any iterator. Each iterator value should yield +/// a CLI argument such as "--foo". +/// +/// The comptime type E must be an enum with the available actions. +/// If the type E has a decl `detectSpecialCase`, then it will be called +/// for each argument to allow handling of special cases. The function +/// signature for `detectSpecialCase` should be: +/// +/// fn detectSpecialCase(arg: []const u8) ?SpecialCase(E) +/// +pub fn detectIter( + comptime E: type, + iter: anytype, +) DetectError!?E { + var fallback: ?E = null; + var pending: ?E = null; + while (iter.next()) |arg| { + // Allow handling of special cases. + if (@hasDecl(E, "detectSpecialCase")) special: { + const special = E.detectSpecialCase(arg) orelse break :special; + switch (special) { + .action => |a| return a, + .fallback => |a| fallback = a, + .abort_if_no_action => if (pending == null) return null, + } + } + + // Commands must start with "+" + if (arg.len == 0 or arg[0] != '+') continue; + if (pending != null) return DetectError.MultipleActions; + pending = std.meta.stringToEnum(E, arg[1..]) orelse + return DetectError.InvalidAction; + } + + // If we have an action, we always return that action, even if we've + // seen "--help" or "-h" because the action may have its own help text. + if (pending != null) return pending; + + // If we have no action but we have a fallback, then we return that. + if (fallback) |a| return a; + + return null; +} + +/// The action enum E can implement the decl `detectSpecialCase` to +/// return this enum in order to perform various special case actions. +pub fn SpecialCase(comptime E: type) type { + return union(enum) { + /// Immediately return this action. + action: E, + + /// Return this action if no other action is found. + fallback: E, + + /// If there is no pending action (we haven't seen an action yet) + /// then we should return no action. This is kind of weird but is + /// a special case to allow "-e" in Ghostty. + abort_if_no_action, + }; +} + +test "detect direct match" { + const testing = std.testing; + const alloc = testing.allocator; + const Enum = enum { foo, bar, baz }; + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "+foo", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.foo, result.?); +} + +test "detect invalid match" { + const testing = std.testing; + const alloc = testing.allocator; + const Enum = enum { foo, bar, baz }; + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "+invalid", + ); + defer iter.deinit(); + try testing.expectError( + DetectError.InvalidAction, + detectIter(Enum, &iter), + ); +} + +test "detect multiple actions" { + const testing = std.testing; + const alloc = testing.allocator; + const Enum = enum { foo, bar, baz }; + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "+foo +bar", + ); + defer iter.deinit(); + try testing.expectError( + DetectError.MultipleActions, + detectIter(Enum, &iter), + ); +} + +test "detect no match" { + const testing = std.testing; + const alloc = testing.allocator; + const Enum = enum { foo, bar, baz }; + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "--some-flag", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expect(result == null); +} + +test "detect special case action" { + const testing = std.testing; + const alloc = testing.allocator; + const Enum = enum { + foo, + bar, + + fn detectSpecialCase(arg: []const u8) ?SpecialCase(@This()) { + return if (std.mem.eql(u8, arg, "--special")) + .{ .action = .foo } + else + null; + } + }; + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "--special +bar", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.foo, result.?); + } + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "+bar --special", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.foo, result.?); + } + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "+bar", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.bar, result.?); + } +} + +test "detect special case fallback" { + const testing = std.testing; + const alloc = testing.allocator; + const Enum = enum { + foo, + bar, + + fn detectSpecialCase(arg: []const u8) ?SpecialCase(@This()) { + return if (std.mem.eql(u8, arg, "--special")) + .{ .fallback = .foo } + else + null; + } + }; + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "--special", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.foo, result.?); + } + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "+bar --special", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.bar, result.?); + } + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "--special +bar", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.bar, result.?); + } +} + +test "detect special case abort_if_no_action" { + const testing = std.testing; + const alloc = testing.allocator; + const Enum = enum { + foo, + bar, + + fn detectSpecialCase(arg: []const u8) ?SpecialCase(@This()) { + return if (std.mem.eql(u8, arg, "-e")) + .abort_if_no_action + else + null; + } + }; + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "-e", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expect(result == null); + } + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "+foo -e", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expectEqual(Enum.foo, result.?); + } + + { + var iter = try std.process.ArgIteratorGeneral(.{}).init( + alloc, + "-e +bar", + ); + defer iter.deinit(); + const result = try detectIter(Enum, &iter); + try testing.expect(result == null); + } +} diff --git a/vendor/libghostty-vt/src/cli/args.zig b/vendor/libghostty-vt/src/cli/args.zig new file mode 100644 index 00000000..bd5060d6 --- /dev/null +++ b/vendor/libghostty-vt/src/cli/args.zig @@ -0,0 +1,1627 @@ +const std = @import("std"); +const mem = std.mem; +const assert = @import("../quirks.zig").inlineAssert; +const Allocator = mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const diags = @import("diagnostics.zig"); +const internal_os = @import("../os/main.zig"); +const Diagnostic = diags.Diagnostic; +const DiagnosticList = diags.DiagnosticList; +const CommaSplitter = @import("CommaSplitter.zig"); + +const log = std.log.scoped(.cli); + +// TODO: +// - Only `--long=value` format is accepted. Do we want to allow +// `--long value`? Not currently allowed. + +// For trimming +pub const whitespace = " \t"; + +/// The base errors for arg parsing. Additional errors can be returned due +/// to type-specific parsing but these are always possible. +pub const Error = error{ + ValueRequired, + InvalidField, + InvalidValue, +}; + +/// Parse the command line arguments from iter into dst. +/// +/// dst must be a struct. The fields and their types will be used to determine +/// the valid CLI flags. See the tests in this file as an example. For field +/// types that are structs, the struct can implement the `parseCLI` function +/// to do custom parsing. +/// +/// If the destination type has a field "_arena" of type `?ArenaAllocator`, +/// an arena allocator will be created (or reused if set already) for any +/// allocations. Allocations are necessary for certain types, like `[]const u8`. +/// +/// If the destination type has a field "_diagnostics", it must be of type +/// "DiagnosticList" and any diagnostic messages will be added to that list. +/// When diagnostics are present, only allocation errors will be returned. +/// +/// If the destination type has a decl "compatibility", it must be of type +/// std.StaticStringMap(CompatibilityHandler(T)), and it will be used to +/// handle backwards compatibility for fields with the given name. The +/// field name doesn't need to exist (so you can setup compatibility for +/// removed fields). The value is a function that will be called when +/// all other parsing fails for that field. If a field changes such that +/// the old values would NOT error, then the caller should handle that +/// downstream after parsing is done, not through this method. +/// +/// Note: If the arena is already non-null, then it will be used. In this +/// case, in the case of an error some memory might be leaked into the arena. +pub fn parse( + comptime T: type, + alloc: Allocator, + dst: *T, + iter: anytype, +) !void { + const info = @typeInfo(T); + assert(info == .@"struct"); + + // Make an arena for all our allocations if we support it. Otherwise, + // use an allocator that always fails. If the arena is already set on + // the config, then we reuse that. See memory note in parse docs. + const arena_available = @hasField(T, "_arena"); + var arena_owned: bool = false; + const arena_alloc = if (arena_available) arena: { + // If the arena is unset, we create it. We mark that we own it + // only so that we can clean it up on error. + if (dst._arena == null) { + dst._arena = .init(alloc); + arena_owned = true; + } + + break :arena dst._arena.?.allocator(); + } else fail: { + // Note: this is... not safe... + var fail = std.testing.FailingAllocator.init(alloc, .{}); + break :fail fail.allocator(); + }; + errdefer if (arena_available and arena_owned) { + dst._arena.?.deinit(); + dst._arena = null; + }; + + while (iter.next()) |arg| { + // Do manual parsing if we have a hook for it. + if (@hasDecl(T, "parseManuallyHook")) { + if (!try dst.parseManuallyHook( + arena_alloc, + arg, + iter, + )) return; + } + + // If the destination supports help then we check for it, call + // the help function and return. + if (@hasDecl(T, "help")) { + if (mem.eql(u8, arg, "--help") or + mem.eql(u8, arg, "-h")) + { + try dst.help(); + return; + } + } + + // If this doesn't start with "--" then it isn't a config + // flag. We don't support positional arguments or configuration + // values set with spaces so this is an error. + if (!mem.startsWith(u8, arg, "--")) { + if (comptime !canTrackDiags(T)) return Error.InvalidField; + + // Add our diagnostic + try dst._diagnostics.append(arena_alloc, .{ + .key = try arena_alloc.dupeZ(u8, arg), + .message = "invalid field", + .location = try diags.Location.fromIter(iter, arena_alloc), + }); + + continue; + } + + var key: []const u8 = arg[2..]; + const value: ?[]const u8 = value: { + // If the arg has "=" then the value is after the "=". + if (mem.indexOf(u8, key, "=")) |idx| { + defer key = key[0..idx]; + break :value key[idx + 1 ..]; + } + + break :value null; + }; + + parseIntoField(T, arena_alloc, dst, key, value) catch |err| err: { + // If we get an error parsing a field, then we try to fall + // back to compatibility handlers if able. + if (@hasDecl(T, "compatibility")) { + // If we have a compatibility handler for this key, then + // we call it and see if it handles the error. + if (T.compatibility.get(key)) |handler| { + if (handler(dst, arena_alloc, key, value)) { + log.info( + "compatibility handler for {s} handled error, you may be using a deprecated field: {}", + .{ key, err }, + ); + break :err; + } + } + } + + if (comptime !canTrackDiags(T)) return err; + + // The error set is dependent on comptime T, so we always add + // an extra error so we can have the "else" below. + const ErrSet = @TypeOf(err) || error{ Unknown, OutOfMemory } || Error; + const message: [:0]const u8 = switch (@as(ErrSet, @errorCast(err))) { + // OOM is not recoverable since we need to allocate to + // track more error messages. + error.OutOfMemory => return err, + error.InvalidField => "unknown field", + error.ValueRequired => formatValueRequired(T, arena_alloc, key) catch "value required", + error.InvalidValue => formatInvalidValue(T, arena_alloc, key, value) catch "invalid value", + else => try std.fmt.allocPrintSentinel( + arena_alloc, + "unknown error {}", + .{err}, + 0, + ), + }; + + // Add our diagnostic + try dst._diagnostics.append(arena_alloc, .{ + .key = try arena_alloc.dupeZ(u8, key), + .message = message, + .location = try diags.Location.fromIter(iter, arena_alloc), + }); + }; + } +} + +/// The function type for a compatibility handler. The compatibility +/// handler is documented in the `parse` function documentation. +/// +/// The function type should return bool if the compatibility was +/// handled, and false otherwise. If false is returned then the +/// naturally occurring error will continue to be processed as if +/// this compatibility handler was not present. +/// +/// Compatibility handlers aren't allowed to return errors because +/// they're generally only called in error cases, so we already have +/// an error message to show users. If there is an error in handling +/// the compatibility, then the handler should return false. +pub fn CompatibilityHandler(comptime T: type) type { + return *const fn ( + dst: *T, + alloc: Allocator, + key: []const u8, + value: ?[]const u8, + ) bool; +} + +/// Convenience function to create a compatibility handler that +/// renames a field from `from` to `to`. +pub fn compatibilityRenamed( + comptime T: type, + comptime to: []const u8, +) CompatibilityHandler(T) { + comptime assert(@hasField(T, to)); + + return (struct { + fn compat( + dst: *T, + alloc: Allocator, + key: []const u8, + value: ?[]const u8, + ) bool { + _ = key; + + parseIntoField(T, alloc, dst, to, value) catch |err| { + log.warn("error parsing renamed field {s}: {}", .{ + to, + err, + }); + + return false; + }; + + return true; + } + }).compat; +} + +fn formatValueRequired( + comptime T: type, + arena_alloc: std.mem.Allocator, + key: []const u8, +) std.Io.Writer.Error![:0]const u8 { + var stream: std.Io.Writer.Allocating = .init(arena_alloc); + const writer = &stream.writer; + + try writer.print("value required", .{}); + try formatValues(T, key, writer); + try writer.writeByte(0); + + const written = stream.written(); + return written[0 .. written.len - 1 :0]; +} + +fn formatInvalidValue( + comptime T: type, + arena_alloc: std.mem.Allocator, + key: []const u8, + value: ?[]const u8, +) std.Io.Writer.Error![:0]const u8 { + var stream: std.Io.Writer.Allocating = .init(arena_alloc); + const writer = &stream.writer; + + try writer.print("invalid value \"{?s}\"", .{value}); + try formatValues(T, key, writer); + try writer.writeByte(0); + + const written = stream.written(); + return written[0 .. written.len - 1 :0]; +} + +fn formatValues( + comptime T: type, + key: []const u8, + writer: *std.Io.Writer, +) std.Io.Writer.Error!void { + @setEvalBranchQuota(2000); + const typeinfo = @typeInfo(T); + inline for (typeinfo.@"struct".fields) |f| { + if (std.mem.eql(u8, key, f.name)) { + switch (@typeInfo(f.type)) { + .@"enum" => |e| { + try writer.print(", valid values are: ", .{}); + inline for (e.fields, 0..) |field, i| { + if (i != 0) try writer.print(", ", .{}); + try writer.print("{s}", .{field.name}); + } + }, + else => {}, + } + break; + } + } +} + +/// Returns true if this type can track diagnostics. +fn canTrackDiags(comptime T: type) bool { + return @hasField(T, "_diagnostics"); +} + +/// Parse a single key/value pair into the destination type T. +/// +/// This may result in allocations. The allocations can only be freed by freeing +/// all the memory associated with alloc. It is expected that alloc points to +/// an arena. +pub fn parseIntoField( + comptime T: type, + alloc: Allocator, + dst: *T, + key: []const u8, + value: ?[]const u8, +) !void { + const info = @typeInfo(T); + assert(info == .@"struct"); + + inline for (info.@"struct".fields) |field| { + if (field.name[0] != '_' and mem.eql(u8, field.name, key)) { + // For optional fields, we just treat it as the child type. + // This lets optional fields default to null but get set by + // the CLI. + const Field = switch (@typeInfo(field.type)) { + .optional => |opt| opt.child, + else => field.type, + }; + const fieldInfo = @typeInfo(Field); + const canHaveDecls = fieldInfo == .@"struct" or + fieldInfo == .@"union" or + fieldInfo == .@"enum"; + + // If the value is empty string (set but empty string), + // then we reset the value to the default. + if (value) |v| default: { + if (v.len != 0) break :default; + // Set default value if possible. + if (canHaveDecls and @hasDecl(Field, "init")) { + try @field(dst, field.name).init(alloc); + return; + } + const raw = field.default_value_ptr orelse break :default; + const ptr: *const field.type = @ptrCast(@alignCast(raw)); + @field(dst, field.name) = ptr.*; + return; + } + + // If we are a type that can have decls and have a parseCLI decl, + // we call that and use that to set the value. + if (canHaveDecls) { + if (@hasDecl(Field, "parseCLI")) { + const fnInfo = @typeInfo(@TypeOf(Field.parseCLI)).@"fn"; + switch (fnInfo.params.len) { + // 1 arg = (input) => output + 1 => @field(dst, field.name) = try Field.parseCLI(value), + + // 2 arg = (self, input) => void + 2 => switch (@typeInfo(field.type)) { + .@"struct", + .@"union", + .@"enum", + => try @field(dst, field.name).parseCLI(value), + + // If the field is optional and set, then we use + // the pointer value directly into it. If its not + // set we need to create a new instance. + .optional => if (@field(dst, field.name)) |*v| { + try v.parseCLI(value); + } else { + // Note: you cannot do @field(dst, name) = undefined + // because this causes the value to be "null" + // in ReleaseFast modes. + var tmp: Field = undefined; + try tmp.parseCLI(value); + @field(dst, field.name) = tmp; + }, + + else => @compileError("unexpected field type"), + }, + + // 3 arg = (self, alloc, input) => void + 3 => switch (@typeInfo(field.type)) { + .@"struct", + .@"union", + .@"enum", + => try @field(dst, field.name).parseCLI(alloc, value), + + .optional => if (@field(dst, field.name)) |*v| { + try v.parseCLI(alloc, value); + } else { + var tmp: Field = undefined; + try tmp.parseCLI(alloc, value); + @field(dst, field.name) = tmp; + }, + + else => @compileError("unexpected field type"), + }, + + else => @compileError("parseCLI invalid argument count"), + } + + return; + } + } + + // No parseCLI, magic the value based on the type + @field(dst, field.name) = switch (Field) { + []const u8 => value: { + const slice = value orelse return error.ValueRequired; + const buf = try alloc.alloc(u8, slice.len); + @memcpy(buf, slice); + break :value buf; + }, + + [:0]const u8 => value: { + const slice = value orelse return error.ValueRequired; + const buf = try alloc.allocSentinel(u8, slice.len, 0); + @memcpy(buf, slice); + buf[slice.len] = 0; + break :value buf; + }, + + bool => try parseBool(value orelse "t"), + + inline u8, + u16, + u21, + u32, + u64, + usize, + i8, + i16, + i32, + i64, + isize, + => |Int| std.fmt.parseInt( + Int, + value orelse return error.ValueRequired, + 0, + ) catch return error.InvalidValue, + + f32, + f64, + => |Float| std.fmt.parseFloat( + Float, + value orelse return error.ValueRequired, + ) catch return error.InvalidValue, + + else => switch (fieldInfo) { + .@"enum" => std.meta.stringToEnum( + Field, + value orelse return error.ValueRequired, + ) orelse return error.InvalidValue, + + .@"struct" => try parseStruct( + Field, + alloc, + value orelse return error.ValueRequired, + ), + + .@"union" => try parseTaggedUnion( + Field, + alloc, + value orelse return error.ValueRequired, + ), + + else => @compileError("unsupported field type"), + }, + }; + + return; + } + } + + return error.InvalidField; +} + +pub fn parseTaggedUnion(comptime T: type, alloc: Allocator, v: []const u8) !T { + const info = @typeInfo(T).@"union"; + assert(@typeInfo(info.tag_type.?) == .@"enum"); + + // Get the union tag that is being set. We support values with no colon + // if the value is void so its not an error to have no colon. + const colon_idx = mem.indexOf(u8, v, ":") orelse v.len; + const tag_str = std.mem.trim(u8, v[0..colon_idx], whitespace); + const value = if (colon_idx < v.len) v[colon_idx + 1 ..] else ""; + + // Find the field in the union that matches the tag. + inline for (info.fields) |field| { + if (mem.eql(u8, field.name, tag_str)) { + // Special case void types where we don't need a value. + if (field.type == void) { + if (value.len > 0) return error.InvalidValue; + return @unionInit(T, field.name, {}); + } + + // We need to create a struct that looks like this union field. + // This lets us use parseIntoField as if its a dedicated struct. + const Target = @Type(.{ .@"struct" = .{ + .layout = .auto, + .fields = &.{.{ + .name = field.name, + .type = field.type, + .default_value_ptr = null, + .is_comptime = false, + .alignment = @alignOf(field.type), + }}, + .decls = &.{}, + .is_tuple = false, + } }); + + // Parse the value into the struct + var t: Target = undefined; + try parseIntoField(Target, alloc, &t, field.name, value); + + // Build our union + return @unionInit(T, field.name, @field(t, field.name)); + } + } + + return error.InvalidValue; +} + +fn parseStruct(comptime T: type, alloc: Allocator, v: []const u8) !T { + return switch (@typeInfo(T).@"struct".layout) { + .auto => parseAutoStruct(T, alloc, v, null), + .@"packed" => parsePackedStruct(T, v), + else => @compileError("unsupported struct layout"), + }; +} + +pub fn parseAutoStruct( + comptime T: type, + alloc: Allocator, + v: []const u8, + default_: ?T, +) !T { + const info = @typeInfo(T).@"struct"; + comptime assert(info.layout == .auto); + + // We start our result as undefined so we don't get an error for required + // fields. We track required fields below and we validate that we set them + // all at the bottom of this function (in addition to setting defaults for + // optionals). + var result: T = undefined; + + // Keep track of which fields were set so we can error if a required + // field was not set. + const FieldSet = std.StaticBitSet(info.fields.len); + var fields_set: FieldSet = .initEmpty(); + + // We split each value by "," allowing for quoting and escaping. + var iter: CommaSplitter = .init(v); + loop: while (try iter.next()) |entry| { + // Find the key/value, trimming whitespace. The value may be quoted + // which we strip the quotes from. + const idx = mem.indexOf(u8, entry, ":") orelse return error.InvalidValue; + const key = std.mem.trim(u8, entry[0..idx], whitespace); + + // used if we need to decode a double-quoted string. + var buf: std.Io.Writer.Allocating = .init(alloc); + defer buf.deinit(); + + const value = value: { + const value = std.mem.trim(u8, entry[idx + 1 ..], whitespace); + + // Detect a quoted string. + if (value.len >= 2 and + value[0] == '"' and + value[value.len - 1] == '"') + { + // Decode a double-quoted string as a Zig string literal. + const parsed = try std.zig.string_literal.parseWrite(&buf.writer, value); + if (parsed == .failure) return error.InvalidValue; + break :value buf.written(); + } + + break :value value; + }; + + inline for (info.fields, 0..) |field, i| { + if (std.mem.eql(u8, field.name, key)) { + try parseIntoField(T, alloc, &result, key, value); + fields_set.set(i); + continue :loop; + } + } + + // No field matched + return error.InvalidValue; + } + + // Ensure all required fields are set + inline for (info.fields, 0..) |field, i| { + if (!fields_set.isSet(i)) { + @field(result, field.name) = default: { + // If we're given a default value then we inherit those. + // Otherwise we use the default values as specified by the + // struct. + if (default_) |default| { + break :default @field(default, field.name); + } else { + const default_ptr = field.default_value_ptr orelse return error.InvalidValue; + const typed_ptr: *const field.type = @ptrCast(@alignCast(default_ptr)); + break :default typed_ptr.*; + } + }; + } + } + + return result; +} + +pub fn parsePackedStruct(comptime T: type, v: []const u8) !T { + const info = @typeInfo(T).@"struct"; + comptime assert(info.layout == .@"packed"); + + var result: T = .{}; + + // Allow standalone boolean values like "true" and "false" to + // turn on or off all of the struct's fields. + bools: { + const b = parseBool(v) catch break :bools; + inline for (info.fields) |field| { + assert(field.type == bool); + @field(result, field.name) = b; + } + return result; + } + + // We split each value by "," + var iter = std.mem.splitSequence(u8, v, ","); + loop: while (iter.next()) |part_raw| { + // Determine the field we're looking for and the value. If the + // field is prefixed with "no-" then we set the value to false. + const part, const value = part: { + const negation_prefix = "no-"; + const trimmed = std.mem.trim(u8, part_raw, whitespace); + if (std.mem.startsWith(u8, trimmed, negation_prefix)) { + break :part .{ trimmed[negation_prefix.len..], false }; + } else { + break :part .{ trimmed, true }; + } + }; + + inline for (info.fields) |field| { + assert(field.type == bool); + if (std.mem.eql(u8, field.name, part)) { + @field(result, field.name) = value; + continue :loop; + } + } + + // No field matched + return error.InvalidValue; + } + + return result; +} + +pub fn parseBool(v: []const u8) !bool { + const t = &[_][]const u8{ "1", "t", "T", "true" }; + const f = &[_][]const u8{ "0", "f", "F", "false" }; + + inline for (t) |str| { + if (mem.eql(u8, v, str)) return true; + } + inline for (f) |str| { + if (mem.eql(u8, v, str)) return false; + } + + return error.InvalidValue; +} + +test "parse: simple" { + const testing = std.testing; + + var data: struct { + a: []const u8 = "", + b: bool = false, + @"b-f": bool = true, + + _arena: ?ArenaAllocator = null, + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--a=42 --b --b-f=false", + ); + defer iter.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expect(data._arena != null); + try testing.expectEqualStrings("42", data.a); + try testing.expect(data.b); + try testing.expect(!data.@"b-f"); + + // Reparsing works + var iter2 = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--a=84", + ); + defer iter2.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter2); + try testing.expect(data._arena != null); + try testing.expectEqualStrings("84", data.a); + try testing.expect(data.b); + try testing.expect(!data.@"b-f"); +} + +test "parse: quoted value" { + const testing = std.testing; + + var data: struct { + a: u8 = 0, + b: []const u8 = "", + _arena: ?ArenaAllocator = null, + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--a=\"42\" --b=\"hello!\"", + ); + defer iter.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expectEqual(@as(u8, 42), data.a); + try testing.expectEqualStrings("hello!", data.b); +} + +test "parse: empty value resets to default" { + const testing = std.testing; + + var data: struct { + a: u8 = 42, + b: bool = false, + _arena: ?ArenaAllocator = null, + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--a= --b=", + ); + defer iter.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expectEqual(@as(u8, 42), data.a); + try testing.expect(!data.b); +} + +test "parse: positional arguments are invalid" { + const testing = std.testing; + + var data: struct { + a: u8 = 42, + _arena: ?ArenaAllocator = null, + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--a=84 what", + ); + defer iter.deinit(); + try testing.expectError( + error.InvalidField, + parse(@TypeOf(data), testing.allocator, &data, &iter), + ); + try testing.expectEqual(@as(u8, 84), data.a); +} + +test "parse: diagnostic tracking" { + const testing = std.testing; + + var data: struct { + a: []const u8 = "", + b: enum { one } = .one, + + _arena: ?ArenaAllocator = null, + _diagnostics: DiagnosticList = .{}, + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--what --a=42", + ); + defer iter.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expect(data._arena != null); + try testing.expectEqualStrings("42", data.a); + try testing.expect(data._diagnostics.items().len == 1); + { + const diag = data._diagnostics.items()[0]; + try testing.expectEqual(diags.Location.none, diag.location); + try testing.expectEqualStrings("what", diag.key); + try testing.expectEqualStrings("unknown field", diag.message); + } +} + +test "parse: diagnostic location" { + const testing = std.testing; + + var data: struct { + a: []const u8 = "", + b: enum { one, two } = .one, + + _arena: ?ArenaAllocator = null, + _diagnostics: DiagnosticList = .{}, + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var r: std.Io.Reader = .fixed( + \\a=42 + \\what + \\b=two + ); + + var iter: LineIterator = .{ .r = &r, .filepath = "test" }; + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expect(data._arena != null); + try testing.expectEqualStrings("42", data.a); + try testing.expect(data.b == .two); + try testing.expect(data._diagnostics.items().len == 1); + { + const diag = data._diagnostics.items()[0]; + try testing.expectEqualStrings("what", diag.key); + try testing.expectEqualStrings("unknown field", diag.message); + try testing.expectEqualStrings("test", diag.location.file.path); + try testing.expectEqual(2, diag.location.file.line); + } +} + +test "parse: compatibility handler" { + const testing = std.testing; + + var data: struct { + a: bool = false, + _arena: ?ArenaAllocator = null, + + pub const compatibility: std.StaticStringMap( + CompatibilityHandler(@This()), + ) = .initComptime(&.{ + .{ "a", compat }, + }); + + fn compat( + self: *@This(), + alloc: Allocator, + key: []const u8, + value: ?[]const u8, + ) bool { + _ = alloc; + if (std.mem.eql(u8, key, "a")) { + if (value) |v| { + if (mem.eql(u8, v, "yuh")) { + self.a = true; + return true; + } + } + } + + return false; + } + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--a=yuh", + ); + defer iter.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expect(data._arena != null); + try testing.expect(data.a); +} + +test "parse: compatibility renamed" { + const testing = std.testing; + + var data: struct { + a: bool = false, + b: bool = false, + _arena: ?ArenaAllocator = null, + + pub const compatibility: std.StaticStringMap( + CompatibilityHandler(@This()), + ) = .initComptime(&.{ + .{ "old", compatibilityRenamed(@This(), "a") }, + }); + } = .{}; + defer if (data._arena) |arena| arena.deinit(); + + var iter = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--old=true --b=true", + ); + defer iter.deinit(); + try parse(@TypeOf(data), testing.allocator, &data, &iter); + try testing.expect(data._arena != null); + try testing.expect(data.a); + try testing.expect(data.b); +} + +test "parseIntoField: ignore underscore-prefixed fields" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + _a: []const u8 = "12", + } = .{}; + + try testing.expectError( + error.InvalidField, + parseIntoField(@TypeOf(data), alloc, &data, "_a", "42"), + ); + try testing.expectEqualStrings("12", data._a); +} + +test "parseIntoField: struct with init func" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: struct { + const Self = @This(); + + v: []const u8, + + pub fn init(self: *Self, _alloc: Allocator) !void { + _ = _alloc; + self.* = .{ .v = "HELLO!" }; + } + }, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "a", ""); + try testing.expectEqual(@as([]const u8, "HELLO!"), data.a.v); +} + +test "parseIntoField: string" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: []const u8, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "a", "42"); + try testing.expectEqualStrings("42", data.a); +} + +test "parseIntoField: sentinel string" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: [:0]const u8, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "a", "42"); + try testing.expectEqualStrings("42", data.a); + try testing.expectEqual(@as(u8, 0), data.a[data.a.len]); +} + +test "parseIntoField: bool" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: bool, + } = undefined; + + // True + try parseIntoField(@TypeOf(data), alloc, &data, "a", "1"); + try testing.expectEqual(true, data.a); + try parseIntoField(@TypeOf(data), alloc, &data, "a", "t"); + try testing.expectEqual(true, data.a); + try parseIntoField(@TypeOf(data), alloc, &data, "a", "T"); + try testing.expectEqual(true, data.a); + try parseIntoField(@TypeOf(data), alloc, &data, "a", "true"); + try testing.expectEqual(true, data.a); + + // False + try parseIntoField(@TypeOf(data), alloc, &data, "a", "0"); + try testing.expectEqual(false, data.a); + try parseIntoField(@TypeOf(data), alloc, &data, "a", "f"); + try testing.expectEqual(false, data.a); + try parseIntoField(@TypeOf(data), alloc, &data, "a", "F"); + try testing.expectEqual(false, data.a); + try parseIntoField(@TypeOf(data), alloc, &data, "a", "false"); + try testing.expectEqual(false, data.a); +} + +test "parseIntoField: unsigned numbers" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + u8: u8, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "u8", "1"); + try testing.expectEqual(@as(u8, 1), data.u8); +} + +test "parseIntoField: floats" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + f64: f64, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "f64", "1"); + try testing.expectEqual(@as(f64, 1.0), data.f64); +} + +test "parseIntoField: enums" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + const Enum = enum { one, two, three }; + var data: struct { + v: Enum, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "v", "two"); + try testing.expectEqual(Enum.two, data.v); +} + +test "parseIntoField: packed struct" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + const Field = packed struct { + a: bool = false, + b: bool = true, + }; + var data: struct { + v: Field, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "v", "b"); + try testing.expect(!data.v.a); + try testing.expect(data.v.b); +} + +test "parseIntoField: packed struct negation" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + const Field = packed struct { + a: bool = false, + b: bool = true, + }; + var data: struct { + v: Field, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "v", "a,no-b"); + try testing.expect(data.v.a); + try testing.expect(!data.v.b); +} + +test "parseIntoField: packed struct true/false" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + const Field = packed struct { + a: bool = false, + b: bool = true, + }; + var data: struct { + v: Field, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "v", "true"); + try testing.expect(data.v.a); + try testing.expect(data.v.b); + + try parseIntoField(@TypeOf(data), alloc, &data, "v", "false"); + try testing.expect(!data.v.a); + try testing.expect(!data.v.b); + + try testing.expectError( + error.InvalidValue, + parseIntoField(@TypeOf(data), alloc, &data, "v", "true,a"), + ); +} + +test "parseIntoField: packed struct whitespace" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + const Field = packed struct { + a: bool = false, + b: bool = true, + }; + var data: struct { + v: Field, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "v", " a, no-b "); + try testing.expect(data.v.a); + try testing.expect(!data.v.b); +} + +test "parseIntoField: optional field" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: ?bool = null, + } = .{}; + + // True + try parseIntoField(@TypeOf(data), alloc, &data, "a", "1"); + try testing.expectEqual(true, data.a.?); + + // Unset + try parseIntoField(@TypeOf(data), alloc, &data, "a", ""); + try testing.expect(data.a == null); +} + +test "parseIntoField: struct with parse func" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: struct { + const Self = @This(); + + v: []const u8, + + pub fn parseCLI(value: ?[]const u8) !Self { + _ = value; + return Self{ .v = "HELLO!" }; + } + }, + } = undefined; + + try parseIntoField(@TypeOf(data), alloc, &data, "a", "42"); + try testing.expectEqual(@as([]const u8, "HELLO!"), data.a.v); +} + +test "parseIntoField: optional struct with parse func" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + a: ?struct { + const Self = @This(); + + v: []const u8, + + pub fn parseCLI(self: *Self, _: Allocator, value: ?[]const u8) !void { + _ = value; + self.* = .{ .v = "HELLO!" }; + } + } = null, + } = .{}; + + try parseIntoField(@TypeOf(data), alloc, &data, "a", "42"); + try testing.expectEqual(@as([]const u8, "HELLO!"), data.a.?.v); +} + +test "parseIntoField: struct with basic fields" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + value: struct { + a: []const u8, + b: u32, + c: u8 = 12, + } = undefined, + } = .{}; + + // Set required fields + try parseIntoField(@TypeOf(data), alloc, &data, "value", "a:hello,b:42"); + try testing.expectEqualStrings("hello", data.value.a); + try testing.expectEqual(42, data.value.b); + try testing.expectEqual(12, data.value.c); + + // Set all fields + try parseIntoField(@TypeOf(data), alloc, &data, "value", "a:world,b:84,c:24"); + try testing.expectEqualStrings("world", data.value.a); + try testing.expectEqual(84, data.value.b); + try testing.expectEqual(24, data.value.c); + + // Missing require dfield + try testing.expectError( + error.InvalidValue, + parseIntoField(@TypeOf(data), alloc, &data, "value", "a:hello"), + ); +} + +test "parseIntoField: tagged union" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + value: union(enum) { + a: u8, + b: u8, + c: void, + d: []const u8, + e: [:0]const u8, + } = undefined, + } = .{}; + + // Set one field + try parseIntoField(@TypeOf(data), alloc, &data, "value", "a:1"); + try testing.expectEqual(1, data.value.a); + + // Set another + try parseIntoField(@TypeOf(data), alloc, &data, "value", "b:2"); + try testing.expectEqual(2, data.value.b); + + // Set void field + try parseIntoField(@TypeOf(data), alloc, &data, "value", "c"); + try testing.expectEqual({}, data.value.c); + + // Set string field + try parseIntoField(@TypeOf(data), alloc, &data, "value", "d:hello"); + try testing.expectEqualStrings("hello", data.value.d); + + // Set sentinel string field + try parseIntoField(@TypeOf(data), alloc, &data, "value", "e:hello"); + try testing.expectEqualStrings("hello", data.value.e); +} + +test "parseIntoField: tagged union unknown filed" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + value: union(enum) { + a: u8, + b: u8, + } = undefined, + } = .{}; + + try testing.expectError( + error.InvalidValue, + parseIntoField(@TypeOf(data), alloc, &data, "value", "c:1"), + ); +} + +test "parseIntoField: tagged union invalid field value" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + value: union(enum) { + a: u8, + b: u8, + } = undefined, + } = .{}; + + try testing.expectError( + error.InvalidValue, + parseIntoField(@TypeOf(data), alloc, &data, "value", "a:hello"), + ); +} + +test "parseIntoField: tagged union missing tag" { + const testing = std.testing; + var arena = ArenaAllocator.init(testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + var data: struct { + value: union(enum) { + a: u8, + b: u8, + } = undefined, + } = .{}; + + try testing.expectError( + error.InvalidValue, + parseIntoField(@TypeOf(data), alloc, &data, "value", "a"), + ); + try testing.expectError( + error.InvalidValue, + parseIntoField(@TypeOf(data), alloc, &data, "value", ":a"), + ); +} + +/// An iterator that considers its location to be CLI args. It +/// iterates through an underlying iterator and increments a counter +/// to track the current CLI arg index. +/// +/// This also ignores any argument that starts with `+`. It assumes that +/// actions were parsed out before this iterator was created. +pub fn ArgsIterator(comptime Iterator: type) type { + return struct { + const Self = @This(); + + /// The underlying args iterator. + iterator: Iterator, + + /// Our current index into the iterator. This is 1-indexed. + /// The 0 value is used to indicate that we haven't read any + /// values yet. + index: usize = 0, + + pub fn deinit(self: *Self) void { + if (@hasDecl(Iterator, "deinit")) { + self.iterator.deinit(); + } + } + + pub fn next(self: *Self) ?[]const u8 { + const value = self.iterator.next() orelse return null; + self.index += 1; + + // We ignore any argument that starts with "+". This is used + // to indicate actions and are expected to be parsed out before + // this iterator is created. + if (value.len > 0 and value[0] == '+') return self.next(); + + return value; + } + + /// Returns a location for a diagnostic message. + pub fn location(self: *const Self, _: Allocator) error{}!?diags.Location { + return .{ .cli = self.index }; + } + }; +} + +/// Create an args iterator for the process args. This will skip argv0. +pub fn argsIterator(alloc_gpa: Allocator) internal_os.args.ArgIterator.InitError!ArgsIterator(internal_os.args.ArgIterator) { + var iter = try internal_os.args.iterator(alloc_gpa); + errdefer iter.deinit(); + _ = iter.next(); // skip argv0 + return .{ .iterator = iter }; +} + +test "ArgsIterator" { + const testing = std.testing; + + const child = try std.process.ArgIteratorGeneral(.{}).init( + testing.allocator, + "--what +list-things --a=42", + ); + const Iter = ArgsIterator(@TypeOf(child)); + var iter: Iter = .{ .iterator = child }; + defer iter.deinit(); + + try testing.expectEqualStrings("--what", iter.next().?); + try testing.expectEqualStrings("--a=42", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} + +/// Returns an iterator (implements "next") that reads CLI args by line. +/// Each CLI arg is expected to be a single line. This is used to implement +/// configuration files. +pub const LineIterator = struct { + const Self = @This(); + + /// The maximum size a single line can be. We don't expect any + /// CLI arg to exceed this size. Can't wait to git blame this in + /// like 4 years and be wrong about this. + pub const MAX_LINE_SIZE = 4096; + + /// Our stateful reader. + r: *std.Io.Reader, + + /// Filepath that is used for diagnostics. This is only used for + /// diagnostic messages so it can be formatted however you want. + /// It is prefixed to the messages followed by the line number. + filepath: []const u8 = "", + + /// The current line that we're on. This is 1-indexed because + /// lines are generally 1-indexed in the real world. The value + /// can be zero if we haven't read any lines yet. + line: usize = 0, + + /// This is the buffer where we store the current entry that + /// is formatted to be compatible with the parse function. + entry: [MAX_LINE_SIZE]u8 = [_]u8{ '-', '-' } ++ ([_]u8{0} ** (MAX_LINE_SIZE - 2)), + + pub fn init(reader: *std.Io.Reader) Self { + return .{ .r = reader }; + } + + pub fn next(self: *Self) ?[]const u8 { + // First prime the reader. + // File readers at least are initialized with a size of 0, + // and this will actually prompt the reader to get the actual + // size of the file, which will be used in the EOF check below. + // + // This will also optimize reads down the line as we're + // more likely to beworking with buffered data. + // + // fillMore asserts that the buffer has available capacity, + // so skip this if it's full. + if (self.r.bufferedLen() < self.r.buffer.len) { + self.r.fillMore() catch {}; + } + + var writer: std.Io.Writer = .fixed(self.entry[2..]); + + var entry = while (self.r.seek != self.r.end) { + // Reset write head + writer.end = 0; + + _ = self.r.streamDelimiterEnding(&writer, '\n') catch |e| { + log.warn("cannot read from \"{s}\": {}", .{ self.filepath, e }); + return null; + }; + _ = self.r.discardDelimiterInclusive('\n') catch {}; + + var entry = writer.buffered(); + self.line += 1; + + // Trim any whitespace (including CR) around it + const trim = std.mem.trim(u8, entry, whitespace ++ "\r"); + if (trim.len != entry.len) { + std.mem.copyForwards(u8, entry, trim); + entry = entry[0..trim.len]; + } + + // Ignore blank lines and comments + if (entry.len == 0 or entry[0] == '#') continue; + break entry; + } else return null; + + if (mem.indexOf(u8, entry, "=")) |idx| { + const key = std.mem.trim(u8, entry[0..idx], whitespace); + const value = value: { + var value = std.mem.trim(u8, entry[idx + 1 ..], whitespace); + + // Detect a quoted string. + if (value.len >= 2 and + value[0] == '"' and + value[value.len - 1] == '"') + { + // Trim quotes since our CLI args processor expects + // quotes to already be gone. + value = value[1 .. value.len - 1]; + } + + break :value value; + }; + + const len = key.len + value.len + 1; + if (entry.len != len) { + std.mem.copyForwards(u8, entry, key); + entry[key.len] = '='; + std.mem.copyForwards(u8, entry[key.len + 1 ..], value); + entry = entry[0..len]; + } + } + + // We need to reslice so that we include our '--' at the beginning + // of our buffer so that we can trick the CLI parser to treat it + // as CLI args. + return self.entry[0 .. entry.len + 2]; + } + + /// Returns a location for a diagnostic message. + pub fn location( + self: *const Self, + alloc: Allocator, + ) Allocator.Error!?diags.Location { + // If we have no filepath then we have no location. + if (self.filepath.len == 0) return null; + + return .{ .file = .{ + .path = try alloc.dupe(u8, self.filepath), + .line = self.line, + } }; + } +}; + +/// An iterator valid for arg parsing from a slice. +pub const SliceIterator = struct { + const Self = @This(); + + slice: []const []const u8, + idx: usize = 0, + + pub fn next(self: *Self) ?[]const u8 { + if (self.idx >= self.slice.len) return null; + defer self.idx += 1; + return self.slice[self.idx]; + } +}; + +/// Construct a SliceIterator from a slice. +pub fn sliceIterator(slice: []const []const u8) SliceIterator { + return .{ .slice = slice }; +} + +test "LineIterator" { + const testing = std.testing; + var reader: std.Io.Reader = .fixed( + \\A + \\B=42 + \\C + \\ + \\# A comment + \\D + \\ + \\ # An indented comment + \\ E + \\ + \\# A quoted string with whitespace + \\F= "value " + ); + + var iter: LineIterator = .init(&reader); + try testing.expectEqualStrings("--A", iter.next().?); + try testing.expectEqualStrings("--B=42", iter.next().?); + try testing.expectEqualStrings("--C", iter.next().?); + try testing.expectEqualStrings("--D", iter.next().?); + try testing.expectEqualStrings("--E", iter.next().?); + try testing.expectEqualStrings("--F=value ", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} + +test "LineIterator end in newline" { + const testing = std.testing; + var reader: std.Io.Reader = .fixed("A\n\n"); + + var iter: LineIterator = .init(&reader); + try testing.expectEqualStrings("--A", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} + +test "LineIterator spaces around '='" { + const testing = std.testing; + var reader: std.Io.Reader = .fixed("A = B\n\n"); + + var iter: LineIterator = .init(&reader); + try testing.expectEqualStrings("--A=B", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} + +test "LineIterator no value" { + const testing = std.testing; + var reader: std.Io.Reader = .fixed("A = \n\n"); + + var iter: LineIterator = .init(&reader); + try testing.expectEqualStrings("--A=", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} + +test "LineIterator with CRLF line endings" { + const testing = std.testing; + var reader: std.Io.Reader = .fixed("A\r\nB = C\r\n"); + + var iter: LineIterator = .init(&reader); + try testing.expectEqualStrings("--A", iter.next().?); + try testing.expectEqualStrings("--B=C", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} + +test "LineIterator with buffered reader" { + const testing = std.testing; + var f: std.Io.Reader = .fixed("A\nB = C\n"); + var buf: [2]u8 = undefined; + var r = f.limited(.unlimited, &buf); + const reader = &r.interface; + + var iter: LineIterator = .init(reader); + try testing.expectEqualStrings("--A", iter.next().?); + try testing.expectEqualStrings("--B=C", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} + +test "LineIterator with buffered and primed reader" { + const testing = std.testing; + var f: std.Io.Reader = .fixed("A\nB = C\n"); + var buf: [2]u8 = undefined; + var r = f.limited(.unlimited, &buf); + const reader = &r.interface; + + try reader.fill(buf.len); + + var iter: LineIterator = .init(reader); + try testing.expectEqualStrings("--A", iter.next().?); + try testing.expectEqualStrings("--B=C", iter.next().?); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); + try testing.expectEqual(@as(?[]const u8, null), iter.next()); +} diff --git a/vendor/libghostty-vt/src/cli/boo.zig b/vendor/libghostty-vt/src/cli/boo.zig new file mode 100644 index 00000000..2834eadb --- /dev/null +++ b/vendor/libghostty-vt/src/cli/boo.zig @@ -0,0 +1,236 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const args = @import("args.zig"); +const Action = @import("ghostty.zig").Action; +const Allocator = std.mem.Allocator; +const vaxis = @import("vaxis"); + +const framedata = @import("framedata").compressed; + +const vxfw = vaxis.vxfw; + +pub const Options = struct { + pub fn deinit(self: Options) void { + _ = self; + } + + /// Enables `-h` and `--help` to work. + pub fn help(self: Options) !void { + _ = self; + return Action.help_error; + } +}; + +const Boo = struct { + frame: u8, + framerate: u32, // 30 fps + // We know the size of this at compile time, but we heap allocate the slice to prevent the + // binary from increasing too much in size + buffer: [frame_width * frame_height]vaxis.Cell = undefined, + + ghostty_style: vaxis.Style, + outline_style: vaxis.Style, + + // Width of a single frame + const frame_width = 100; + // Height of a single frame + const frame_height = 41; + + fn widget(self: *Boo) vxfw.Widget { + return .{ + .userdata = self, + .eventHandler = Boo.typeErasedEventHandler, + .drawFn = Boo.typeErasedDrawFn, + }; + } + + fn typeErasedEventHandler(ptr: *anyopaque, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { + const self: *Boo = @ptrCast(@alignCast(ptr)); + switch (event) { + .init, + .tick, + => { + self.updateFrame(); + ctx.redraw = true; + return ctx.tick(self.framerate, self.widget()); + }, + .key_press => |key| { + if (key.matches('c', .{ .ctrl = true }) or + key.matches(vaxis.Key.escape, .{})) + { + ctx.quit = true; + return; + } + }, + else => {}, + } + } + + fn typeErasedDrawFn(ptr: *anyopaque, ctx: vxfw.DrawContext) Allocator.Error!vxfw.Surface { + const self: *Boo = @ptrCast(@alignCast(ptr)); + const max = ctx.max.size(); + + // Warn for screen size + if (max.width < frame_width or max.height < frame_height) { + const text: vxfw.Text = .{ .text = "Screen must be at least 100w x 41h" }; + const center: vxfw.Center = .{ .child = text.widget() }; + return center.draw(ctx); + } + + // Calculate x and y offsets to center the animation frame + const offset_y = (max.height - frame_height) / 2; + const offset_x = (max.width - frame_width) / 2; + + // Create the animation surface + const child: vxfw.Surface = .{ + .size = .{ .width = @intCast(frame_width), .height = @intCast(frame_height) }, + .widget = self.widget(), + .buffer = &self.buffer, + .children = &.{}, + }; + + // Allocate a slice of child surfaces + var children = try ctx.arena.alloc(vxfw.SubSurface, 1); + children[0] = .{ + .origin = .{ .row = @intCast(offset_y), .col = @intCast(offset_x) }, + .surface = child, + }; + + return .{ + .size = max, + .widget = self.widget(), + .buffer = &.{}, + .children = children, + }; + } + + /// Updates our internal buffer with the current frame, then advances the frame index + fn updateFrame(self: *Boo) void { + const frame = frames[self.frame]; + // A frame is characters with html spans. When we encounter a span, we use the outline style + // until the span ends. That is, when we find a '<', we parse until '>'. Then we use the + // outline styule until the next '<', and skip until the next '>' + + const State = enum { + normal, + span, + in_tag, + in_closing_tag, + }; + + var cell_idx: usize = 0; + + var line_iter = std.mem.splitScalar(u8, frame, '\n'); + while (line_iter.next()) |line| { + var state: State = .normal; + var style = self.ghostty_style; + var cp_iter: std.unicode.Utf8Iterator = .{ .bytes = line, .i = 0 }; + while (cp_iter.nextCodepointSlice()) |char| { + switch (state) { + .normal => if (std.mem.eql(u8, "<", char)) { + state = .in_tag; + // We will be entering a span + style = self.outline_style; + continue; + }, + .span => if (std.mem.eql(u8, "<", char)) { + state = .in_tag; + style = self.ghostty_style; + continue; + }, + .in_tag => { + // If we encounter a '/', we are a closing tag + // If we parse all the way to a '>' we are an opening tag: we are now in a span + if (std.mem.eql(u8, "/", char)) + state = .in_closing_tag + else if (std.mem.eql(u8, ">", char)) + state = .span; + continue; + }, + .in_closing_tag => { + // If we are closing a tag, we will enter the normal state + if (std.mem.eql(u8, ">", char)) state = .normal; + continue; + }, + } + self.buffer[cell_idx] = .{ + .char = .{ + .grapheme = char, + .width = 1, + }, + .style = style, + }; + cell_idx += 1; + } + } + std.debug.assert(cell_idx == self.buffer.len); + + // Lastly, update the frame index + self.frame += 1; + if (self.frame == frames.len) self.frame = 0; + } +}; + +/// The `boo` command is used to display the animation from the Ghostty website in the terminal +pub fn run(gpa: Allocator) !u8 { + // Disable on non-desktop systems. + switch (builtin.os.tag) { + .windows, .macos, .linux, .freebsd => {}, + else => return 1, + } + + var opts: Options = .{}; + defer opts.deinit(); + + { + var iter = try args.argsIterator(gpa); + defer iter.deinit(); + try args.parse(Options, gpa, &opts, &iter); + } + + try decompressFrames(gpa); + defer { + gpa.free(frames); + gpa.free(decompressed_data); + } + + var app = try vxfw.App.init(gpa); + defer app.deinit(); + + var boo: Boo = undefined; + boo.frame = 0; + boo.framerate = 1000 / 30; + boo.ghostty_style = .{}; + boo.outline_style = .{ .fg = .{ .index = 4 } }; + @memset(&boo.buffer, .{}); + + try app.run(boo.widget(), .{}); + + return 0; +} + +/// We store a global ref to the decompressed data. All of our frames reference into this data +var decompressed_data: []const u8 = undefined; + +/// Heap allocated list of frames. The underlying frame data references decompressed_data +var frames: []const []const u8 = undefined; + +/// Decompress the frames into a slice of individual frames +fn decompressFrames(gpa: Allocator) !void { + var src: std.Io.Reader = .fixed(framedata); + + // var buf: [std.compress.flate.max_window_len]u8 = undefined; + var decompress: std.compress.flate.Decompress = .init(&src, .raw, &.{}); + + var out: std.Io.Writer.Allocating = .init(gpa); + _ = try decompress.reader.streamRemaining(&out.writer); + decompressed_data = try out.toOwnedSlice(); + + var frame_list: std.ArrayList([]const u8) = try .initCapacity(gpa, 235); + + var frame_iter = std.mem.splitScalar(u8, decompressed_data, '\x01'); + while (frame_iter.next()) |frame| { + try frame_list.append(gpa, frame); + } + frames = try frame_list.toOwnedSlice(gpa); +} diff --git a/vendor/libghostty-vt/src/cli/crash_report.zig b/vendor/libghostty-vt/src/cli/crash_report.zig new file mode 100644 index 00000000..f0940fda --- /dev/null +++ b/vendor/libghostty-vt/src/cli/crash_report.zig @@ -0,0 +1,93 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const args = @import("args.zig"); +const Action = @import("ghostty.zig").Action; +const Config = @import("../config.zig").Config; +const crash = @import("../crash/main.zig"); + +pub const Options = struct { + pub fn deinit(self: Options) void { + _ = self; + } + + /// Enables "-h" and "--help" to work. + pub fn help(self: Options) !void { + _ = self; + return Action.help_error; + } +}; + +/// The `crash-report` command is used to inspect and send crash reports. +/// +/// When executed without any arguments, this will list existing crash reports. +/// +/// This command currently only supports listing crash reports. Viewing +/// and sending crash reports is unimplemented and will be added in the future. +pub fn run(alloc_gpa: Allocator) !u8 { + // Use an arena for the whole command to avoid manual memory management. + var arena = std.heap.ArenaAllocator.init(alloc_gpa); + defer arena.deinit(); + const alloc = arena.allocator(); + + var opts: Options = .{}; + defer opts.deinit(); + + { + var iter = try args.argsIterator(alloc_gpa); + defer iter.deinit(); + try args.parse(Options, alloc_gpa, &opts, &iter); + } + + var buffer: [1024]u8 = undefined; + var stdout_file: std.fs.File = .stdout(); + var stdout_writer = stdout_file.writer(&buffer); + const stdout = &stdout_writer.interface; + + const result = runInner(alloc, &stdout_file, stdout); + stdout.flush() catch {}; + return result; +} + +fn runInner( + alloc: Allocator, + stdout_file: *std.fs.File, + stdout: *std.Io.Writer, +) !u8 { + const crash_dir = try crash.defaultDir(alloc); + var reports: std.ArrayList(crash.Report) = .empty; + errdefer reports.deinit(alloc); + + var it = try crash_dir.iterator(); + while (try it.next()) |report| try reports.append(alloc, .{ + .name = try alloc.dupe(u8, report.name), + .mtime = report.mtime, + }); + + // If we have no reports, then we're done. If we have a tty then we + // print a message, otherwise we do nothing. + if (reports.items.len == 0) { + if (std.posix.isatty(stdout_file.handle)) { + try stdout.writeAll("No crash reports! ๐Ÿ‘ป\n"); + } + return 0; + } + + std.mem.sort(crash.Report, reports.items, {}, lt); + + for (reports.items) |report| { + var buf: [128]u8 = undefined; + const now = std.time.nanoTimestamp(); + const diff = now - report.mtime; + const since = if (diff <= 0) "now" else s: { + const d = Config.Duration{ .duration = @intCast(diff) }; + break :s try std.fmt.bufPrint(&buf, "{f} ago", .{d.round(std.time.ns_per_s)}); + }; + try stdout.print("{s} ({s})\n", .{ report.name, since }); + } + + return 0; +} + +fn lt(_: void, lhs: crash.Report, rhs: crash.Report) bool { + return lhs.mtime > rhs.mtime; +} diff --git a/vendor/libghostty-vt/src/cli/diagnostics.zig b/vendor/libghostty-vt/src/cli/diagnostics.zig new file mode 100644 index 00000000..7f4dcc45 --- /dev/null +++ b/vendor/libghostty-vt/src/cli/diagnostics.zig @@ -0,0 +1,197 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assert = @import("../quirks.zig").inlineAssert; +const Allocator = std.mem.Allocator; +const build_config = @import("../build_config.zig"); + +/// A diagnostic message from parsing. This is used to provide additional +/// human-friendly warnings and errors about the parsed data. +/// +/// All of the memory for the diagnostic is allocated from the arena +/// associated with the config structure. If an arena isn't available +/// then diagnostics are not supported. +pub const Diagnostic = struct { + location: Location = .none, + key: [:0]const u8 = "", + message: [:0]const u8, + + /// Write the full user-friendly diagnostic message to the writer. + pub fn format(self: *const Diagnostic, writer: *std.Io.Writer) !void { + switch (self.location) { + .none => {}, + .cli => |index| try writer.print("cli:{}:", .{index}), + .file => |file| try writer.print( + "{s}:{}:", + .{ file.path, file.line }, + ), + } + + if (self.key.len > 0) { + try writer.print("{s}: ", .{self.key}); + } else if (self.location != .none) { + try writer.print(" ", .{}); + } + + try writer.print("{s}", .{self.message}); + } + + pub fn clone(self: *const Diagnostic, alloc: Allocator) Allocator.Error!Diagnostic { + return .{ + .location = try self.location.clone(alloc), + .key = try alloc.dupeZ(u8, self.key), + .message = try alloc.dupeZ(u8, self.message), + }; + } +}; + +/// The possible locations for a diagnostic message. This is used +/// to provide context for the message. +pub const Location = union(enum) { + none, + cli: usize, + file: struct { + path: []const u8, + line: usize, + }, + + pub const Key = @typeInfo(Location).@"union".tag_type.?; + + pub fn fromIter(iter: anytype, alloc: Allocator) Allocator.Error!Location { + const Iter = t: { + const T = @TypeOf(iter); + break :t switch (@typeInfo(T)) { + .pointer => |v| v.child, + .@"struct" => T, + else => return .none, + }; + }; + + if (!@hasDecl(Iter, "location")) return .none; + return (try iter.location(alloc)) orelse .none; + } + + pub fn clone(self: *const Location, alloc: Allocator) Allocator.Error!Location { + return switch (self.*) { + .none, + .cli, + => self.*, + + .file => |v| .{ .file = .{ + .path = try alloc.dupe(u8, v.path), + .line = v.line, + } }, + }; + } +}; + +/// A list of diagnostics. The "_diagnostics" field must be this type +/// for diagnostics to be supported. If this field is an incorrect type +/// a compile-time error will be raised. +/// +/// This is implemented as a simple wrapper around an array list +/// so that we can inject some logic around adding diagnostics +/// and potentially in the future structure them differently. +pub const DiagnosticList = struct { + /// The list of diagnostics. + list: std.ArrayListUnmanaged(Diagnostic) = .{}, + + /// Precomputed data for diagnostics. This is used specifically + /// when we build libghostty so that we can precompute the messages + /// and return them via the C API without allocating memory at + /// call time. + precompute: Precompute = precompute_init, + + const precompute_enabled = switch (build_config.artifact) { + // We enable precompute for tests so that the logic is + // semantically analyzed and run. + .exe, .wasm_module => builtin.is_test, + + // We specifically want precompute for libghostty. + .lib => true, + }; + + const Precompute = if (precompute_enabled) struct { + messages: std.ArrayListUnmanaged([:0]const u8) = .{}, + + pub fn clone( + self: *const Precompute, + alloc: Allocator, + ) Allocator.Error!Precompute { + var result: Precompute = .{}; + try result.messages.ensureTotalCapacity(alloc, self.messages.items.len); + for (self.messages.items) |msg| { + result.messages.appendAssumeCapacity( + try alloc.dupeZ(u8, msg), + ); + } + return result; + } + } else void; + + const precompute_init: Precompute = if (precompute_enabled) .{} else {}; + + pub fn clone( + self: *const DiagnosticList, + alloc: Allocator, + ) Allocator.Error!DiagnosticList { + var result: DiagnosticList = .{}; + + try result.list.ensureTotalCapacity(alloc, self.list.items.len); + for (self.list.items) |*diag| result.list.appendAssumeCapacity( + try diag.clone(alloc), + ); + + if (comptime precompute_enabled) { + result.precompute = try self.precompute.clone(alloc); + } + + return result; + } + + pub fn append( + self: *DiagnosticList, + alloc: Allocator, + diag: Diagnostic, + ) Allocator.Error!void { + try self.list.append(alloc, diag); + errdefer _ = self.list.pop(); + + if (comptime precompute_enabled) { + var stream: std.Io.Writer.Allocating = .init(alloc); + defer stream.deinit(); + diag.format(&stream.writer) catch |err| switch (err) { + // WriteFailed in this instance can only mean an OOM + error.WriteFailed => return error.OutOfMemory, + }; + + const owned: [:0]const u8 = try stream.toOwnedSliceSentinel(0); + errdefer alloc.free(owned); + + try self.precompute.messages.append(alloc, owned); + errdefer _ = self.precompute.messages.pop(); + + assert(self.precompute.messages.items.len == self.list.items.len); + } + } + + pub fn empty(self: *const DiagnosticList) bool { + return self.list.items.len == 0; + } + + pub fn items(self: *const DiagnosticList) []const Diagnostic { + return self.list.items; + } + + /// Returns true if there are any diagnostics for the given + /// location type. + pub fn containsLocation( + self: *const DiagnosticList, + location: Location.Key, + ) bool { + for (self.list.items) |diag| { + if (diag.location == location) return true; + } + + return false; + } +}; diff --git a/vendor/libghostty-vt/src/cli/edit_config.zig b/vendor/libghostty-vt/src/cli/edit_config.zig new file mode 100644 index 00000000..c08651a0 --- /dev/null +++ b/vendor/libghostty-vt/src/cli/edit_config.zig @@ -0,0 +1,180 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assert = @import("../quirks.zig").inlineAssert; +const args = @import("args.zig"); +const Allocator = std.mem.Allocator; +const Action = @import("ghostty.zig").Action; +const configpkg = @import("../config.zig"); +const internal_os = @import("../os/main.zig"); +const Config = configpkg.Config; + +pub const Options = struct { + pub fn deinit(self: Options) void { + _ = self; + } + + /// Enables `-h` and `--help` to work. + pub fn help(self: Options) !void { + _ = self; + return Action.help_error; + } +}; + +/// The `edit-config` command opens the Ghostty configuration file in the +/// editor specified by the `$VISUAL` or `$EDITOR` environment variables. +/// +/// IMPORTANT: This command will not reload the configuration after +/// editing. You will need to manually reload the configuration using the +/// application menu, configured keybind, or by restarting Ghostty. We +/// plan to auto-reload in the future, but Ghostty isn't capable of +/// this yet. +/// +/// The filepath opened is the default user-specific configuration +/// file, which is typically located at `$XDG_CONFIG_HOME/ghostty/config.ghostty`. +/// On macOS, this may also be located at +/// `~/Library/Application Support/com.mitchellh.ghostty/config.ghostty`. +/// On macOS, whichever path exists and is non-empty will be prioritized, +/// prioritizing the Application Support directory if neither are +/// non-empty. +/// +/// This command prefers the `$VISUAL` environment variable over `$EDITOR`, +/// if both are set. If neither are set, it will print an error +/// and exit. +pub fn run(alloc: Allocator) !u8 { + // Implementation note (by @mitchellh): I do proper memory cleanup + // throughout this command, even though we plan on doing `exec`. + // I do this out of good hygiene in case we ever change this to + // not using `exec` anymore and because this command isn't performance + // critical where setting up the defer cleanup is a problem. + + var buffer: [1024]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&buffer); + const stderr = &stderr_writer.interface; + + var opts: Options = .{}; + defer opts.deinit(); + + { + var iter = try args.argsIterator(alloc); + defer iter.deinit(); + try args.parse(Options, alloc, &opts, &iter); + } + + const result = runInner(alloc, stderr); + // Flushing *shouldn't* fail but... + stderr.flush() catch {}; + return result; +} + +fn runInner(alloc: Allocator, stderr: *std.Io.Writer) !u8 { + // We load the configuration once because that will write our + // default configuration files to disk. We don't use the config. + var config = try Config.load(alloc); + defer config.deinit(); + + // Find the preferred path. + const path = try configpkg.preferredDefaultFilePath(alloc); + defer alloc.free(path); + + // We don't currently support Windows because we use the exec syscall. + if (comptime builtin.os.tag == .windows) { + try stderr.print( + \\The `ghostty +edit-config` command is not supported on Windows. + \\Please edit the configuration file manually at the following path: + \\ + \\{s} + \\ + , + .{path}, + ); + return 1; + } + + // Get our editor + const get_env_: ?internal_os.GetEnvResult = env: { + // VISUAL vs. EDITOR: https://unix.stackexchange.com/questions/4859/visual-vs-editor-what-s-the-difference + if (try internal_os.getenv(alloc, "VISUAL")) |v| { + if (v.value.len > 0) break :env v; + v.deinit(alloc); + } + + if (try internal_os.getenv(alloc, "EDITOR")) |v| { + if (v.value.len > 0) break :env v; + v.deinit(alloc); + } + + break :env null; + }; + defer if (get_env_) |v| v.deinit(alloc); + const editor: []const u8 = if (get_env_) |v| v.value else ""; + + // If we don't have `$EDITOR` set then we can't do anything + // but we can still print a helpful message. + if (editor.len == 0) { + try stderr.print( + \\The $EDITOR or $VISUAL environment variable is not set or is empty. + \\This environment variable is required to edit the Ghostty configuration + \\via this CLI command. + \\ + \\Please set the environment variable to your preferred terminal + \\text editor and try again. + \\ + \\If you prefer to edit the configuration file another way, + \\you can find the configuration file at the following path: + \\ + \\ + , + .{}, + ); + + // Output the path using the OSC8 sequence so that it is linked. + try stderr.print( + "\x1b]8;;file://{s}\x1b\\{s}\x1b]8;;\x1b\\\n", + .{ path, path }, + ); + + return 1; + } + + const command = command: { + var buffer: std.io.Writer.Allocating = .init(alloc); + defer buffer.deinit(); + const writer = &buffer.writer; + try writer.writeAll(editor); + try writer.writeByte(' '); + { + var sh: internal_os.ShellEscapeWriter = .init(writer); + try sh.writer.writeAll(path); + try sh.writer.flush(); + } + try writer.flush(); + break :command try buffer.toOwnedSliceSentinel(0); + }; + defer alloc.free(command); + + // We require libc because we want to use std.c.environ for envp + // and not have to build that ourselves. We can remove this + // limitation later but Ghostty already heavily requires libc + // so this is not a big deal. + comptime assert(builtin.link_libc); + + const err = std.posix.execvpeZ( + "/bin/sh", + &.{ "/bin/sh", "-c", command }, + std.c.environ, + ); + + // If we reached this point then exec failed. + try stderr.print( + \\Failed to execute the editor. Error code={}. + \\ + \\This is usually due to the executable path not existing, invalid + \\permissions, or the shell environment not being set up + \\correctly. + \\ + \\Editor: {s} + \\Path: {s} + \\ + , .{ err, editor, path }); + return 1; +} diff --git a/vendor/libghostty-vt/src/cli/explain_config.zig b/vendor/libghostty-vt/src/cli/explain_config.zig new file mode 100644 index 00000000..4f034afe --- /dev/null +++ b/vendor/libghostty-vt/src/cli/explain_config.zig @@ -0,0 +1,151 @@ +const std = @import("std"); +const args = @import("args.zig"); +const Allocator = std.mem.Allocator; +const Action = @import("ghostty.zig").Action; +const help_strings = @import("help_strings"); +const Config = @import("../config/Config.zig"); +const ConfigKey = @import("../config/key.zig").Key; +const KeybindAction = @import("../input/Binding.zig").Action; +const Pager = @import("Pager.zig"); + +pub const Options = struct { + /// The config option to explain. For example: + /// + /// ghostty +explain-config --option=font-size + option: ?[]const u8 = null, + + /// The keybind action to explain. For example: + /// + /// ghostty +explain-config --keybind=copy_to_clipboard + keybind: ?[]const u8 = null, + + pub fn deinit(self: Options) void { + _ = self; + } + + /// Enables `-h` and `--help` to work. + pub fn help(self: Options) !void { + _ = self; + return Action.help_error; + } +}; + +/// The `explain-config` command prints the documentation for a single +/// Ghostty configuration option or keybind action. +/// +/// Examples: +/// +/// ghostty +explain-config font-size +/// ghostty +explain-config copy_to_clipboard +/// ghostty +explain-config --option=font-size +/// ghostty +explain-config --keybind=copy_to_clipboard +/// +/// Flags: +/// +/// * `--option`: The name of the configuration option to explain. +/// * `--keybind`: The name of the keybind action to explain. +/// * `--no-pager`: Disable automatic paging of output. +pub fn run(alloc: Allocator) !u8 { + var option_name: ?[]const u8 = null; + var keybind_name: ?[]const u8 = null; + var positional: ?[]const u8 = null; + var no_pager: bool = false; + + var iter = try args.argsIterator(alloc); + defer iter.deinit(); + defer if (option_name) |s| alloc.free(s); + defer if (keybind_name) |s| alloc.free(s); + defer if (positional) |s| alloc.free(s); + + while (iter.next()) |arg| { + if (std.mem.startsWith(u8, arg, "--option=")) { + option_name = try alloc.dupe(u8, arg["--option=".len..]); + } else if (std.mem.startsWith(u8, arg, "--keybind=")) { + keybind_name = try alloc.dupe(u8, arg["--keybind=".len..]); + } else if (std.mem.eql(u8, arg, "--no-pager")) { + no_pager = true; + } else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) { + return Action.help_error; + } else if (!std.mem.startsWith(u8, arg, "-")) { + positional = try alloc.dupe(u8, arg); + } + } + + // Resolve what to look up. Explicit flags go directly to their + // respective lookup. A bare positional argument tries config + // options first, then keybind actions as a fallback. + const name = keybind_name orelse option_name orelse positional orelse { + var stderr: std.fs.File = .stderr(); + var buffer: [4096]u8 = undefined; + var stderr_writer = stderr.writer(&buffer); + try stderr_writer.interface.writeAll("Usage: ghostty +explain-config