`TabView` assumes to be the sole owner of all `Tab`s within a Window.
As such, it could close the managed `Window` once all tabs are removed
from its widget.
However, during `AdwTabView::close-page` signal triggered by libadwaita,
the `Tab` to be closed will gain an another reference for the duration
of the signal, breaking `TabView.closeTab` (called via
`Tab.closeWithConfirmation`) assumptions that having no tabs meant they
are all destroyed.
This commit solves the issue by scheduling `Tab.closeWithConfirmation`
to be run after `AdwTabView::close-page` signal has finished processing.
This is done to match against the default application id when Ghostty is
built using debug configuration, preparing the Flatpak version for D-Bus
activation support (#7433).
This is done to match against the default application id when Ghostty is
built using debug configuration, done to prepare the Flatpak version for
D-Bus activation support.
First, remove the always-inlined openTerminalFromPasteboard code and
combine it with openTerminal. Now that we're doing a bit of work inside
openTerminal, there's little better to having an intermediate, inlined
function.
Second, combine some type-casting operations (saving a .map() call).
Lastly, adjust some variable names because a generic `objs` or `urls`
was a little ambiguous now that we're all in one function scope.
This fixes a small memory leak I found where the `SplitNode.Leaf` was
not being deinitialized properly when closing a split. It would get
deinitialized the next time a split was made or the window was closed,
so the leak wasn't big. The surface view underneath the split was also
properly deinitialized because we forced it, so again, the leak was
quite small.
But conceptually this is a big problem, because when we change the
surface tree we expect the deinit chain to propagate properly through
the whole thing, _including_ to the SurfaceView.
This fixes that by removing the `id(node)` call. I don't find this to be
necessary anymore. I don't know when that happened but we've changed
quite a lot in our split system since it was introduced. I'm also not
100% sure why the `id(node)` was causing a strong reference to begin
with... which bothers me a bit.
AI note: While I manually hunted this down, I started up Claude Code and
Codex in separate tabs to also hunt for the memory leak. They both
failed to find it and offered solutions that didn't work.
This fixes a small memory leak I found where the `SplitNode.Leaf` was
not being deinitialized properly when closing a split. It would get
deinitialized the next time a split was made or the window was closed,
so the leak wasn't big. The surface view underneath the split was also
properly deinitialized because we forced it, so again, the leak was
quite small.
But conceptually this is a big problem, because when we change the
surface tree we expect the deinit chain to propagate properly through
the whole thing, _including_ to the SurfaceView.
This fixes that by removing the `id(node)` call. I don't find this to be
necessary anymore. I don't know when that happened but we've changed
quite a lot in our split system since it was introduced. I'm also not
100% sure why the `id(node)` was causing a strong reference to begin
with... which bothers me a bit.
AI note: While I manually hunted this down, I started up Claude Code and
Codex in separate tabs to also hunt for the memory leak. They both
failed to find it and offered solutions that didn't work.
Just a quick follow-up to #7462: I noticed Swift was admonishing me
about a `var` that could be a `let`, and realized that it was I who had
failed to make that change. Apologies for the noise.
This is the conversion of #7496 into a PR. The problem was discussed in
#3288: When `macos-titlebar-style = tabs`, the reset zoom button doesn't
appear with only a single tab, i.e., no tab bar.
This fix was simply to remove an unnecessary branch, as the titlebar
style doesn't make any difference for the required logic. If the tab bar
is visible, you should unconditionally hide the titlebar's button and
rely on the per-tab buttons. If the tab bar is not visible, you should
hide or show the titlebar button depending on `surfaceIsZoomed`.
One wrinkle was that the main toolbar title started showing on top of
the tabs. Turns out it was previously not showing only because it would
always be pushed into the hidden overflow menu. With the reset zoom
button present (either hidden or visible, but always there), this no
longer happens reliably, presumably due to some cascading change in the
overflow calculations. In any case, it seems brittle to rely on this way
of concealing the main title, so I added a few lines of code to properly
hide it when the tab bar is visible.
Screenshots:



Related to #7433
This extracts our "launched from desktop" logic into a config option.
The default value is detection using the same logic as before, but now
this can be overridden by the user.
This also adds the systemd and dbus activation sources from #7433.
There are a number of reasons why we decided to do this:
1. It automatically gets us caching since the configuration is only
loaded once (per reload, a rare occurrence).
2. It allows us to override the logic when testing. Previously, we had
to do more complex environment faking to get the same behavior.
3. It forces exhaustive switches in any desktop handling code, which
will make it easier to ensure valid behaviors if we introduce new launch
sources (as we are in #7433).
4. It lowers code complexity since callsites don't need to have N
`launchedFromX()` checks and can use a single value.
Related to #7433
This extracts our "launched from desktop" logic into a config option.
The default value is detection using the same logic as before, but now
this can be overridden by the user.
This also adds the systemd and dbus activation sources from #7433.
There are a number of reasons why we decided to do this:
1. It automatically gets us caching since the configuration is only
loaded once (per reload, a rare occurrence).
2. It allows us to override the logic when testing. Previously, we
had to do more complex environment faking to get the same
behavior.
3. It forces exhaustive switches in any desktop handling code, which
will make it easier to ensure valid behaviors if we introduce new
launch sources (as we are in #7433).
4. It lowers code complexity since callsites don't need to have N
`launchedFromX()` checks and can use a single value.
Fixes#6766
This ensures that during surface deinit the cgroup is removed. By the
time the surface is deinitialized, the subprocess should already be dead
so the cgroup can be safely removed. If the cgroup cannot be removed for
any reason we log a warning.
I'm still investigating whether we also need to remove the transient
cgroup we create per app but that's a lot less noisy since app startup
and shutdown is a lot more rare.
Fixes#6766
This ensures that during surface deinit the cgroup is removed. By the
time the surface is deinitialized, the subprocess should already be
dead so the cgroup can be safely removed. If the cgroup cannot be
removed for any reason we log a warning.
This should make the sorting more robust to fonts with questionable
metadata or atypical style names.
I was originally just going to change the scoring slightly to account
for fonts whose regular italic style is named "Regular Italic" - which
previously resulted in the Bold Italic or Thin Italic style being chosen
instead because they're shorter names, but I decided to do some better
inspection of the metadata and looser style name matching while I was
changing code here anyway.
Also adds a unit test to verify the sorting works correctly, though a
more comprehensive set of tests may be desirable in the future.
(Feel free to make any changes to the PR you feel necessary before
merging while I'm gone over the weekend.)
This improves "outer edge" alignment of octants and other elements drawn
using `yQuads` and friends with blocks drawn with `draw_block` -- this
should guarantee alignment along a continuous edge, but may result in a
1px overlap of opposing edges (such as a top half block followed by a
bottom half block with an odd cell height, they will both have the
center row filled).
This is very necessary since several block elements are needed to
complete the set of octants, since dedicated octant characters aren't
included when they would be redundant.
Fixes#7479
<details>
<summary><b><code>Box.ppm</code> diff</b></summary>

</details>
> [!NOTE]
> In the future I think we should have a unified single source of truth
for grid positions (divisions of the cell) to ensure this type of thing
can't happen with other characters, and also it would make a lot of the
code cleaner. For now this works though.
This improves "outer edge" alignment of octants and other elements drawn
using `yQuads` and friends with blocks drawn with `draw_block` -- this
should guarantee alignment along a continuous edge, but may result in a
1px overlap of opposing edges (such as a top half block followed by a
bottom half block with an odd cell height, they will both have the
center row filled).
This is very necessary since several block elements are needed to
complete the set of octants, since dedicated octant characters aren't
included when they would be redundant.
Fixes#7480
I regret to inform the haters that an AI agent did this while I was out
getting a coffee. I only had to follow up once to ask it rename the
method to be more idiomatic. I absolutely didn't need AI to do this, but
I was leaving the office _anyways_ and figured why not. Got back and it
was done. What a time to be alive.
I'm not sure if this should be enabled by default like the tab
animation, but on KDE at least this is unintrusive enough for me to
always enable by default. Alacritty appears to agree with me as well.
Fixes#7124
This should make the sorting more robust to fonts with questionable
metadata or atypical style names.
I was originally just going to change the scoring slightly to account
for fonts whose regular italic style is named "Regular Italic" - which
previously resulted in the Bold Italic or Thin Italic style being chosen
instead because they're shorter names, but I decided to do some better
inspection of the metadata and looser style name matching while I was
changing code here anyway.
Also adds a unit test to verify the sorting works correctly, though a
more comprehensive set of tests may be desirable in the future.
Fixes#7481
We unfortunately don't have a great way to unit test this since our
logic relies on argv and I'm too lazy to extract it out right now.
The core issue is that we previously detected if font-families changed
by comparing lengths. This doesn't work because the CLI args can reset
and add families back to a lesser length. This caused an integer
overflow.
We can fix this by not being clever and introducing the overwrite logic
directly into the config type. I unit tested that.
Fixes#7481
We unfortunately don't have a great way to unit test this since our
logic relies on argv and I'm too lazy to extract it out right now.
The core issue is that we previously detected if font-families changed
by comparing lengths. This doesn't work because the CLI args can reset
and add families back to a lesser length. This caused an integer
overflow.
We can fix this by not being clever and introducing the overwrite logic
directly into the config type. I unit tested that.
I'm not sure if this should be enabled by default like the tab
animation, but on KDE at least this is unintrusive enough for me to
always enable by default. Alacritty appears to agree with me as well.
Even though gtk4-layer-shell's documentation claims that "nobody quite
knows what it's for", some compositors (like Niri) can define custom
rules based on the layer name and it's beneficial in those cases to
define a distinct name just for our quick terminals.
The default keybinds for showing the GTK inspector (`ctrl+shift+i` and
`ctrl+shift+d`) don't work reliably in Ghostty due to the way Ghostty
handles input. You can show the GTK inspector by setting the environment
variable `GTK_DEBUG` to `interactive` before starting Ghostty but that's
not always convenient.
This adds a keybind action that will show the GTK inspector. Due to API
limitations toggling the GTK inspector using the keybind action is
impractical because GTK does not provide a convenient API to determine
if the GTK inspector is already showing. Thus we limit ourselves to
strictly showing the GTK inspector. To close the GTK inspector the user
must click the close button on the GTK inspector window. If the GTK
inspector window is already visible but is hidden, calling the keybind
action will not bring the GTK inspector window to the front.
This brings the behavior of mode 47, 1047, and 1049 much closer to
xterm's behavior. I found that our prior implementation had many
deficiencies.
For example, we weren't properly copying the cursor state back to the
primary screen from the alternate screen for modes 47 and 1047. And we
weren't saving/restoring cursor state unconditionally for mode 1049 even
if we were already in the alternate screen.
I also found that we were dangling hyperlink state on the primary screen
when we switched to alternate. xterm doesn't support hyperlinks but we
did the opposite behavior for going from alternate to primary. So one
way or the other its a bug. I'm worried this one could've maybe led to
memory corruption under the right circumstances but I wasn't able to
prove it.
These are weird, edgy behaviors that I don't think anyone expected
(evidence by there being no bug reports about them), but they are bugs
nontheless.
Many tests added.
(Btw: this flew under the radar of our "xterm audit" because that only
included sequences and not modes. I noted this in the audit issue itself
but just an FYI.)
Related to #7468
This changes the behavior of "ignore". Previously, Ghostty would
consider "ignore" actions consumed but do nothing. They were like a
black hole. Now, Ghostty returns `ignored` which lets the apprt forward
the event to the OS/GUI.
This enables keys that would otherwise be pty-encoded to be processed
later, such as for GTK to show the GTK inspector.
Related to #7468
This changes the behavior of "ignore". Previously, Ghostty would
consider "ignore" actions consumed but do nothing. They were like a
black hole. Now, Ghostty returns `ignored` which lets the apprt forward
the event to the OS/GUI.
This enables keys that would otherwise be pty-encoded to be processed
later, such as for GTK to show the GTK inspector.
This brings the behavior of mode 47, 1047, and 1049 much closer to
xterm's behavior. I found that our prior implementation had many
deficiencies.
For example, we weren't properly copying the cursor state back to the
primary screen from the alternate screen for modes 47 and 1047. And we
weren't saving/restoring cursor state unconditionally for mode 1049 even
if we were already in the alternate screen.
These are weird, edgy behaviors that I don't think anyone expected
(evidence by there being no bug reports about them), but they are bugs
nontheless.
Many tests added.
This fixes an issue where Ghostty would not build against the macOS 15.5
SDK.
What was happening was that Zig was adding its embedded libc paths to
the clang command line, which included old headers that were
incompatible with the latest (macOS 15.5) SDK. Ghostty was adding the
newer paths but they were being overridden by the embedded libc paths.
The reason this was happening is because Zig was using its own logic to
find the libc paths and this was colliding with the paths we were
setting manually. To fix this, we now use a `libc.txt` file that
explicitly tells Zig where to find libc, and we base this on our own SDK
search logic.
This fixes an issue where Ghostty would not build against the macOS 15.5 SDK.
What was happening was that Zig was adding its embedded libc paths to
the clang command line, which included old headers that were
incompatible with the latest (macOS 15.5) SDK. Ghostty was adding the
newer paths but they were being overridden by the embedded libc paths.
The reason this was happening is because Zig was using its own logic to
find the libc paths and this was colliding with the paths we were
setting manually. To fix this, we now use a `libc.txt` file that
explicitly tells Zig where to find libc, and we base this on our own SDK
search logic.
The default keybinds for showing the GTK inspector (`ctrl+shift+i` and
`ctrl+shift+d`) don't work reliably in Ghostty due to the way Ghostty
handles input. You can show the GTK inspector by setting the environment
variable `GTK_DEBUG` to `interactive` before starting Ghostty but that's
not always convenient.
This adds a keybind action that will show the GTK inspector. Due to
API limitations toggling the GTK inspector using the keybind action is
impractical because GTK does not provide a convenient API to determine
if the GTK inspector is already showing. Thus we limit ourselves to
strictly showing the GTK inspector. To close the GTK inspector the user
must click the close button on the GTK inspector window. If the GTK
inspector window is already visible but is hidden, calling the keybind
action will not bring the GTK inspector window to the front.