720 Commits

Author SHA1 Message Date
Mitchell Hashimoto
ee963f6296 macOS: fix invalid kitty keyboard encoding of control characters (#5747)
Fixes #5743 

This fixes a terrible regression where by fixing one issue we introduced
another, and the other is that ctrl keys didn't work with programs with
Kitty keyboard protocol.

The problem is that our fix blindly assumed control was always consumed
for translation, which is obviously wrong but I didn't think there'd be
downstream effects. The reality is that we need to be more accurate.

This PR makes it so that:

* If macOS provides us with UTF-8 text, we assume all mods were involved
except super
* If macOS does not provide us with UTF-8 text, we use whatever
UCKeyTranslate consumed
* We never allow UCKeyTranslate to consume control because it turns it
into the masked ASCII value and we don't want that.

The only _new_ behavior which fixes the bug is point 2 above. 

Tested:

  1. Dvorak Ctrl characters
  2. Ergo-L Ctrl characters
  3. US standard Ctrl characters
  4. Japanese IME input Ctrl input to modify IME state
5. Ctrl keybindings with Kitty keyboard protocol in both Kitty and
Neovim
2025-02-13 15:03:54 -08:00
Mitchell Hashimoto
b44b1086d3 apprt/embedded: proper consumed modifier state for ctrl keys 2025-02-13 14:58:41 -08:00
Mitchell Hashimoto
9978ea3b9c Revert "macos: don't remove ctrl modifier for text input"
This reverts commit 3104b21758.
2025-02-13 14:20:13 -08:00
Mitchell Hashimoto
710ea1c8d9 up all the metadata to 1.1.2 2025-02-13 13:02:06 -08:00
Mitchell Hashimoto
ed60e07257 apprt: require envmap for exec-based termio (#5742)
Supercedes #5726
2025-02-13 12:27:13 -08:00
Mitchell Hashimoto
1fea8028a3 apprt: require envmap for exec-based termio
Supercedes #5726
2025-02-13 12:26:11 -08:00
Mitchell Hashimoto
c481bdf704 macos: make goto_split, goto_tab, and move_tab performable (#5740)
Fixes #5552

This makes the mentioned actions performable. This isn't perfect, but it
does so in a way that resolves the user issue in #5552. This commit
returns an action is NOT performed if it doesn't have splits or tabs
(respectiely for the actions), but also reports its ALWAYS performed if
it does.

This latter logic isn't accurate: we should only return performable if
it was actually done. So for example, goto_split:top should do nothing
if we're already at the top. But, we report it as performed today.

This is good enough to resolve the issue and fix the core problem faced
for 1.1.0.
2025-02-13 10:59:04 -08:00
Mitchell Hashimoto
5105c52ef7 macos: make goto_split, goto_tab, and move_tab performable
Fixes #5552

This makes the mentioned actions performable. This isn't perfect, but it
does so in a way that resolves the user issue in #5552. This commit
returns an action is NOT performed if it doesn't have splits or tabs
(respectiely for the actions), but also reports its ALWAYS performed if
it does.

This latter logic isn't accurate: we should only return performable if
it was actually done. So for example, goto_split:top should do nothing
if we're already at the top. But, we report it as performed today.

This is good enough to resolve the issue and fix the core problem faced
for 1.1.0.
2025-02-13 10:40:07 -08:00
Mitchell Hashimoto
20dcae876d macos: don't remove ctrl modifier for text input (#5738)
Fixes #5448

We previously removed the ctrl modifier for text commit (IME-style) to
workaround a libghostty quirk (as noted in the comment in the diff). But
this broke other keyboard layouts.

This commit attempts to clean this up slightly -- but not completely --
by removing that hack, and only modifying the ctrl behavior for the
UCKeyTranslate call.

Long term, I plan to remove UCKeyTranslate completely, as noted in the
todo comment already written just below this diff.

This fixes the aforementioned issue and hopefully doesn't regress any
other behavior. I tested the following:

  1. Dvorak Ctrl characters
  2. Ergo-L Ctrl characters
  3. US standard Ctrl characters
  4. Japanese IME input Ctrl input to modify IME state
2025-02-13 09:51:26 -08:00
Mitchell Hashimoto
3104b21758 macos: don't remove ctrl modifier for text input
Fixes #5448

We previously removed the ctrl modifier for text commit (IME-style)
to workaround a libghostty quirk (as noted in the comment in the diff).
But this broke other keyboard layouts.

This commit attempts to clean this up slightly -- but not completely --
by removing that hack, and only modifying the ctrl behavior for the
UCKeyTranslate call.

Long term, I plan to remove UCKeyTranslate completely, as noted in the
todo comment already written just below this diff.

This fixes the aforementioned issue and hopefully doesn't regress any
other behavior. I tested the following:

  1. Dvorak Ctrl characters
  2. Ergo-L Ctrl characters
  3. US standard Ctrl characters
  4. Japanese IME input Ctrl input to modify IME state
2025-02-13 09:43:07 -08:00
Mitchell Hashimoto
15e41959eb Eliminate tab content flickering during tab movement on macOS (#5729)
Fixes https://github.com/ghostty-org/ghostty/issues/5689

The flickering was caused by uncontrolled visual updates during tab
reordering. The fix uses `NSAnimationContext` to batch all visual
changes together and simplifies window focus handling to prevent
unnecessary redraws.


https://github.com/user-attachments/assets/2a6525cd-8a97-418b-8442-18dab6b4636d
2025-02-13 09:24:41 -08:00
Mitchell Hashimoto
783f4014f8 build: replace codeberg dep with self-hosted mirror (#5736)
The content hashes match so if users don't trust us they can grab from
upstream.
2025-02-13 09:24:30 -08:00
Mitchell Hashimoto
2a29f71b2c draw octants directly rather than relying on font (#5433)
* implement `yQuads()` and `draw_octant()`, pretty obvious extensions of
existing code. to allocate up to 3 potential remainder lines, consider
that octants will often appear in a rectangular subset of the terminal.
we want the distributed excess uniformly distributed across such a
region. so:
* one excess row: break symmetry in any direction (pick an arbitrary
tetrad and use it everywhere)
  * two excess rows: go to alternating tetrads
* three excess rows: break symmetry, do not use three contiguous tetrads
* our `Octant`s are octary arrays of `bool`, provided as a somewhat
opaque constant table
* the 8-line copy-and-paste draw based on the `Octant` is not the
prettiest thing in the known universe
* we could generalize `draw_sextant()` and `draw_octant()` like
notcurses did, almost certainly
  * oh bird thou never wert
 

with that said, i don't think `draw_octant()` is actually being called
lol, so let's not merge this yet. happy to hear early feedback, though.
2025-02-13 09:21:11 -08:00
Mitchell Hashimoto
5919c57527 build: replace codeberg dep with self-hosted mirror
The content hashes match so if users don't trust us they can grab from
upstream.
2025-02-13 09:18:50 -08:00
nick black
8b2f9acfb4 Implement draw_octant() and yQuads() for U16.0 2025-02-13 09:08:33 -08:00
Mitchell Hashimoto
6f4716fd60 macOS: update additional references to Sparkle 2.6.3->2.6.4 (#5734) 2025-02-13 08:49:28 -08:00
Jeffrey C. Ollie
74f222abec macOS: update additional references to Sparkle 2.6.3->2.6.4 2025-02-13 10:35:38 -06:00
Jeffrey C. Ollie
9f6067d899 gtk: fix ui/blueprint builder (#5727)
fixes issues found while making use of the new builder api
2025-02-13 08:03:49 -06:00
Bryan Lee
f72fd32bf0 Eliminate tab content flickering during tab movement on macOS 2025-02-13 14:59:14 +08:00
Jeffrey C. Ollie
e396c8538a gtk: fix ui/blueprint builder 2025-02-12 22:16:33 -06:00
Jeffrey C. Ollie
432beac315 gtk: add support for using GTK Builder UI files and Blueprints (#5714)
Adds buildtime and comptime checks to make sure that Blueprints/UI files
are availble and correctly formed. Will also compile Blueprints to UI
files so that they are available to GTK code.
2025-02-12 19:10:50 -06:00
Mitchell Hashimoto
256281c5de terminal: reflow the saved cursor if we have one (#5720)
Fixes #5718

When a terminal is resized with text reflow (i.e. soft-wrapped text),
the cursor is generally reflowed with it.

For example, imagine a terminal window 5-columns wide and you type the
following without pressing enter. The cursor is on the X.

```
OOOOO
OOX
```

If you resize the window now to 8 or more columns, this happens, as
expected:

```
OOOOOOOX
```

As expected, the cursor remains on the "X". This behaves like any other
text input...

Terminals also provide an escape sequence to
[save the cursor (ESC 7 aka
DECSC)](https://ghostty.org/docs/vt/esc/decsc). This includes, amongst
other things, the cursor position. The cursor can be restored with
[DECRC](https://ghostty.org/docs/vt/esc/decrc).

The behavior of the position of the _saved cursor_ in the context of
text reflow is unspecified and varies wildly between terminals Ghostty
does this right now (as do many other terminals):

```
OOOOOOOO
   X
```

This commit changes the behavior so that we reflow the saved cursor.
2025-02-12 13:16:11 -08:00
Jeffrey C. Ollie
f63242f7fb gtk: add support for using GTK Builder UI files and Blueprints
Adds buildtime and comptime checks to make sure that Blueprints/UI files
are availble and correctly formed. Will also compile Blueprints to UI
files so that they are available to GTK code.
2025-02-12 15:05:42 -06:00
Mitchell Hashimoto
7dac9e02b3 terminal: reflow the saved cursor if we have one
Fixes #5718

When a terminal is resized with text reflow (i.e. soft-wrapped text), the cursor
is generally reflowed with it.

For example, imagine a terminal window 5-columns wide and you type the
following without pressing enter. The cursor is on the X.

```
OOOOO
OOX
```

If you resize the window now to 8 or more columns, this happens, as expected:

```
OOOOOOOX
```

As expected, the cursor remains on the "X". This behaves like any other text
input...

Terminals also provide an escape sequence to
[save the cursor (ESC 7 aka DECSC)](https://ghostty.org/docs/vt/esc/decsc).
This includes, amongst other things, the cursor position. The cursor can be
restored with [DECRC](https://ghostty.org/docs/vt/esc/decrc).

The behavior of the position of the _saved cursor_ in the context of text
reflow is unspecified and varies wildly between terminals Ghostty does this
right now (as do many other terminals):

```
OOOOOOOO
   X
```

This commit changes the behavior so that we reflow the saved cursor.
2025-02-12 10:34:31 -08:00
Jeffrey C. Ollie
99cbc06292 gtk: use the standard path for including Adwaita header file (#5716)
As pointed out by @tristan957 the standard path for including the
Adwaita header file is simply "adwaita.h". While it may have been
necessary in the past to use a non-standard include path, that no longer
appears to be the case.
2025-02-12 11:29:38 -06:00
Jeffrey C. Ollie
749bac3d72 gtk: use the standard path for including Adwaita header file
As pointed out by @tristan957 the standard path for including the
Adwaita header file is simply "adwaita.h". While it may have been
necessary in the past to use a non-standard include path, that no longer
appears to be the case.
2025-02-12 11:14:38 -06:00
Mitchell Hashimoto
1bb87e14a5 termio: free envmap when able, fix memory leak (#5715)
Caused by #5650

I actually don't understand how this didn't happen before or why we
didn't notice it but it seems like the envmap was never freed. In the
latest debug builds prior to this build GPA reports the leak.

We should free the envmap when the subprocess is deinitialized. But also
we can free the env map as soon as we start the subprocess which saves
some small amount of memory at runtime.

Additionally, we should only be freeing the envmap on error if we
created it.
2025-02-12 09:00:56 -08:00
Mitchell Hashimoto
9ea29dc508 termio: free envmap when able, fix memory leak
Caused by #5650

I actually don't understand how this didn't happen before or why we
didn't notice it but it seems like the envmap was never freed. In the
latest debug builds prior to this build GPA reports the leak.

We should free the envmap when the subprocess is deinitialized. But also
we can free the env map as soon as we start the subprocess which saves
some small amount of memory at runtime.

Additionally, we should only be freeing the envmap on error if we
created it.
2025-02-12 08:48:26 -08:00
Mitchell Hashimoto
f2f0c6b4e9 macOS: use Nix to get Zig deps (#5699)
This should help with API rate limits being hit by macOS builders since
they can't use the Namespace cache that Linux builders can use to cache
Zig dependencies. It will need to be run by Mitchell once so that the
Cachix action can push everything up to the cache and then the full
benefits should be seen. Not sure how using `--system` on all the macOS
builds will affect things overall but it doesn't seem to have affected
the CI.
2025-02-12 07:17:13 -08:00
Jeffrey C. Ollie
1e3cd89516 macOS: use Nix to get Zig deps 2025-02-12 08:21:16 -06:00
Mitchell Hashimoto
019f789819 macOS: fix iOS build breakage from #5644 (#5713) 2025-02-12 06:18:06 -08:00
Jeffrey C. Ollie
9bac6ecbff macOS: fix iOS build breakage from #5644 2025-02-12 07:54:50 -06:00
Leah Amelia Chen
76ab6717c9 gtk: fix context menu computed location (#5710)
When a tab bar is displayed, the context menu opened with right click is
offset from the cursor.

This was due to using the incorrect coordinate space for describing
where to draw the context menu.


[gtk_popover_set_pointing_to](https://docs.gtk.org/gtk4/method.Popover.set_pointing_to.html)
uses the coordinate space of the popover's parent, however we used the
active window's coordinate space which was noticeably different when the
tab bar is visible.

Before:
![Screenshot_12-Feb_22-39-07_com mitchellh
ghostty](https://github.com/user-attachments/assets/5263dd9d-f3ab-44f6-baf2-7208d3d3bd01)
After:
![Screenshot_12-Feb_22-41-41_com mitchellh
ghostty-debug](https://github.com/user-attachments/assets/8d3e8bbd-af4e-43a6-a52d-6601a0b66ece)
2025-02-12 13:08:33 +01:00
azhn
b1ae7eea2f gtk: fix context menu computed location 2025-02-12 22:46:00 +11:00
azhn
c061d06aa1 Revert "gtk: Point right-click context menu exactly at cursor to improve behaviour at edges and closeness to cursor"
This reverts commit bf5351ed41.
2025-02-12 19:50:32 +11:00
azhn
bf5351ed41 gtk: Point right-click context menu exactly at cursor to improve behaviour at edges and closeness to cursor 2025-02-12 19:02:56 +11:00
Mitchell Hashimoto
338a07ecad Performable tab and split navigation (#5644)
Partial fixes #5552 (for GTK).

This PR adds the core infrastructure for keybind actions that are
implemented as runtime app actions to be performable. This is done by
having `rt_app.performAction` return a boolean. By default all runtime
app actions return `true` (the action was performed) unless they are
modified to return `true`/`false` as appropriate.

The GTK apprt is modified so that `goto_split`, `previous_tab`,
`next_tab`, `last_tab`, and `goto_tab` are performable. macOS support
will need to be added in a subsequent commit.

This doesn't completely solve the issue for the OP because if the
`goto_split` isn't performable there is no fallback to`previous_tab` or
`next_tab`.

I don't think that the approach taken in #5579 is the right one as it
conflates split and tab navigation unconditionally which I don't think
is what everyone would want. Either a separate action that explicitly
combines the actions or a solution to #3175 will be the ultimate
solution I believe.
2025-02-11 17:03:39 -08:00
Mitchell Hashimoto
f26c96d51b apprt/glfw: return false for unimplemented actions 2025-02-11 16:53:04 -08:00
Mitchell Hashimoto
927cb4e622 apprt/gtk: some stylistic changes 2025-02-11 16:52:31 -08:00
Mitchell Hashimoto
cbf562ecb3 apprt/embedded: make performAction return the performable state 2025-02-11 16:43:50 -08:00
Jeffrey C. Ollie
69fd438370 gtk: make previous_tab, next_tab, last_tab, and goto_tab performable 2025-02-11 16:43:50 -08:00
Jeffrey C. Ollie
57e7565b7f gtk: make goto_split a performable action 2025-02-11 16:43:50 -08:00
Jeffrey C. Ollie
4ad749607a core: performAction now returns a bool
This is to facilitate the `performable:` prefix on keybinds that are
implemented using app runtime actions.
2025-02-11 16:43:50 -08:00
Mitchell Hashimoto
66442cd469 Refactor keybinding actions reference generation (#5695)
https://github.com/ghostty-org/ghostty/pull/4974#issuecomment-2652048815
2025-02-11 15:05:22 -08:00
Mitchell Hashimoto
afc42fe891 macos: add a variety of artist-drawn alternate icons (#5696)
This is just a fun change to add a bunch of alternate icons. We don't
want to add too many since this increases the final bundle size but we
also want to have some fun. :)


![Ghostty-More-Alts](https://github.com/user-attachments/assets/2bf228fb-7a1c-4341-a4a1-e9daca93d1c5)
2025-02-11 15:03:55 -08:00
Mitchell Hashimoto
58ab66f094 macos: add a variety of artist-drawn alternate icons
This is just a fun change to add a bunch of alternate icons. We don't
want to add too many since this increases the final bundle size but we
also want to have some fun. :)
2025-02-11 14:51:43 -08:00
Bryan Lee
1674224c1a Refactor keybinding actions reference generation 2025-02-12 06:29:40 +08:00
Mitchell Hashimoto
4aa452a2c7 gtk(x11): set WINDOWID env var for subprocesses (#5650)
`WINDOWID` is the conventional environment variable for scripts that
want to know the X11 window ID of the terminal, so that it may call
tools like `xprop` or `xdotool`. We already know the window ID for
window protocol handling, so we might as well throw this in for
convenience.

Originally suggested by #4299
2025-02-11 13:55:30 -08:00
Leah Amelia Chen
56ea6c406c gtk(x11): set WINDOWID env var for subprocesses
`WINDOWID` is the conventional environment variable for scripts that
want to know the X11 window ID of the terminal, so that it may call
tools like `xprop` or `xdotool`. We already know the window ID for
window protocol handling, so we might as well throw this in for
convenience.
2025-02-11 13:42:12 -08:00
Mitchell Hashimoto
f8b547f92e Revert "Fixed documentation generation in list-actions --docs command (#4974)"
This reverts commit f3d0c7c2ad, reversing
changes made to 4b77a1c71e.
2025-02-11 12:55:40 -08:00
Mitchell Hashimoto
adb187685b Fix confirm-close-surface not working for hidden quick terminal (#5647)
Fixes #5450.


https://github.com/user-attachments/assets/7090fe55-dd1f-4517-9f8a-ffc3807283db
2025-02-11 12:55:20 -08:00
Mitchell Hashimoto
f3d0c7c2ad Fixed documentation generation in list-actions --docs command (#4974)
Fixes https://github.com/ghostty-org/ghostty/issues/4958

## Changes

1. Fixed documentation generation in `actions.mdx`:

- Fixed an issue where the last action's documentation was [not properly
generated](fe6c69263c/docs/config/keybind/reference.mdx (crash))
   
- Ensured all actions' documentation is correctly included in the output

2. Improved `ghostty +list-actions --docs` command output formatting:

   - Grouped related actions together with shared documentation
   
   - Added proper spacing between action groups
  
<details> 
    <summary>ghostty-dev +list-actions --docs</summary>

```
ignore:
  Ignore this key combination, don't send it to the child process, just
  black hole it.

unbind:
  This action is used to flag that the binding should be removed from
  the set. This should never exist in an active set and `set.put` has an
  assertion to verify this.

csi:
  Send a CSI sequence. The value should be the CSI sequence without the
  CSI header (`ESC [` or `\x1b[`).

esc:
  Send an `ESC` sequence.

text:
  Send the given text. Uses Zig string literal syntax. This is currently
  not validated. If the text is invalid (i.e. contains an invalid escape
  sequence), the error will currently only show up in logs.

cursor_key:
  Send data to the pty depending on whether cursor key mode is enabled
  (`application`) or disabled (`normal`).

reset:
  Reset the terminal. This can fix a lot of issues when a running
  program puts the terminal into a broken state. This is equivalent to
  when you type "reset" and press enter.

  If you do this while in a TUI program such as vim, this may break
  the program. If you do this while in a shell, you may have to press
  enter after to get a new prompt.

copy_to_clipboard:
paste_from_clipboard:
paste_from_selection:
  Copy and paste.

copy_url_to_clipboard:
  Copy the URL under the cursor to the clipboard. If there is no
  URL under the cursor, this does nothing.

increase_font_size:
decrease_font_size:
  Increase/decrease the font size by a certain amount.

reset_font_size:
  Reset the font size to the original configured size.

clear_screen:
  Clear the screen. This also clears all scrollback.

select_all:
  Select all text on the screen.

scroll_to_top:
scroll_to_bottom:
scroll_page_up:
scroll_page_down:
scroll_page_fractional:
scroll_page_lines:
  Scroll the screen varying amounts.

adjust_selection:
  Adjust an existing selection in a given direction. This action
  does nothing if there is no active selection.

jump_to_prompt:
  Jump the viewport forward or back by prompt. Positive number is the
  number of prompts to jump forward, negative is backwards.

write_scrollback_file:
  Write the entire scrollback into a temporary file. The action
  determines what to do with the filepath. Valid values are:

    - "paste": Paste the file path into the terminal.
    - "open": Open the file in the default OS editor for text files.
      The default OS editor is determined by using `open` on macOS
      and `xdg-open` on Linux.

write_screen_file:
  Same as write_scrollback_file but writes the full screen contents.
  See write_scrollback_file for available values.

write_selection_file:
  Same as write_scrollback_file but writes the selected text.
  If there is no selected text this does nothing (it doesn't
  even create an empty file). See write_scrollback_file for
  available values.

new_window:
  Open a new window. If the application isn't currently focused,
  this will bring it to the front.

new_tab:
  Open a new tab.

previous_tab:
  Go to the previous tab.

next_tab:
  Go to the next tab.

last_tab:
  Go to the last tab (the one with the highest index)

goto_tab:
  Go to the tab with the specific number, 1-indexed. If the tab number
  is higher than the number of tabs, this will go to the last tab.

move_tab:
  Moves a tab by a relative offset.
  Adjusts the tab position based on `offset`. For example `move_tab:-1` for left, `move_tab:1` for right.
  If the new position is out of bounds, it wraps around cyclically within the tab range.

toggle_tab_overview:
  Toggle the tab overview.
  This only works with libadwaita enabled currently.

new_split:
  Create a new split in the given direction. The new split will appear in
  the direction given. For example `new_split:up`. Valid values are left, right, up, down and auto.

goto_split:
  Focus on a split in a given direction. For example `goto_split:up`.
  Valid values are left, right, up, down, previous and next.

toggle_split_zoom:
  zoom/unzoom the current split.

resize_split:
  Resize the current split by moving the split divider in the given
  direction. For example `resize_split:left,10`. The valid directions are up, down, left and right.

equalize_splits:
  Equalize all splits in the current window

inspector:
  Show, hide, or toggle the terminal inspector for the currently focused
  terminal.

open_config:
  Open the configuration file in the default OS editor. If your default OS
  editor isn't configured then this will fail. Currently, any failures to
  open the configuration will show up only in the logs.

reload_config:
  Reload the configuration. The exact meaning depends on the app runtime
  in use but this usually involves re-reading the configuration file
  and applying any changes. Note that not all changes can be applied at
  runtime.

close_surface:
  Close the current "surface", whether that is a window, tab, split, etc.
  This only closes ONE surface. This will trigger close confirmation as
  configured.

close_tab:
  Close the current tab, regardless of how many splits there may be.
  This will trigger close confirmation as configured.

close_window:
  Close the window, regardless of how many tabs or splits there may be.
  This will trigger close confirmation as configured.

close_all_windows:
  Close all windows. This will trigger close confirmation as configured.
  This only works for macOS currently.

toggle_fullscreen:
  Toggle fullscreen mode of window.

toggle_window_decorations:
  Toggle window decorations on and off. This only works on Linux.

toggle_secure_input:
  Toggle secure input mode on or off. This is used to prevent apps
  that monitor input from seeing what you type. This is useful for
  entering passwords or other sensitive information.

  This applies to the entire application, not just the focused
  terminal. You must toggle it off to disable it, or quit Ghostty.

  This only works on macOS, since this is a system API on macOS.

toggle_quick_terminal:
  Toggle the "quick" terminal. The quick terminal is a terminal that
  appears on demand from a keybinding, often sliding in from a screen
  edge such as the top. This is useful for quick access to a terminal
  without having to open a new window or tab.

  When the quick terminal loses focus, it disappears. The terminal state
  is preserved between appearances, so you can always press the keybinding
  to bring it back up.

  To enable the quick terminally globally so that Ghostty doesn't
  have to be focused, prefix your keybind with `global`. Example:

  \```ini
  keybind = global:cmd+grave_accent=toggle_quick_terminal
  \```

  The quick terminal has some limitations:

    - It is a singleton; only one instance can exist at a time.
    - It does not support tabs, but it does support splits.
    - It will not be restored when the application is restarted
      (for systems that support window restoration).
    - It supports fullscreen, but fullscreen will always be a non-native
      fullscreen (macos-non-native-fullscreen = true). This only applies
      to the quick terminal window. This is a requirement due to how
      the quick terminal is rendered.

  See the various configurations for the quick terminal in the
  configuration file to customize its behavior.

  This currently only works on macOS.

toggle_visibility:
  Show/hide all windows. If all windows become shown, we also ensure
  Ghostty becomes focused. When hiding all windows, focus is yielded
  to the next application as determined by the OS.

  This currently only works on macOS.

quit:
  Quit ghostty.

crash:
  Crash ghostty in the desired thread for the focused surface.

  WARNING: This is a hard crash (panic) and data can be lost.

  The purpose of this action is to test crash handling. For some
  users, it may be useful to test crash reporting functionality in
  order to determine if it all works as expected.

  The value determines the crash location:

    - "main" - crash on the main (GUI) thread.
    - "io" - crash on the IO thread for the focused surface.
    - "render" - crash on the render thread for the focused surface.

```
</details>

## Testing

- Run `ghostty-dev +list-actions --docs` to verify the new output format

- Check generated _zig-out/share/ghostty/webdata/actions.mdx_ to ensure
all actions are properly documented
2025-02-11 12:47:47 -08:00
Mitchell Hashimoto
4b77a1c71e Remember last focused window position for next startup (#5529)
Implements window position persistence on macOS, similar to Terminal.app
and iTerm2. The window position is saved when it becomes main or moves,
and restored on next startup. Window position is kept within visible
screen bounds.

cc @jsumners - Would you mind giving it a try? Any feedback would be
appreciated!

Resolves #4233
2025-02-11 12:47:12 -08:00
Mitchell Hashimoto
5ad348b23e Add keyboard navigation for Terminal IO window (#3909)
## Changes

- Add keyboard navigation support in Terminal IO window
  - Use J/K keys for vim-style navigation
  - Support arrow keys for traditional navigation


https://github.com/user-attachments/assets/e5d3bba1-1a47-49c9-ac59-c6195515605c

Resolves https://github.com/ghostty-org/ghostty/issues/1096
2025-02-11 12:46:18 -08:00
Bryan Lee
c627231b0f Fix confirm-close-surface not working for hidden quick terminal 2025-02-11 12:42:34 -08:00
Bryan Lee
31273aaabc Remember last focused window position for next startup 2025-02-11 12:33:29 -08:00
Mitchell Hashimoto
d1614f6bc4 Fix segfault in reset with Kitty graphics images on screen (#5693)
Fixes #5649
2025-02-11 11:26:06 -08:00
Mitchell Hashimoto
95ae3642f9 macos: ensure previously key window regains key on toggle_visibility (#5692)
Fixes #5690

When we hide the app and then show it again, the previously key window
is lost. This is because we are not using unhide and are manually doing
it (and we're not using unhide for good reasons commented in the source
already).

Modify our hidden state to include what the key window was (as a weak
ref) and restore it when we show the app again.
2025-02-11 11:23:48 -08:00
Qwerasd
b624cfe262 fix(terminal): avoid Screen.reset causing use of undefined
Fully reset the kitty image storage instead of using the delete handler,
previously this caused a memory corruption / likely segfault due to use
of undefined, since the delete handler tries to clear the tracked pins
for placements, which it gets from the terminal, for which `undefined`
was passed in before.
2025-02-11 14:20:47 -05:00
Mitchell Hashimoto
c6da845f33 macos: ensure previously key window regains key on toggle_visibility
Fixes #5690

When we hide the app and then show it again, the previously key window
is lost. This is because we are not using unhide and are manually
doing it (and we're not using unhide for good reasons commented in the
source already).

Modify our hidden state to include what the key window was (as a weak
ref) and restore it when we show the app again.
2025-02-11 11:13:19 -08:00
Mitchell Hashimoto
869819eb89 macos: toggle_visibility fullscreen check requires active app (#5691)
This fixes a regression from #5472. The fullscreen check must check if
the app is active otherwise the guard statement fails and we can't bring
the macOS app back from the background.

This regression never hit a versioned release, only tip.
2025-02-11 11:11:36 -08:00
Mitchell Hashimoto
f986a32185 macos: toggle_visibility fullscreen check requires active app
This fixes a regression from #5472. The fullscreen check must check if
the app is active otherwise the guard statement fails and we can't bring
the macOS app back from the background.
2025-02-11 11:01:20 -08:00
Mitchell Hashimoto
0532f67282 nix: use zig2nix to manage nix cache/deps (#5439)
This brings the internal package more in line with how the nixpkgs
package is built. It also handles recursive dependencies better than the
current system.
2025-02-11 08:41:54 -08:00
Jeffrey C. Ollie
3a8c934b19 nix: use zig2nix to manage nix cache/deps
This brings the internal package more in line with how the nixpkgs
package is built. It also handles recursive dependencies better than the
current system.
2025-02-11 08:25:34 -08:00
Mitchell Hashimoto
ab71dac282 Fix large OSC 8 links causing memory corruption (#5666)
Fixes #5635

### Changes:
- Added handling to `Screen.adjustCapacity` which properly returns an
error if there's no room for the cursor hyperlink. Note the TODO, in the
future we should probably make it handle this by increasing the capacity
further.

- Added handling to `Screen.cursorSetHyperlink` which ensures there will
be sufficient capacity to add the redundant cursor hyperlink to the
string alloc after adjusting the capacity.

***Future work:** Aside from separating the cursor's managed memory from
the page memory, which is a big thing we desperately need, we should
probably also make the page string alloc intern the strings it gets to
deduplicate.*
  
- Fixed a slight bug in the OSC parser which was causing the hyperlink
command to be issued with an empty (0-length) URI when the buffer
capacity was exceeded during an OSC 8 sequence.

***Future work:** We should consider increasing the `MAX_BUF` since some
specs call for a max size of 4096 (such as various Kitty protocols) --
otherwise we should switch all the OSCs that can take arbitrary data
like this to use the allocator instead of the fixed buffer.*
  
- Fixed a problem where when cloning content across pages (as happened
every time we had to adjust page capacities) hyperlinks would not be
properly looked up, often leading to many many redundant copies of a
given URI being stored, exploding string memory.
2025-02-11 08:09:03 -08:00
Mitchell Hashimoto
fa5fe5d293 core: protect against crashes and hangs when themes are not files (#5632)
If a theme was not a file or a directory you could get a crash or a hang
(depending on platform) if the theme references a directory. This patch
also prevents attempts to load from other non-file sources.

Fixes: #5596
2025-02-11 07:24:10 -08:00
Mitchell Hashimoto
d87cf61d26 gtk: introduce Zig bindings for GTK/GObject (#5560)
`zig-gobject` is a set of GObject bindings that allow us to write
GTK-facing code in Zig instead of getting hands dirty with C. It's been
tested and refined in real-life applications and several GTK
contributors agree that it is a marked improvement over using the C API
directly, such as allowing method call syntax and avoiding many manual
`@ptrCast`s.

This PR doesn't actually contain any changes to our preexisting GTK code
— the migration process is intended to begin in chunks, firstly in
self-contained components (e.g. the header bar, overlays, etc.), and
then full-scale migration can begin when we remove non-Adwaita GTK
builds for 1.2. (After all, why port code that you'll remove later
either way?)
2025-02-11 07:19:59 -08:00
Qwerasd
dd8c795ec6 test(terminal/screen): cursorSetHyperlink OOM handling edge case
Tests handling introduced in 09c76d95 which ensures sufficient space for
the cursor hyperlink uri in the string alloc when adjusting capacity.
2025-02-10 15:52:40 -05:00
Qwerasd
a1b682d0da fix(terminal): properly lookup hyperlinks when cloning rows across pages
Before this we were doing bad things with the memory, looking at
`PageEntry`s for links not actually in the page we were reading the
strings from.
2025-02-10 15:52:40 -05:00
Qwerasd
e540a79032 test(terminal/screen): OOM handling in adjustCapacity
Adds tests for the adjustCapacity changes from 09c76d95

Fixes a small oversight in that change as well (resetting cursor style).
2025-02-10 14:21:42 -05:00
Qwerasd
1c524238c8 test(terminal/osc): fix command longer than buffer test
Ensure that the state is invalidated properly, this previously wasn't
the case but was fixed in 03fd9a97
2025-02-10 13:27:26 -05:00
Leah Amelia Chen
843c714088 gtk: introduce Zig bindings for GTK/GObject
`zig-gobject` is a set of GObject bindings that allow us to write
GTK-facing code in Zig instead of getting hands dirty with C.
It's been tested and refined in real-life applications and several
GTK contributors agree that it is a marked improvement over using
the C API directly, such as allowing method call syntax and avoiding
many manual `@ptrCast`s.

This commit doesn't actually contain any changes to our preexisting
GTK code — the migration process is intended to begin in chunks,
firstly in self-contained components (e.g. the header bar, overlays,
etc.), and then full-scale migration can begin when we remove non-Adwaita
GTK builds for 1.2. (After all, why port code that you'll remove later
either way?)
2025-02-10 18:34:08 +01:00
Qwerasd
03fd9a970b fix(terminal): properly invalidate over-sized OSCs
The `self.complete = false` here is important so we don't accidentally
dispatch, for example, a hyperlink command with an empty URI.
2025-02-10 11:49:05 -05:00
Qwerasd
09c76d95c7 fix(terminal): handle errors in Screen.adjustCapacity
Previously assumed adjusted capacities would always fit the cursor style
and hyperlink, this is not necessarily the case and led to memory
corruption in scenarios with large hyperlinks.
2025-02-10 00:17:08 -05:00
Qwerasd
a3e462bbba Hopefully fix weird bug for Intel Mac users (#5652)
I don't have a machine locally that can reproduce the issue in #5597 but
have been working with @paperlib over Discord to try to narrow down the
cause. My fix in #5625 didn't fix the problem, so I'm *really* hoping
this patch does.

The problem presents as any text that matches the default bg color not
being rendered, regardless of the cell's bg color. The best I can figure
is that either we have some sort of accidental UB or there's a driver
bug (perhaps related to the addition of the `[[flat]]` interpolation
qualifier to the vertex out structure?), but I don't have a system
locally to iterate on to narrow it down.
2025-02-09 18:24:58 -05:00
Qwerasd
28200fb9e5 fix(Metal): don't throw away glyphs when the fg and bg colors match
This optimization is extremely niche and seems to be related to a
strange bug experienced by Intel Mac users. Considering it costs some
amount to have this extra check here even though it's false in the vast
majority of cases, I feel it's pretty safe to just remove it entirely.
2025-02-09 18:11:36 -05:00
Jeffrey C. Ollie
2c06ce761b GTK: disable color management (#5593)
fixes: https://github.com/ghostty-org/ghostty/discussions/4976

Before: 

![image](https://github.com/user-attachments/assets/169c29b5-b52a-44cd-aea8-43c2cfd8e411)

After:

![image](https://github.com/user-attachments/assets/80468ae2-7d75-464f-a2b0-37da48fdf61d)
2025-02-08 20:48:36 -06:00
Jeffrey C. Ollie
1947ba9c68 core: protect against crashes and hangs when themes are not files
If a theme was not a file or a directory you could get a crash or a hang
(depending on platform) if the theme references a directory. This patch
also prevents attempts to load from other non-file sources.

Fixes: #5596
2025-02-08 15:17:25 -06:00
Qwerasd
f95f636f1f Metal: use "Managed" resource storage mode on discrete GPUs (#5625)
Discrete GPUs cannot use the "shared" storage mode. This causes
undefined behavior right now, and I believe it's what's causing a
problem on Intel systems with discrete GPUs with "inverted" cells.
(Observed in discussion #5597)

This commit also sets the CPU cache mode to "write combined" for our
resources since we don't read them back so Metal can optimize them
further with this hint.
2025-02-07 13:49:39 -05:00
Qwerasd
ea16890fd3 Metal: use "Managed" resource storage mode on discrete GPUs
Discrete GPUs cannot use the "shared" storage mode. This causes
undefined behavior right now, and I believe it's what's causing a
problem on Intel systems with discrete GPUs with "inverted" cells.

This commit also sets the CPU cache mode to "write combined" for our
resources since we don't read them back so Metal can optimize them
further with this hint.
2025-02-07 13:15:18 -05:00
eifr
c7044b198c Merge branch 'main' into disable-color-management 2025-02-07 18:00:18 +02:00
eifr
5d6c021e26 update gtk version 2025-02-07 18:00:08 +02:00
Mitchell Hashimoto
79d6d26784 Introduce subsystem maintainers via CODEOWNERS (#5599)
This commit introduces the proposed subsystem maintainer system for
Ghostty. This commit doesn't assign anyone yet to the subsystems, but
defines the CODEOWNERS file, creates the GitHub teams, and documents the
system.

We can discuss this in Discord, but any feedback is also welcome here.
2025-02-07 07:21:32 -08:00
eifr
76cf58915b add comments 2025-02-06 10:25:26 +02:00
eifr
cbe0478572 remove config 2025-02-06 10:19:23 +02:00
Mitchell Hashimoto
ae25ad4047 Introduce subsystem maintainers via CODEOWNERS
This commit introduces the proposed subsystem maintainer system for
Ghostty. This commit doesn't assign anyone yet to the subsystems, but
defines the CODEOWNERS file, creates the GitHub teams, and documents the
system.
2025-02-05 18:49:30 -08:00
eifr
1ce23c079e expand comment + rename 2025-02-05 23:35:12 +02:00
eifr
d87bfdff1a move color-mgmt to config 2025-02-05 22:48:28 +02:00
Mitchell Hashimoto
4eb5885017 macos: update Sparkle to 2.6.4 to workaround security issue (#5598)
https://github.com/ghostty-org/ghostty/security/dependabot/4
2025-02-05 10:57:32 -08:00
Mitchell Hashimoto
f2db80520a Fix cursor character not visible when cursor color matches background (#5570)
When cursor color matches the background color, the optimization that
skips rendering identical colors causes the character under cursor to
become invisible. This PR ensures characters at cursor position are
always rendered, maintaining cursor visibility regardless of color
settings.

<img width="1129" alt="image"
src="https://github.com/user-attachments/assets/b82387c1-2bfd-481d-b679-1a1f82d21bcb"
/>

cc @kpovel - Would you mind giving it a try? Any feedback would be
appreciated!

Fixes #5554
2025-02-05 10:47:31 -08:00
Mitchell Hashimoto
1d7f041f55 macos: update Sparkle to 2.6.4 to workaround security issue
https://github.com/ghostty-org/ghostty/security/dependabot/4
2025-02-05 10:45:10 -08:00
Mitchell Hashimoto
fbdee68667 Add a final newline to "No crash reports!" (#5583) 2025-02-05 10:35:28 -08:00
Mitchell Hashimoto
581b87b20c gtk: remove CSD styling when CSDs aren't used (#5581)
Fixes #2023
2025-02-05 10:33:48 -08:00
Ofir Levitan
f660ec8bd2 GTK: disable color management 2025-02-05 16:04:53 +02:00
Kat
47e50abe24 Add a final newline to "No crash reports!" 2025-02-05 08:22:58 +00:00
Leah Amelia Chen
0c1f3d724d gtk: remove CSD styling when CSDs aren't used
Fixes #2023
2025-02-05 09:01:46 +01:00
Bryan Lee
4a95e8e48c Fix cursor character not visible when cursor color matches background 2025-02-04 20:50:34 +08:00
Mitchell Hashimoto
f0d276062b Toggling visibility is now ignored when in fullscreen mode. (#5472)
This PR ensures visibility toggling is ignored when the currently
focused surface is fullscreen. This includes the following:

- Show/Hide All Terminals is ignored
- Using a binding to toggle is ignored

This ensures Ghostty doesn't unexpectedly lose focus or disappear and is
the expected behavior on macOS.
2025-02-03 13:57:48 -08:00
Mitchell Hashimoto
7b593b9d7c linux: ensure that group dir fd is closed (#5515)
The CLOEXEC flag on the fd will ensure that the directory is closed on
the child, but also need to close the fd in the parent.
2025-02-03 13:46:28 -08:00
Mitchell Hashimoto
0538d6db6a macOS: binding checks should never trigger preedit callbacks (#5559)
Fixes #5558

Binding checks would sometimes trigger preedit which would cause some
characters to leak through.

This is a bit of a band-aid. The real long term solution is noted in the
TODO comment in this commit, but I wanted to avoid regressions in a
patch release so I'm going to defer that to 1.2. This commit fixes the
main issue for 1.1.1.
2025-02-03 13:46:19 -08:00
Shaps Benkau
8d31f6ce2e Toggling visibility is now ignored when in fullscreen mode. 2025-02-03 13:44:23 -08:00
Mitchell Hashimoto
fc14c5b070 core: add explicit errors to src/os/env.zig (#5530) 2025-02-03 13:38:23 -08:00
Mitchell Hashimoto
fe6b46f4e7 core: add explicit errors to src/pty.zig (#5531) 2025-02-03 13:38:00 -08:00
Mitchell Hashimoto
c07b1ac2f0 gtk(x11): support server-side decorations (#5533)
Remind me to never touch Xlib code ever again.
2025-02-03 13:35:24 -08:00
Mitchell Hashimoto
730c6884f7 macOS: binding checks should never trigger preedit callbacks
Fixes #5558

Binding checks would sometimes trigger preedit which would cause some
characters to leak through.

This is a bit of a band-aid. The real long term solution is noted in the
TODO comment in this commit, but I wanted to avoid regressions in a
patch release so I'm going to defer that to 1.2. This commit fixes the
main issue for 1.1.1.
2025-02-03 13:32:47 -08:00
Jeffrey C. Ollie
b7fa8e5947 linux: ensure that group dir fd is closed
The CLOEXEC flag on the fd will ensure that the directory is closed on
the child, but also need to close the fd in the parent.
2025-02-03 15:10:06 -06:00
Jeffrey C. Ollie
3fdbd5f7ba core: fix up pty Zig error sets 2025-02-03 15:06:59 -06:00
Jeffrey C. Ollie
7f0d22a31e core: add explicit errors to src/pty.zig 2025-02-03 15:06:59 -06:00
Jeffrey C. Ollie
8607d463ff core: fix puralization of src/os/env.zig Errors->Error 2025-02-03 14:38:00 -06:00
Jeffrey C. Ollie
002cce4e81 core: handle src/os/env.zig errors on windows 2025-02-03 14:26:12 -06:00
Jeffrey C. Ollie
8660cdaad5 core: add explicit errors to src/os/env.zig 2025-02-03 14:26:12 -06:00
Leah Amelia Chen
d61ee46210 config: mention X11 support in window-decoration docs 2025-02-03 18:56:09 +01:00
Leah Amelia Chen
4cd49632b2 gtk(x11): support server-side decorations
Remind me to never touch Xlib code ever again.
2025-02-03 18:56:09 +01:00
Mitchell Hashimoto
cdd2099090 apprt/gtk: handle input methods that end preedit before commit (#5550)
Fixes #5494

When ibus/fcitx is not running (the GTK "simple" input method is
active), the preedit end event triggers _before_ the commit event. For
both ibus/fcitx, the opposite is true. We were relying on this ordering.

This commit changes the GTK input handling to not rely on this ordering.
Instead, we encode our composing state into the boolean state of whether
a key event is pressed. This happens before ANY input method events are
triggered.

Tested dead key handling on: X11/Wayland, ibus/fcitx/none.
2025-02-03 09:37:17 -08:00
Mitchell Hashimoto
f70ba14abd apprt/gtk: handle input methods that end preedit before commit
Fixes #5494

When ibus/fcitx is not running (the GTK "simple" input method is
active), the preedit end event triggers _before_ the commit event. For
both ibus/fcitx, the opposite is true. We were relying on this ordering.

This commit changes the GTK input handling to not rely on this ordering.
Instead, we encode our composing state into the boolean state of whether
a key event is pressed. This happens before ANY input method events are
triggered.

Tested dead key handling on: X11/Wayland, ibus/fcitx/none.
2025-02-03 09:24:29 -08:00
Mitchell Hashimoto
3b3e75c1dc gtk(wayland): respect window-decoration=none on GNOME (#5463)
This is, admittedly, a very silly bug. On GNOME the SSD protocol is not
available and past me just decided to always enable CSDs in that case,
*even when* `window-decoration = none`. I now question my own
intelligence.
2025-02-03 07:23:10 -08:00
Mitchell Hashimoto
61fd41116a linux: fix installation path of nautilus-python extension (#5469)
Fixes #5468
2025-02-03 07:19:01 -08:00
Mitchell Hashimoto
fe085001cb Update iTerm2 colorschemes (#5511)
Upstream revision:
db227d159a
2025-02-03 07:14:36 -08:00
Mitchell Hashimoto
1a1d3db207 Linux: Syscall errno handle (#5537)
When trying to run valgrind this incorrectly results in a correct
result, this is because `posix.errno` will use libc errno when linking
libc which ghostty does.


cf90dfd309/lib/std/posix.zig (L219-L221)

Since we are making the syscall directly we should not use this function
but rather use the return code directly on the enum, name from this
function seems odd to me (no zig experience) but it is the suggested
answer from zig (refer to issue below)
https://github.com/ziglang/zig/issues/22718

Note this definitely isnt much better than what we were doing before in
the case of running in valgrind

```text
error(linux-cgroup): unable to clone: os.linux.E__enum_81093.NOSYS
debug(io_thread): IO thread exited
warning(io_thread): error in io thread err=error.CloneError
warning(io_thread): abrupt io thread exit detected, starting xev to drain mailbox
debug(io_thread): io thread fully exiting after abnormal failure
```

opening a new tab shows
```
error starting IO thread: error.CloneError

The underlying shell or command was unable to be started.
This error is usually due to exhausting a system resource.
If this looks like a bug, please report it.

This terminal is non-functional. Please close it and try again.
```

this did not show on the original surface only on the new tab
2025-02-03 07:12:18 -08:00
Mitchell Hashimoto
79dd3776e2 Fix a typo in the docs for adjust-strikethrough-position and adjust-overline-position (#5543)
Presumably an error from copy-pasting documentation text.
2025-02-03 07:11:33 -08:00
Kat
1bd5ea6f73 Fix a typo in the docs for adjust-strikethrough-position and adjust-overline-position 2025-02-03 05:58:48 +00:00
rhodes-b
fb2516fbf6 comment on why to not use posix.errno 2025-02-02 16:35:20 -06:00
rhodes-b
bc156266c6 fix errno handle 2025-02-02 16:27:10 -06:00
mitchellh
5100f4ff7d deps: Update iTerm2 color schemes 2025-02-02 00:10:42 +00:00
Jeffrey C. Ollie
660d9dc671 linux: fix installation path of nautilus-python extension
Fixes #5468
2025-01-31 09:14:50 -06:00
Leah Amelia Chen
ac582ccf7c gtk(wayland): respect window-decoration=none on GNOME
This is, admittedly, a very silly bug. On GNOME the SSD protocol is not
available and past me just decided to always enable CSDs in that case,
*even when* `window-decoration = none`. I now question my own intelligence.
2025-01-31 12:17:05 +01:00
Mitchell Hashimoto
c5508e7d19 update version for development 2025-01-30 14:23:24 -08:00
Mitchell Hashimoto
dda242c96e ci: update publish job 2025-01-30 13:58:17 -08:00
Mitchell Hashimoto
facda0c3fb gtk(x11): update blur region upon syncAppearance (#5443)
Fixes a bug where the blur region offset used to accomodate CSDs are
applied even when CSDs are disabled.
2025-01-30 13:43:48 -08:00
Mitchell Hashimoto
75dec598cc nix: fix Ghostty homepage in package (#5436) 2025-01-30 13:40:49 -08:00
Leah Amelia Chen
48a1a10330 gtk(x11): update blur region upon syncAppearance
Fixes a bug where the blur region offset used to accomodate CSDs
are applied even when CSDs are disabled.
2025-01-30 20:28:00 +01:00
Jeffrey C. Ollie
6dd9bf0038 nix: fix Ghostty homepage in package 2025-01-30 11:04:44 -06:00
Mitchell Hashimoto
adf4066b69 nix: use --system build flag instead of relying on $ZIG_GLOBAL_CACHE_DIR (#5434)
Fixes #5431
2025-01-30 08:48:31 -08:00
Jeffrey C. Ollie
40973417d0 nix: use --system build flag instead of relying on $ZIG_GLOBAL_CACHE_DIR
Fixes #5431
2025-01-30 10:16:45 -06:00
Mitchell Hashimoto
a62b26cd2f next version will be 1.1.0 2025-01-30 07:18:43 -08:00
Mitchell Hashimoto
04d36361b1 ci: separate publish and release workflows (#5425)
Fixes #4218 

This now requires a separate manually triggered `publish` workflow to be
run after the release completes in order to transition the release to
the `published` state.

Practically today this only means that the release will be published to
the macOS auto-updater, but in the future we could add additional steps
such as creating a GH release or some other notifications.

Importantly, this lets us verify the release in the uploaded location
before general users are notified of the update.
2025-01-29 17:21:52 -08:00
Mitchell Hashimoto
dbc4edc583 ci: separate publish and release workflows
This now requires a separate manually triggered `publish` workflow to be
run after the release completes in order to transition the release to
the `published` state.

Practically today this only means that the release will be published to
the macOS auto-updater, but in the future we could add additional steps
such as creating a GH release or some other notifications.

Importantly, this lets us verify the release in the uploaded location
before general users are notified of the update.
2025-01-29 16:39:35 -08:00
Mitchell Hashimoto
692168f8dd dist: remove cdata tags from appcast, we escape it all 2025-01-29 15:08:30 -08:00
Mitchell Hashimoto
fefda69ac3 input: performable bindings aren't part of the reverse mapping (#5421)
Fixes #4522

This is a bit of a hammer-meets-nail solution, but it's a simple
solution to the problem. The reverse mapping is used to find the binding
that an action is bound to, and it's used by apprt's to populate the
accelerator label in the UI.

The problem is that accelerators in GTK are handled early in the event
handling process and its difficult to get that event mapping to a
specific surface. Therefore, the "performable" prefix was not working.
On macOS, this issue didn't exist because there exists an OS mechanism
to install an event handler earlier than the menu system.

This commit changes the reverse mapping to only include bindings that
are not performable. This way, the keybind always reaches the surface
and can be handled by `Surface.keyCallback` which processes
`performable`.

The caveat is that performable bindings will not show up in the UI for
menu items. This is documented in this commit now. They still work, its
just a UI issue.
2025-01-29 14:44:52 -08:00
Mitchell Hashimoto
c33b82c634 macOS: add link to release notes on appcast (#5184)
Resolves #4730

I opted to link directly to the release notes of the updated version in
the `<description>` body, i.e. in the future it would link directly
v1.0.2's release notes at
https://ghostty.org/docs/install/release-notes/1-0-2. This thereby
implements the second option mentioned in the issue. It wasn't that much
harder than the third option, but if there's any particular reason _not
to_ link directly to a version's changelog page, I'll be more than happy
to modify this PR and implement the third option instead - in any case,
I had opened https://github.com/ghostty-org/website/pull/277 so that
https://ghostty.org/docs/install/release-notes is a valid page.

I also wrapped the current text `<![CDATA[ ... ]]>` just in case, as I
noticed this is a pretty standard practice in other apps that use
`<description>` to embed their release notes when using Sparkle -
[Sparkle's
documentation](https://sparkle-project.org/documentation/publishing/#embedded-release-notes)
says to do this so you can use unescaped HTML.
2025-01-29 14:38:18 -08:00
Mitchell Hashimoto
ce2a3773d2 input: performable bindings aren't part of the reverse mapping
Fixes #4522

This is a bit of a hammer-meets-nail solution, but it's a simple
solution to the problem. The reverse mapping is used to find the
binding that an action is bound to, and it's used by apprt's to populate
the accelerator label in the UI.

The problem is that accelerators in GTK are handled early in the event
handling process and its difficult to get that event mapping to a
specific surface. Therefore, the "performable" prefix was not working.
On macOS, this issue didn't exist because there exists an OS mechanism
to install an event handler earlier than the menu system.

This commit changes the reverse mapping to only include bindings that
are not performable. This way, the keybind always reaches the surface
and can be handled by `Surface.keyCallback` which processes
`performable`.

The caveat is that performable bindings will not show up in the UI
for menu items. This is documented in this commit now. They still work,
its just a UI issue.
2025-01-29 14:12:21 -08:00
Mitchell Hashimoto
09ccda4d28 apprt/gtk: set key modifier flag if physical modifier key is pressed (#5420)
Fixes #5191
2025-01-29 13:25:41 -08:00
Mitchell Hashimoto
27b254db8a apprt/gtk: set key modifier flag if physical modifier key is pressed
Fixes #5191
2025-01-29 13:12:50 -08:00
Mitchell Hashimoto
e5364392ee fix: remove extraneous if check after change to exhaustive switch (#5419)
This came out of cmd+triple-click fix in PR #5373
2025-01-29 11:03:52 -08:00
Lee Marlow
71d0481da8 Remove if check that was left in after change to exhaustive switch
This came out of cmd+triple-click fix in PR #5373
2025-01-29 11:38:35 -07:00
Mitchell Hashimoto
76fd4fa8df fix: cmd+triple-click select all command output when first line wraps (#5373)
I found this bug was easily reproduced with any command that wrapped to
multiple rows on the first line of its output. The cause is that we stop
searching for rows once we reach the first one where
`row.semantic_prompt = .command`, which means that we reach the bottom
line of wrapped output and stop there.

This PR makes it so that we continue iterating until we reach a row
where `semantic_prompt != .command` and then return the previous one (or
the last one if we run out of rows).

I also updated the test cases to include this.

I considered that this bug would also be avoided if we didn't propagate
the `command` semantic prompt to additional rows on wrapped lines, but I
don't know enough about the shell integration to make a call on that.

Closes #4693
2025-01-29 07:28:17 -08:00
Mitchell Hashimoto
d31e6c8b2a Fix older adwaita tab bars appearing above the title bar (#5410)
When setting up the GTK window with older Adwaita versions, we need to
explicitly create and add a tab bar. The previous code simply prepended
the tab bar into the GtkBox, which resulted in it being the very first
element (ahead of the title bar).

Instead, we grab a reference to the GTK title bar widget and call
`gtk_box_insert_child_after()` to explicitly insert our tab bar into the
hierarchy just after the title bar.
2025-01-29 10:09:54 -05:00
Daniel Fox
a80cf3db9c Fix older adwaita tab bars at top of window 2025-01-28 12:30:57 -08:00
Mitchell Hashimoto
603639ad44 renderer/Metal: improve linear blending correction (#5401)
While I actually do personally prefer the previous style's appearance, I
have a feeling this version will be much more popular, since it
essentially replicates the appearance of non-linear blending but without
any fringing artifacts. Details are explained in the comments and commit
messages.

<details>
<summary>
<h3>Screenshots for comparison</h3>
</summary>

*(open images in separate tabs and make sure they're at 100% scale for
proper comparison)*

|blending|screenshot|
|-|-|

|`native`|![native](https://github.com/user-attachments/assets/295bbab3-60a7-4915-93d9-a938082fa309)|

|`linear`|![linear](https://github.com/user-attachments/assets/a9a5a5ea-cf57-4730-8ac6-3ce1dc29c5f8)|
|`linear-corrected`
(old)|![linear-corrected_old](https://github.com/user-attachments/assets/4fd9510d-b798-4900-8c31-00e3f67fd806)|
|`linear-corrected`
(new)|![linear-corrected_new](https://github.com/user-attachments/assets/a379f307-db8a-4335-aa11-3ac71d01470e)|
</details>
2025-01-28 09:59:56 -05:00
Qwerasd
016a26cf98 cleanup: rename text-blending to alpha-blending
+ correct docs
2025-01-27 19:37:44 -05:00
Qwerasd
5c8f984ea1 renderer/Metal: improve linear blending correction
More mathematically sound approach, does a much better job of matching
the appearance of non-linear blending. Removed `experimental` from name
because it's not really an experiment anymore.
2025-01-27 19:15:18 -05:00
Qwerasd
ac568900a5 fix(renderer/Metal): properly load cursor color 2025-01-26 20:40:19 -05:00
Daniel Patterson
4b8010a6f4 Change ifs to exhaustive switches 2025-01-26 15:22:23 +00:00
Daniel Patterson
645b4b0031 Fix cmd+triple click not selecting full output 2025-01-25 23:37:46 +00:00
Mitchell Hashimoto
71e62f96fa macos: hide dock globally if the dock conflicts (#5363)
Related to #5361

The fix in 5361 wasn't sufficient since it only applied if our app was
in the foreground. Our quick terminal is a non-activating NSPanel to
allow it to work on any space (fullscreen included). This means that
Ghostty doesn't become the active app when the quick terminal is shown
and another app is in the foreground.

To work around this, we now hide the dock globally when the quick
terminal is shown AND the dock is in a conflicting position. We restore
this state when the quick terminal is hidden, loses focus, or Ghostty is
quit.
2025-01-24 20:30:47 -08:00
Mitchell Hashimoto
a58b1998a9 macos: hide dock globally if the dock conflicts
Related to #5361

The fix in 5361 wasn't sufficient since it only applied if our app was
in the foreground. Our quick terminal is a non-activating NSPanel to
allow it to work on any space (fullscreen included). This means that
Ghostty doesn't become the active app when the quick terminal is shown
and another app is in the foreground.

To work around this, we now hide the dock globally when the quick
terminal is shown AND the dock is in a conflicting position. We restore
this state when the quick terminal is hidden, loses focus, or Ghostty is
quit.
2025-01-24 20:18:44 -08:00
Mitchell Hashimoto
e2e6770ed1 Docs: improve doc structure for action bindings with args and examples (#5344)
Related to #4446 , as suggested i've made a smaller chunk of updates for
the action binding docs, including arguments and examples. I did this
only for enum types this time.

I've also included the argument type name in the Argument (e.g.
AdjustSelection).

Not sure if we should include the keybindings or not in the example?

I also added a format of some unrelated code change.
2025-01-24 15:45:54 -08:00
Mitchell Hashimoto
a88e30179a cli/list-keybinds: output chorded keybinds (#5357)
Print chorded/sequenced keybinds in `+list-keybinds`.

Recursively traverses the binding sets of sequenced keybinds and builds
a singly-linked list of triggers for each leaf. Also adapted the current
sorting criteria to work for multiple triggers per keybind.

Chorded keybinds are already output when not printing to a tty so that
code path is unchanged.

Closes #4505
2025-01-24 15:34:51 -08:00
Mitchell Hashimoto
3b108945f3 Merge branch 'main' into cleanup-action-binding-docs 2025-01-24 15:33:39 -08:00
Mitchell Hashimoto
136d6e9341 macos: autohide dock if quick terminal would conflict with it (#5361)
Fixes #5328

The dock sits above the level of the quick terminal, and the quick
terminal frame typical includes the dock. Hence, if the dock is visible
and the quick terminal would conflict with it, then part of the terminal
is obscured.

This commit makes the dock autohide if the quick terminal would conflict
with it. The autohide is disabled when the quick terminal is closed.

We can't set our window level above the dock, as this would prevent
things such as input methods from rendering properly in the quick
terminal window.

iTerm2 (the only other macOS terminal I know of that supports a dropdown
mode) frames the terminal around the dock. I think this looks less
aesthetically pleasing and I prefer autohiding the dock instead.

We can introduce a setting to change this behavior if desired later.

Additionally, this commit introduces a mechanism to safely set
app-global presentation options from multiple sources without stepping
on each other.

## Demo



https://github.com/user-attachments/assets/1f5bb945-dca4-49e7-8bcb-95b55524cfb5
2025-01-24 15:03:41 -08:00
Mitchell Hashimoto
a5a73f8352 macos: autohide dock if quick terminal would conflict with it
Fixes #5328

The dock sits above the level of the quick terminal, and the quick
terminal frame typical includes the dock. Hence, if the dock is visible
and the quick terminal would conflict with it, then part of the terminal
is obscured.

This commit makes the dock autohide if the quick terminal would conflict
with it. The autohide is disabled when the quick terminal is closed.

We can't set our window level above the dock, as this would prevent
things such as input methods from rendering properly in the quick
terminal window.

iTerm2 (the only other macOS terminal I know of that supports a dropdown
mode) frames the terminal around the dock. I think this looks less
aesthetically pleasing and I prefer autohiding the dock instead.

We can introduce a setting to change this behavior if desired later.

Additionally, this commit introduces a mechanism to safely set
app-global presentation options from multiple sources without stepping
on each other.
2025-01-24 14:51:17 -08:00
Mitchell Hashimoto
4f857fc4e9 Ignore SIGPIPE (#5360)
Fixes #5359

The comments explain what's going on. Longer term we should adjust our
termio/exec to avoid the SIGPIPE but its still possible (i.e. that
thread crashes) to happen so we should be robust to it.
2025-01-24 13:53:45 -08:00
Mitchell Hashimoto
f73cae0738 Ignore SIGPIPE
Fixes #5359

The comments explain what's going on. Longer term we should adjust our
termio/exec to avoid the SIGPIPE but its still possible (i.e. that
thread crashes) to happen so we should be robust to it.
2025-01-24 13:48:30 -08:00
Mitchell Hashimoto
47ff4c96e0 build: options to enable/disable terminfo & termcap install (take 2) (#5340)
Fixes #5253

Add -Demit-terminfo and -Demit-termcap build options to enable/disable
installation of source terminfo and termcap files.

Replacement for #5311
2025-01-24 13:28:48 -08:00
Mitchell Hashimoto
75d6ee539a termio/exec: call waitpid in process exit callback (#5353)
Fixes #4554

When a process exited on its own (we didn't kill it), we were not
calling waitpid. This commit adds a waitpid call in the event loop
process exit notification.

This happened because when an external exit happened we could call
`subprocess.externalExit` which tells our subprocess manager to NOT kill
the process (since its already dead). Unfortunately in this case it
means we also didn't call waitpid.
2025-01-24 12:19:22 -08:00
Mitchell Hashimoto
9ab2e563bb Update libxev to fix zombie processes on macOS
Fixes #4554

xev.Process.wait is documented as being equivalent to calling `waitpid`,
i.e. including reaping the process. On Linux, it does this automatically
by using pidfd and the `waitid` syscall. On macOS, it wasn't doing this.

This commit updates libxev to include a fix that explicitly calls
`waitpid` for kqueue.
2025-01-24 12:07:01 -08:00
Daniel Patterson
5ad2ec8f71 Add chorded/sequenced keybinds to +list-keybinds output 2025-01-24 19:58:15 +00:00
Mitchell Hashimoto
69dcea5148 termio/exec: if pty fd HUP, end read thread (#5351)
Fixes #4884

When our command exits, it will close the pty slave fd. This will
trigger a HUP on our poll. Previously, we only checked for IN. When a fd
is closed, IN triggers forever which would leave to an infinite loop and
100% CPU.

Now, detect the HUP and exit the read thread.
2025-01-24 09:48:56 -08:00
Mitchell Hashimoto
8475768ad1 termio/exec: if pty fd HUP, end read thread
Fixes #4884

When our command exits, it will close the pty slave fd. This will
trigger a HUP on our poll. Previously, we only checked for IN. When a fd
is closed, IN triggers forever which would leave to an infinite loop and
100% CPU.

Now, detect the HUP and exit the read thread.
2025-01-24 09:39:22 -08:00
Erlend Lind Madsen
0c5ef5578c Docs: remove type from action arguments 2025-01-24 23:13:42 +07:00
Jeffrey C. Ollie
593d70a42f fix missing check of emit_termcap build option 2025-01-24 10:06:40 -06:00
Jeffrey C. Ollie
d1969f74ac only the cp step needs to depend on the mkdir step 2025-01-24 10:06:39 -06:00
Jeffrey C. Ollie
2f8b0dc899 build: options to enable/disable terminfo & termcap install (take 2)
Fixes #5253

Add -Demit-terminfo and -Demit-termcap build options to enable/disable
installation of source terminfo and termcap files.
2025-01-24 10:06:39 -06:00
Erlend Lind Madsen
c4c2d06571 fmt 2025-01-24 15:06:47 +07:00
Erlend Lind Madsen
076bcccde4 Docs: improve doc structure for action bindings with args and examples
minor doc changes
2025-01-24 15:06:45 +07:00
Mitchell Hashimoto
fd8cacaa67 Prevent fd leaks to the running shell or command (#5341)
Multiple fixes to prevent file descriptor leaks:

- libxev eventfd now uses CLOEXEC
- linux: cgroup clone now uses CLOEXEC for the cgroup fd
- termio pipe uses pipe2 with CLOEXEC
- pty master always sets CLOEXEC because the child doesn't need it
- termio exec now closes pty slave fd after fork

There still appear to be some fd leaks happening. They seem related to
GTK, they aren't things we're accessig directly. I still want to
investigate them but this at least cleans up the major sources of fd
leakage.
2025-01-23 22:21:33 -08:00
Mitchell Hashimoto
0d6a1d3fdb Prevent fd leaks to the running shell or command
Multiple fixes to prevent file descriptor leaks:

- libxev eventfd now uses CLOEXEC
- linux: cgroup clone now uses CLOEXEC for the cgroup fd
- termio pipe uses pipe2 with CLOEXEC
- pty master always sets CLOEXEC because the child doesn't need it
- termio exec now closes pty slave fd after fork

There still appear to be some fd leaks happening. They seem related to
GTK, they aren't things we're accessig directly. I still want to
investigate them but this at least cleans up the major sources of fd
leakage.
2025-01-23 22:12:58 -08:00
Mitchell Hashimoto
c0eb6985ee Revert "build: options to enable/disable terminfo & termcap install"
This reverts commit 8f49a227b7.
2025-01-23 19:38:13 -08:00
Mitchell Hashimoto
4b82e0aa2b fix(Metal): always render explicit background colors fully opaque (#5335)
This fixes a regression introduced by the rework of this area before
during the color space changes (#4913). It seems like the original
intent of this code was the behavior it regressed to, but it turns out
to be better like this.
2025-01-23 19:16:10 -08:00
Qwerasd
78790f6ef7 fix(Metal): always render explicit background colors fully opaque
This fixes a regression introduced by the rework of this area before
during the color space changes. It seems like the original intent of
this code was the behavior it regressed to, but it turns out to be
better like this.
2025-01-23 20:06:53 -05:00
Mitchell Hashimoto
95327bff18 Prevent hyperlink hover state when mouse is outside viewport (#5267)
## Description

Fixed an issue where hyperlinks would maintain their hover state when
the mouse is outside the viewport while holding the Cmd key. This
created inconsistent behavior with the window's standard hover state
clearing logic.

## Changes

- Added viewport boundary check in `mouseRefreshLinks` function to
prevent link processing when cursor is outside the window

## Testing

To reproduce and verify the fix:

1. Run `fd --hyperlink "\.zig$" src/apprt` to create hyperlinks

2. Move mouse over a hyperlink (it should highlight)

3. Move mouse outside window

4. Hold Cmd key

	- Before: Link would show hover state

	- After: Link remains in non-hover state

Fixes #5252

@rrotter Could you please try this to see if it solves your issue?
2025-01-23 16:29:07 -08:00
Mitchell Hashimoto
9b30eb8eb8 bash: handle additional command arguments (#5319)
A '-' or '--' argument signals the end of bash's own options. All
remaining arguments are treated as filenames and arguments. We shouldn't
perform any additional argument processing once we see this signal.

We could also assume a non-interactive shell session in this case unless
the '-i' (interactive) shell option has been explicitly specified, but
let's wait on that until we know that doing so would solve a real user
problem (and avoid any false negatives).
2025-01-23 16:18:49 -08:00
Mitchell Hashimoto
a4b0e6d937 Fix sudo fish shell integration (#5276)
Currently, `sudo_has_sudoedit_flags` variable is being erased when `for`
block ends.
Change its scope to `--function` to prevent this.

Fixes `sudo: you may not specify environment variables in edit mode`.
2025-01-23 16:17:51 -08:00
Mitchell Hashimoto
4c27743931 build: options to enable/disable terminfo & termcap install (#5311)
Fixes #5253

Add `-Demit-terminfo` and `-Demit-termcap` build options to
enable/disable installtion of source terminfo and termcap files.
2025-01-23 16:14:53 -08:00
Mitchell Hashimoto
deb9033739 Generate mdx for cli actions (#4499)
Duplicate existing reference docs generation to cover cli actions. Docs
update pass to make the structure consistent.

See https://github.com/ghostty-org/website/pull/253 for website changes.
2025-01-23 16:11:18 -08:00
Mitchell Hashimoto
78a2a815f3 nix: vms for testing ghostty (#4608)
Adds a Nix VM configuration to run Gnome/Wayland. The primary purpose
will be testing Ghostty in a "clean" environment. I'm going to continue
to experiment with running this in CI to see if we can do some basic
testing of GTK since it's difficult to unit test that code.

To use, run `nix run .#wayland-gnome` in the Ghostty source directory. I
have no idea if this works on macOS. There's probably a lot of extra
stuff that can be trimmed to slim it down.

Whatever directory you run this from will be mounted at `/tmp/shared` in
the VM. The `ghostty` user runs as uid/gid 1000/1000 so that may affect
your ability to read/write that directory if your host system user runs
as a different uid/gid.

If this is something that we'd like to keep, we should add VM
definitions for KDE Plasma and maybe one tiling window manager.


https://github.com/user-attachments/assets/57190913-f338-4383-93aa-90c9e351543d
2025-01-23 16:03:29 -08:00
Anund
098a46f077 docs: generate mdx file for cli actions 2025-01-23 15:58:33 -08:00
Anund
168dd31367 documentation: consistent format for actions help 2025-01-23 15:58:33 -08:00
Mitchell Hashimoto
148a009a95 config: rename adw-toasts to app-notifications (#5332)
There is no `renamed` entry for this because this was never part of a
released version of Ghostty. This is not considered a break change.

Fixes #4460
2025-01-23 15:53:49 -08:00
Mitchell Hashimoto
5327646d58 config: rename adw-toasts to app-notifications
There is no `renamed` entry for this because this was never part of a
released version of Ghostty. This is not considered a break change.

Fixes #4460
2025-01-23 15:41:28 -08:00
Mitchell Hashimoto
0c24da1412 gtk: request initial color scheme asynchronously (#5064)
Requesting the initial color scheme on systems where the D-Bus interface
is nonexistent would delay Ghostty startup by 1-2 minutes. That's not
acceptable. Our color scheme events are already async-friendly anyway.

Fixes #4632
2025-01-23 15:31:54 -08:00
Leah Amelia Chen
956bb8f02b gtk: request initial color scheme asynchronously
Requesting the initial color scheme on systems where the D-Bus interface
is nonexistent would delay Ghostty startup by 1-2 minutes. That's not
acceptable. Our color scheme events are already async-friendly anyway.

Fixes #4632
2025-01-23 15:19:57 -08:00
Mitchell Hashimoto
b4a90a7a22 fix: gtk titlebar being restored if it shouldn't be (#5090)
I feel like we should be respecting the value of `gtk-titlebar` in the
`gtkWindowNotifyMaximized` callback as mentioned in discord
(https://discord.com/channels/1005603569187160125/1005603569711452192/1328976978094723163)

Would close https://github.com/ghostty-org/ghostty/issues/5262
2025-01-23 15:17:07 -08:00
Mitchell Hashimoto
078ee42be3 apprt/gtk: we should only show the headerbar again if csd 2025-01-23 15:04:48 -08:00
Adam Wolf
1be89cb146 fix: also respect gtk-titlebar value in fullscreened callback 2025-01-23 15:01:49 -08:00
Adam Wolf
80eb406b82 fix: gtk titlebar being restored if it shouldn't be 2025-01-23 15:01:49 -08:00
Mitchell Hashimoto
e39745113a cli: allow renaming config fields to maintain backwards compatibility (#5329)
Fixes #4631

This introduces a mechanism by which parsed config fields can be renamed
to maintain backwards compatibility. This already has a use case --
implemented in this commit -- for `background-blur-radius` to be renamed
to `background-blur`.

The remapping is comptime-known which lets us do some comptime
validation. The remap check isn't done unless no fields match which
means for well-formed config files, there's no overhead.

For future improvements:

- We should update our config help generator to note renamed fields.
- We could offer automatic migration of config files be rewriting them.
- We can enrich the value type with more metadata to help with config
gen or other tooling.
2025-01-23 14:25:26 -08:00
Mitchell Hashimoto
e854b38872 cli: allow renaming config fields to maintain backwards compatibility
Fixes #4631

This introduces a mechanism by which parsed config fields can be renamed
to maintain backwards compatibility. This already has a use case --
implemented in this commit -- for `background-blur-radius` to be renamed
to `background-blur`.

The remapping is comptime-known which lets us do some comptime
validation. The remap check isn't done unless no fields match which
means for well-formed config files, there's no overhead.

For future improvements:

- We should update our config help generator to note renamed fields.
- We could offer automatic migration of config files be rewriting them.
- We can enrich the value type with more metadata to help with
  config gen or other tooling.
2025-01-23 14:16:37 -08:00
Mitchell Hashimoto
4a3b4ea2b2 macOS: prevent native window drag by top region when titlebar hidden (#2523)
Currently `macos-titlebar-style = hidden` doesn't prevent the native
window drag gesture in the top region of the window- in the original PR
it did, but there were issues with how it went about it so it was
removed (see c6bbdfb). I figured out how to safely and simply remove the
gesture, and as a bonus it opens up potential future enhancements.

The native window drag region is driven ultimately by the window's
`contentLayoutRect`, so we can just override it in `TerminalWindow` to
return a rect the size of the full window, disabling the gesture without
causing any side effects by altering the responder chain.

This makes `macos-titlebar-style = hidden` a much nicer experience. The
window can still be resized, managed by the OS and third party window
managers, and dragged by the edges, but the native drag gesture in the
titlebar region is fully avoided.

### Future work
We may consider adjusting this to produce a `contentLayoutRect` that
doesn't include the padding area of the terminal grid(s), so that the
window can be dragged from a larger region around the edges (and not
just the resize region).
2025-01-23 13:48:31 -08:00
Qwerasd
5477eb87c1 macOS: prevent native window drag by top region when titlebar hidden
The native window drag region is driven ultimately by the window's
`contentLayoutRect`, so we can just override it in `TerminalWindow`
to return a rect the size of the full window, disabling the gesture
without causing any side effects by altering the responder chain.
2025-01-23 13:35:52 -08:00
Julia
9c8c53bffb use main buffer and copy data to fbo texture (opengl) (#5294)
NEEDS REVIEW

continuation of #5037
resolves #4729 

renders all shaders to the default buffer and then copies it to the
designated custom shader texture.

this is a draft pr because:
- it introduces a new shader "pipeline" which doesnt fit in with how the
system was designed to work (which is only rendering to the fbo)
- im not sure if this is the best way to achieve shaders being able to
sample their output while also drawing to the screen. the cusom fbo
(previous implementation) was useful in that it modularized the custom
shader stage in rendering

---------

Co-authored-by: Mitchell Hashimoto <m@mitchellh.com>
2025-01-23 20:57:14 +00:00
Jeffrey C. Ollie
8f49a227b7 build: options to enable/disable terminfo & termcap install
Fixes #5253

Add `-Demit-terminfo` and `-Demit-termcap` build options to
enable/disable installtion of source terminfo and termcap files.
2025-01-23 14:17:33 -06:00
Mitchell Hashimoto
cd57612059 apprt/gtk: when text is committed, end the preedit state (#5324)
Fixes #3567

ibus 1.5.29 doesn't trigger a preedit end state when text is committed.
This is fixed in ibus 1.5.30, but we need to handle this case for older
versions which are shipped on LTS distributions such as Ubuntu.

Every other input method engine I've tried thus far also triggers a
preedit end state when text is committed, and none would expect preedit
to continue after text is committed. So I think it's safe to assume that
this is the expected behavior.
2025-01-23 12:01:18 -08:00
Mitchell Hashimoto
d1e45ef768 apprt/gtk: when text is committed, end the preedit state
Fixes #3567

ibus 1.5.29 doesn't trigger a preedit end state when text is committed.
This is fixed in ibus 1.5.30, but we need to handle this case for older
versions which are shipped on LTS distributions such as Ubuntu.

Every other input method engine I've tried thus far also triggers a
preedit end state when text is committed, and none would expect preedit
to continue after text is committed. So I think it's safe to assume that
this is the expected behavior.
2025-01-23 11:47:46 -08:00
Jon Parise
a2018d7b20 bash: handle additional command arguments
A '-' or '--' argument signals the end of bash's own options. All
remaining arguments are treated as filenames and arguments. We shouldn't
perform any additional argument processing once we see this signal.

We could also assume a non-interactive shell session in this case unless
the '-i' (interactive) shell option has been explicitly specified, but
let's wait on that until we know that doing so would solve a real user
problem (and avoid any false negatives).
2025-01-23 10:43:36 -05:00
Mitchell Hashimoto
eb21a58aa4 apprt/gtk: ibus activation should not encode keys (#5310)
Related to #3567

This cleans up our handling of when GTK tells us the input method
handled the key press to address more scenarios we should not encode the
key event. The comments in this diff should explain clearly.

Reproduction is simple:

1. Use ibus (X11 or Wayland doesn't matter)
2. Press `super+.` to activate the emoji keyboard
3. Notice the `.` is written to the shell AND the emoji keyboard is
activated.

The bug is that `.` should not be encoded since it was used to activate
the emoji keyboard. This PR fixes that.
2025-01-22 20:32:03 -08:00
Mitchell Hashimoto
4408101b8d apprt/gtk: ibus activation should not encode keys
This cleans up our handling of when GTK tells us the input method
handled the key press to address more scenarios we should not encode the
key event. The comments in this diff should explain clearly.
2025-01-22 20:19:25 -08:00
Mitchell Hashimoto
ddf7173ae9 Change default key bindings to capture full screen contents (write_screen_file) (#5285)
The current default keybinding on `Ctrl-Shift-J`/`Cmd-Shift-J` utilize
[`write_scrollback_file`](https://ghostty.org/docs/config/keybind/reference#write_scrollback_file)
which only captures text that's scrolled off-screen. This can be
confusing, as I would expect it to capture everything on and off-screen.

Per the docs,
[`write_screen_file`](https://ghostty.org/docs/config/keybind/reference#write_screen_file)
is the "Same as `write_scrollback_file` but writes the full screen
contents" which is perfect to solve this problem and aligns exactly with
the expected behavior.

This change addresses the friction that people have reported in the
discussions below and specifically coming from the [search scrollback
feature discussion](https://github.com/ghostty-org/ghostty/issues/189),
where `Ctrl-Shift-J`/`Cmd-Shift-J` is one of the interim decent
workarounds but then you also have to figure out the extra intricacy to
use `write_screen_file` instead of `write_scrollback_file`.

Previous discussions:

 - https://github.com/ghostty-org/ghostty/discussions/3652
 - https://github.com/ghostty-org/ghostty/issues/3496
 - https://github.com/ghostty-org/ghostty/discussions/4911
 - https://github.com/ghostty-org/ghostty/discussions/4390
-
https://github.com/ghostty-org/ghostty/discussions/2363#discussioncomment-11735957
-
https://github.com/ghostty-org/ghostty/issues/189#issuecomment-2564719973
 - https://github.com/ghostty-org/ghostty/pull/2040
 
### Workaround

Before this PR is merged, you can achieve the same result by updating
your Ghostty config:

`.config/ghostty`
```sh
# So we have more scrollback history (the size of the scrollback buffer in bytes)
scrollback-limit = 100000000

# The default keybindings use `write_scrollback_file` but that only includes what is
# offscreen. `write_screen_file` includes what's on screen and offscreen.
keybind = ctrl+shift+j=write_screen_file:paste
keybind = ctrl+shift+alt+j=write_screen_file:open
```
2025-01-21 19:38:05 -08:00
Eric Eastwood
a8d2185611 Switch default key bindings to include on and offscreen contents
Previous discussions:

 - https://github.com/ghostty-org/ghostty/discussions/3652
 - https://github.com/ghostty-org/ghostty/issues/3496
 - https://github.com/ghostty-org/ghostty/discussions/4911
 - https://github.com/ghostty-org/ghostty/discussions/4390
 - https://github.com/ghostty-org/ghostty/discussions/2363#discussioncomment-11735957
 - https://github.com/ghostty-org/ghostty/issues/189#issuecomment-2564719973
 - https://github.com/ghostty-org/ghostty/pull/2040
2025-01-21 20:13:14 -06:00
Mitchell Hashimoto
6265adfcd4 apprt/gtk: fundamentally rework input method handling (#5280)
Fixes #4332

This commit fundamentally reworks the input method handling in the GTK
apprt, making it work properly (as reported in the linked issue) on both
Wayland and X11. This was tested with both a Gnome desktop on Wayland
and i3 on X11 with fcitx and mozc.

The main changes are:

- Both key press and release events must be forwarded to the input
method.

- Input method callbacks such as preedit and commit must be expected
outside of keypress events to handle on-screen keyboards and
non-keyboard input devices.

- Input methods should always commit when told to. Previously, we would
only commit when a keypress event was given. This is incorrect. For
example, it didn't work with input method changes outside the app which
should result in committed text (as can be seen with "official" Gnome
apps like Notes or Console).

The key input handling also now generally does less so I think input
latency should be positively affected by this change. I didn't measure.
2025-01-21 14:49:39 -08:00
Mitchell Hashimoto
52936b9b68 apprt/gtk: fundamentally rework input method handling
Fixes #4332

This commit fundamentally reworks the input method handling in the GTK
apprt, making it work properly (as reported in the linked issue) on both
Wayland and X11. This was tested with both a Gnome desktop on Wayland
and i3 on X11 with fcitx and mozc.

The main changes are:

- Both key press and release events must be forwarded to the input
  method.

- Input method callbacks such as preedit and commit must be expected
  outside of keypress events to handle on-screen keyboards and
  non-keyboard input devices.

- Input methods should always commit when told to. Previously, we would
  only commit when a keypress event was given. This is incorrect. For
  example, it didn't work with input method changes outside the app
  which should result in committed text (as can be seen with "official"
  Gnome apps like Notes or Console).

The key input handling also now generally does less so I think input
latency should be positively affected by this change. I didn't measure.
2025-01-21 14:37:40 -08:00
Bryan Lee
bf6cce23da Prevent hyperlink hover state when mouse is outside viewport 2025-01-22 02:49:37 +08:00
m154k1
25ccdfe495 Fix sudo fish shell integration
Set sudo_has_sudoedit_flags scope to --function.
2025-01-21 17:37:28 +02:00
Mitchell Hashimoto
5cb2fa6f75 fix(renderer): clip terminal contents to expected grid size (#4523) (#5265)
Resolves #4523

More notably this fixes a memory corruption crash that can occur while
resizing the font under Metal while there's a lot of active changes
occurring (e.g. while running DOOM fire). The change where all
background colors are explicitly written exposed this issue, though it
was technically a problem the whole time I'm fairly sure, just that the
corruption it caused before was benign.

This significantly improves the robustness of the renderers since it
prevents synchronization issues from causing memory corruption due to
out of bounds read/writes while building the cells.

TODO: when viewport is narrower than renderer grid size, fill blank
margin with bg color- currently appears as black, this only affects
DECCOLM right now, and possibly could create single-frame artefacts
during poorly managed resizes, but it's not ideal regardless.
2025-01-20 19:27:09 -08:00
Mitchell Hashimoto
fccb172ae9 unigen: Remove libc dependency, use ArenaAllocator (#5268)
Not linking libc avoids potential problems when compiling from/for
certain targets (see
https://github.com/ghostty-org/ghostty/discussions/3218), and using an
ArenaAllocator makes unigen run just as fast (in both release and debug
modes) while also taking less memory.

```
Benchmark 1 (3 runs): ./zig-out/bin/unigen-release-c
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.75s  ± 15.8ms    1.73s  … 1.76s           0 ( 0%)        0%
  peak_rss           2.23MB ±    0      2.23MB … 2.23MB          0 ( 0%)        0%
  cpu_cycles         7.22G  ± 62.8M     7.16G  … 7.29G           0 ( 0%)        0%
  instructions       11.5G  ± 16.0      11.5G  … 11.5G           0 ( 0%)        0%
  cache_references    436M  ± 6.54M      430M  …  443M           0 ( 0%)        0%
  cache_misses        310K  ±  203K      134K  …  532K           0 ( 0%)        0%
  branch_misses      1.03M  ± 29.9K      997K  … 1.06M           0 ( 0%)        0%
Benchmark 2 (3 runs): ./zig-out/bin/unigen-release-arena
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.73s  ± 6.40ms    1.72s  … 1.73s           0 ( 0%)          -  1.0% ±  1.6%
  peak_rss           1.27MB ± 75.7KB    1.18MB … 1.31MB          0 ( 0%)        - 43.1% ±  5.4%
  cpu_cycles         7.16G  ± 26.5M     7.13G  … 7.18G           0 ( 0%)          -  0.9% ±  1.5%
  instructions       11.4G  ± 28.2      11.4G  … 11.4G           0 ( 0%)          -  0.8% ±  0.0%
  cache_references    441M  ± 2.89M      439M  …  444M           0 ( 0%)          +  1.2% ±  2.6%
  cache_misses        152K  ±  102K     35.2K  …  220K           0 ( 0%)          - 50.8% ± 117.8%
  branch_misses      1.05M  ± 13.4K     1.04M  … 1.06M           0 ( 0%)          +  2.0% ±  5.1%
```

```
Benchmark 1 (3 runs): ./zig-out/bin/unigen-debug-c
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.75s  ± 32.4ms    1.71s  … 1.77s           0 ( 0%)        0%
  peak_rss           2.23MB ±    0      2.23MB … 2.23MB          0 ( 0%)        0%
  cpu_cycles         7.23G  ±  136M     7.08G  … 7.34G           0 ( 0%)        0%
  instructions       11.5G  ± 37.9      11.5G  … 11.5G           0 ( 0%)        0%
  cache_references    448M  ± 1.03M      447M  …  449M           0 ( 0%)        0%
  cache_misses        148K  ± 42.6K     99.3K  …  180K           0 ( 0%)        0%
  branch_misses       987K  ± 5.27K      983K  …  993K           0 ( 0%)        0%
Benchmark 2 (3 runs): ./zig-out/bin/unigen-debug-arena
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.76s  ± 4.12ms    1.76s  … 1.76s           0 ( 0%)          +  0.4% ±  3.0%
  peak_rss           1.22MB ± 75.7KB    1.18MB … 1.31MB          0 ( 0%)        - 45.1% ±  5.4%
  cpu_cycles         7.27G  ± 17.1M     7.26G  … 7.29G           0 ( 0%)          +  0.6% ±  3.0%
  instructions       11.4G  ± 3.79      11.4G  … 11.4G           0 ( 0%)          -  0.8% ±  0.0%
  cache_references    440M  ± 4.52M      435M  …  444M           0 ( 0%)          -  1.7% ±  1.7%
  cache_misses       43.6K  ± 19.2K     26.5K  … 64.3K           0 ( 0%)        - 70.5% ± 50.8%
  branch_misses      1.04M  ± 2.25K     1.04M  … 1.05M           0 ( 0%)        💩+  5.8% ±  0.9%
```
2025-01-20 19:25:56 -08:00
Ryan Liptak
2d3db866e6 unigen: Remove libc dependency, use ArenaAllocator
Not linking libc avoids potential problems when compiling from/for certain targets (see https://github.com/ghostty-org/ghostty/discussions/3218), and using an ArenaAllocator makes unigen run just as fast (in both release and debug modes) while also taking less memory.

Benchmark 1 (3 runs): ./zig-out/bin/unigen-release-c
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.75s  ± 15.8ms    1.73s  … 1.76s           0 ( 0%)        0%
  peak_rss           2.23MB ±    0      2.23MB … 2.23MB          0 ( 0%)        0%
  cpu_cycles         7.22G  ± 62.8M     7.16G  … 7.29G           0 ( 0%)        0%
  instructions       11.5G  ± 16.0      11.5G  … 11.5G           0 ( 0%)        0%
  cache_references    436M  ± 6.54M      430M  …  443M           0 ( 0%)        0%
  cache_misses        310K  ±  203K      134K  …  532K           0 ( 0%)        0%
  branch_misses      1.03M  ± 29.9K      997K  … 1.06M           0 ( 0%)        0%
Benchmark 2 (3 runs): ./zig-out/bin/unigen-release-arena
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.73s  ± 6.40ms    1.72s  … 1.73s           0 ( 0%)          -  1.0% ±  1.6%
  peak_rss           1.27MB ± 75.7KB    1.18MB … 1.31MB          0 ( 0%)        - 43.1% ±  5.4%
  cpu_cycles         7.16G  ± 26.5M     7.13G  … 7.18G           0 ( 0%)          -  0.9% ±  1.5%
  instructions       11.4G  ± 28.2      11.4G  … 11.4G           0 ( 0%)          -  0.8% ±  0.0%
  cache_references    441M  ± 2.89M      439M  …  444M           0 ( 0%)          +  1.2% ±  2.6%
  cache_misses        152K  ±  102K     35.2K  …  220K           0 ( 0%)          - 50.8% ± 117.8%
  branch_misses      1.05M  ± 13.4K     1.04M  … 1.06M           0 ( 0%)          +  2.0% ±  5.1%

Benchmark 1 (3 runs): ./zig-out/bin/unigen-debug-c
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.75s  ± 32.4ms    1.71s  … 1.77s           0 ( 0%)        0%
  peak_rss           2.23MB ±    0      2.23MB … 2.23MB          0 ( 0%)        0%
  cpu_cycles         7.23G  ±  136M     7.08G  … 7.34G           0 ( 0%)        0%
  instructions       11.5G  ± 37.9      11.5G  … 11.5G           0 ( 0%)        0%
  cache_references    448M  ± 1.03M      447M  …  449M           0 ( 0%)        0%
  cache_misses        148K  ± 42.6K     99.3K  …  180K           0 ( 0%)        0%
  branch_misses       987K  ± 5.27K      983K  …  993K           0 ( 0%)        0%
Benchmark 2 (3 runs): ./zig-out/bin/unigen-debug-arena
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          1.76s  ± 4.12ms    1.76s  … 1.76s           0 ( 0%)          +  0.4% ±  3.0%
  peak_rss           1.22MB ± 75.7KB    1.18MB … 1.31MB          0 ( 0%)        - 45.1% ±  5.4%
  cpu_cycles         7.27G  ± 17.1M     7.26G  … 7.29G           0 ( 0%)          +  0.6% ±  3.0%
  instructions       11.4G  ± 3.79      11.4G  … 11.4G           0 ( 0%)          -  0.8% ±  0.0%
  cache_references    440M  ± 4.52M      435M  …  444M           0 ( 0%)          -  1.7% ±  1.7%
  cache_misses       43.6K  ± 19.2K     26.5K  … 64.3K           0 ( 0%)        - 70.5% ± 50.8%
  branch_misses      1.04M  ± 2.25K     1.04M  … 1.05M           0 ( 0%)        💩+  5.8% ±  0.9%
2025-01-20 18:30:22 -08:00
Qwerasd
3b8ab10776 fix(renderer): clip terminal contents to expected grid size (#4523)
This significantly improves the robustness of the renderers since it
prevents synchronization issues from causing memory corruption due to
out of bounds read/writes while building the cells.

TODO: when viewport is narrower than renderer grid size, fill blank
margin with bg color- currently appears as black, this only affects
DECCOLM right now, and possibly could create single-frame artefacts
during poorly managed resizes, but it's not ideal regardless.
2025-01-20 18:56:13 -05:00
Mitchell Hashimoto
3327d32d66 Added exec permission back in for dolphin context menu item (#5254) 2025-01-20 11:41:53 -08:00
Andrej Daskalov
8a0613bd27 Merge branch 'ghostty-org:main' into main 2025-01-20 20:28:07 +01:00
Mitchell Hashimoto
a977e688cc fix(gtk): confirm tab close on close_tab action (#5166)
#4033 introduced a `ctrl-shift-w` binding to close a tab, but it doesn't
respect the `confirm-close-surface` option. Now `ctrl-shift-w` will ask
for confirmation exactly the same as clicking the close button with the
mouse.
2025-01-20 11:24:01 -08:00
Andrej Daskalov
8c1db16c79 added exec permission back to dolphin action 2025-01-20 20:19:12 +01:00
Mitchell Hashimoto
b82c70fd3c fix: quick terminal hidden by macos menu bar (#5222)
ghostty#5000 changed the window level from `.popupMenu` to `.floating`
to improve IME support. However, this introduced a side effect which
render the Quick Terminal (QT) below the macOS menu bar, whereas
previously it would cover it.

When positioned on `right` and `left`, the top of the QT becomes
partially hidden. This PR adjust the size of the QT to ensure it remains
fully visible and stays below the menu bar.

Before #5000:
![Screen Recording 2025-01-19 at 11 14
48](https://github.com/user-attachments/assets/70565c28-783f-4a2a-a8c9-a2eb16ea50e9)
With #5000:
![Screen Recording 2025-01-19 at 11 13
27](https://github.com/user-attachments/assets/184a1205-0a68-4fa0-a0d8-cc37701529af)
This branch:
![Screen Recording 2025-01-19 at 11 17
32](https://github.com/user-attachments/assets/f589d9e1-c5a0-4ed1-9f9b-651f3cb22f58)
2025-01-20 11:12:11 -08:00
Mitchell Hashimoto
977c9999dd render consecutive shaders to the fbo (opengl) (#5037)
fixes #4729

allows the shaders to sample each other via the fbo texture.

also, a better example would use the full screen e.g.:
"behind.glsl"
```glsl
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    fragColor = vec4(fragCoord/iResolution.xy, 0.0, 1.0);
}
```

"infront.glsl"
```glsl
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    fragColor = texture(iChannel0, fragCoord/iResolution.xy);
}
```
`ghostty --custom-shader=behind.glsl --custom-shader=infront.glsl`
|before|after|
|-|-|
|
![image](https://github.com/user-attachments/assets/d96cc8f1-7d87-4346-963a-a1fb27b81cba)
|
![image](https://github.com/user-attachments/assets/203c3827-9574-4739-9d54-430dc4a47dcc)|
2025-01-20 10:49:33 -08:00
Mitchell Hashimoto
f8ece6392d Fix triple clicking empty line and dragging (#5068)
Fixes core issue #4957. Adds a bool to `SelectLine` allowing the
`selectLine` function to select lines that are empty. When starting a
triple selection on an empty line the initial `selectLine` returns null
because we don't see any characters, in this case we rerun `selectLine`
but short circuit with the `allow_empty_lines`. We need to run
`selectLine` with out allowing empty lines once because if there are
characters on the line we don't want to select empty space.
2025-01-20 10:37:54 -08:00
julia
4cc1fa2111 render consecutive shaders to the fbo
not that big. see comments
2025-01-20 10:36:02 -08:00
Mitchell Hashimoto
a253871942 [macOS] fix: Closing tab with running process while in Stage Manager no longer causes focus to be lost (#5164)
Resolves #5108 

Reproduction: While Stage Manager is activated, create a new tab in
Ghostty, run `sleep 500` in that tab, and try and close it. Make sure to
try both closing the tab with cmd-w and clicking the x; also try
accepting the modal with a mouse click and pressing enter.

Relevant AppKit documentation: [NSAlert
beginSheetModal](https://developer.apple.com/documentation/appkit/nsalert/beginsheetmodal(for:completionhandler:))

What I believe is happening is that the alert modal ("there is still a
process running") is closed automatically after the completion handler
is run, which closes the window on which the alert is attached.
Something seems to go wrong in Stage Manager at this point; perhaps it
detects the alert closing as a "full" window closing and moves focus to
a different window.

The fix I've identified is to manually call the `orderOut` function to
dismiss the alert _before_ the code that closes the window is run.
2025-01-20 10:32:38 -08:00
Mitchell Hashimoto
8ada93d0cb Fix shell-integration-features being ignored with manual shell integration (#5048)
## Descriptions

The code was short-circuiting the shell integration setup when
`shell-integration = none`, which prevented the feature environment
variables from being set. These environment variables are needed even
for manual shell integration to work properly.

## Changes

- Extracted feature environment variables setup into a separate
`setup_features` function

- Modified the shell integration initialization to ensure features are
set up even when `shell-integration = none`

<img width="1126" alt="image"
src="https://github.com/user-attachments/assets/ceeb33f5-26ee-4a3b-a6d5-eed57848c96c"
/>


Fixes https://github.com/ghostty-org/ghostty/issues/5046
2025-01-20 10:28:14 -08:00
otomist
e5a3be3c46 use whitespace instead of new flag for selecting full line 2025-01-20 10:23:41 -08:00
Mitchell Hashimoto
c3ef4d2908 fix(flatpak): construct null-terminated array for arguments (#5213)
The variant format string `^aay` is said to be equivalent to
`g_variant_new_bytestring_array`. Given that no length parameter is
provided, glib assumed a null-terminated array, causing a crash as glib
exceed the read boundaries to copy arbitrary memory.

This commit replaces the array construction code to use its arena
equivalents instead of glib, and make sure that the resulting array is
null-terminated.

Fixes #3616.
2025-01-20 10:22:18 -08:00
Mitchell Hashimoto
0eb6f28375 fix(wuffs): don't premul alpha when loading png (#5189)
This was causing excessively faint/dark areas anywhere there was
transparency, since our image shaders for both Metal and OpenGL assume
the image data has straight alpha (which, I think should be the case for
data transmitted directly with kitty graphics protocol rather than
png-encoded), and do an additional multiplication.

After this change, our image blending looks *like most other apps*, even
though it's still technically wrong since it "should" be done in linear
space. Kitty, for example, does do linear blending for images, which is
*more correct* even though it doesn't look like most apps. On macOS if
`text-blending` is set to `linear` we do get linear blending, and match
Kitty exactly with this change.

In order to have linear blending of images while maintaining non-linear
blending of text for a "native" look on macOS we'd have to use swap
textures with different pixel formats (`*_srgb` / not `*_srgb`) or else
the API validator yells at us (even though it *does* work correctly...)
-- either that or perform our blending in a shader instead of relying on
Metal, which is my plan ultimately whenever I get around to making the
deferred render pipeline.
2025-01-20 10:22:10 -08:00
Mitchell Hashimoto
07d5ae749d Fix typo in binding comments (#5234)
I noticed this when I was reading the keybinding docs on the website and
figured I might as well fix it.
2025-01-20 10:21:15 -08:00
Mitchell Hashimoto
4b9281ee6e gtk: always set the title on the underlying window when using adwaita (#5173)
Fixes #5140
2025-01-20 10:16:03 -08:00
Mitchell Hashimoto
b9b49602cd gtk: don't toggle headerbar on (un)maximize while using SSDs (#5192)
See #5137. We should never display the header bar when using SSDs anyway
2025-01-20 10:11:42 -08:00
Mitchell Hashimoto
f5ff9c0371 chore: update stb_image.h (#5202)
exitting -> exiting
2025-01-20 10:11:20 -08:00
Mitchell Hashimoto
67b828cf21 termio: revise macOS-specific .hushlogin note (#5212)
login(1)'s .hushlogin logic was "fixed" in macOS Sonoma 14.4, so this
comment (and our workaround) is only relevant for versions earlier than
that.

The relevant change to login/login.c is part of system_cmds-979.100.8.

> login.c: look for .hushlogin in home directory (112854361)

-
1bca46ecc5
-
https://github.com/apple-oss-distributions/distribution-macOS/tree/macos-144
2025-01-20 10:10:34 -08:00
Mitchell Hashimoto
2ef04826fd Update iTerm2 colorschemes (#5216)
Upstream revision:
0e23daf592
2025-01-20 10:09:28 -08:00
Mitchell Hashimoto
9ca3cbd94b bash: revert automatic shell integration changes (#5249)
The intention of #5075 was to create a less intrusive, more hermetic
environment in which to source the bash startup files. This caused
problems for multiple people, and I believe that's because the general
expectation is that these files are sourced at global (not function)
scope.

For example, when a file is sourced from within a function scope, any
variables that weren't explicitly exported into the global environment
won't be available outside of the scope of the function. Most system and
personal startup files aren't written with that constraint because it's
not how bash itself loads these files.

As a small improvement over the original code, `rcfile` has been renamed
to `__ghostty_rcfile`. Avoiding leaking this variable while sourcing
these files was a goal of #5075, and prefixing it make it much less of a
potential issue.

This change also reverts the $HOME -> ~/ change. While the ~/ notation
is more concise, using $HOME is more common and easier to implement
safely with regard to quoting.

Fixes #5206
2025-01-20 10:09:06 -08:00
Jon Parise
afa23532b6 bash: revert automatic shell integration changes
The intention of #5075 was to create a less intrusive, more hermetic
environment in which to source the bash startup files. This caused
problems for multiple people, and I believe that's because the general
expectation is that these files are sourced at global (not function)
scope.

For example, when a file is sourced from within a function scope, any
variables that weren't explicitly exported into the global environment
won't be available outside of the scope of the function. Most system and
personal startup files aren't written with that constraint because it's
not how bash itself loads these files.

As a small improvement over the original code, `rcfile` has been renamed
to `__ghostty_rcfile`. Avoiding leaking this variable while sourcing
these files was a goal of #5075, and prefixing it make it much less of a
potential issue.

This change also reverts the $HOME to ~/ change. While the ~/ notation
is more concise, using $HOME is more common and easier to implement
safely with regard to quoting.
2025-01-20 10:56:47 -05:00
Bruno Bachmann
bb58710fa8 Fix typo in binding comments 2025-01-19 14:49:59 -08:00
Damien Mehala
4956d36ee6 fix: quick terminal hidden by macos menu bar
ghostty#5000 changed the window level from `.popupMenu` to `.floating`
to improve IME support. However, this introduced a side effect which
render the Quick Terminal (QT) below the macOS menu bar, whereas
previously it would cover it.

When positioned on `right` and `left`, the top of the QT becomes
partially hidden. This PR adjust the size of the QT to ensure it remains
fully visible and stays below the menu bar.
2025-01-19 11:04:01 +01:00
mitchellh
3f367857fc deps: Update iTerm2 color schemes 2025-01-19 00:58:22 +00:00
Jon Parise
2ee6e005d0 termio: revise macOS-specific .hushlogin note
login(1)'s .hushlogin logic was "fixed" in macOS Sonoma 14.4, so this
comment (and our workaround) is only relevant for versions earlier than
that.

The relevant change to login/login.c is part of system_cmds-979.100.8.

> login.c: look for .hushlogin in home directory (112854361)

- 1bca46ecc5
- https://github.com/apple-oss-distributions/distribution-macOS/tree/macos-144
2025-01-18 15:35:23 -05:00
Leorize
ecad3e75ff fix(flatpak): construct null-terminated array for arguments
The variant format string `^aay` is said to be equivalent to
g_variant_new_bytestring_array. Given that no length parameter is
provided, g_variant_new assumed a null-terminated array, but the array
constructed by the code was not, causing a crash as glib exceed the read
boundaries to copy arbitrary memory.

This commit replaces the array construction code to use its arena
equivalents instead of trying to build one using glib, and make sure
that the resulting array is null-terminated.
2025-01-18 13:47:18 -06:00
Ikko Eltociear Ashimine
0c2c847af3 chore: update stb_image.h
exitting -> exiting
2025-01-18 22:47:18 +09:00
Leah Amelia Chen
68124f60c7 gtk: don't toggle headerbar on (un)maximize while using SSDs
See #5137. We should never display the header bar when using SSDs anyway
2025-01-18 10:42:55 +01:00
Bryan Lee
0016199ec3 Extract keybind actions help generation into a dedicated module 2025-01-18 05:14:50 +08:00
Bryan Lee
8e2c55a5da Improve list-actions command documentation formatting
This commit fixes two issues with the `list-actions` command:

1. Ensures all actions are listed, including those without individual
   documentation but sharing docs with related actions
2. Improves documentation formatting with proper indentation and grouping
2025-01-18 05:14:49 +08:00
Bryan Lee
05fe3e7ec3 Ensure last action's documentation is properly generated
The issue was caused by the documentation generation logic not writing the final buffered content.
2025-01-18 05:14:49 +08:00
Bryan Lee
1b52365541 Add default documentation for undocumented keybind actions
Previously, `ghostty +list-actions` would only show actions that had doc
comments, making it difficult for users to discover all available actions.
This change ensures all actions are listed with appropriate documentation.

For actions without doc comments, we now generate a default message
encouraging contribution.
2025-01-18 05:14:49 +08:00
Bryan Lee
ccd6fd26ec Ensure setup_features runs even when shell detection fails 2025-01-18 05:12:45 +08:00
Bryan Lee
6853a5423f Update the documentation to better explain that shell-integration-features 2025-01-18 05:12:45 +08:00
Bryan Lee
9c1edb5449 Add tests for setup shell integration features 2025-01-18 05:12:44 +08:00
Bryan Lee
8ee4deddb4 Fix shell-integration-features being ignored when shell-integration is none 2025-01-18 05:12:44 +08:00
Qwerasd
c2da843dfd fix(wuffs): don't premul alpha when loading images
It seems like the raw data version of the kitty graphics transmit
operation is meant to be unassociated (aka straight) alpha, though I
can't find any definitive documentation either way- but in any case
unassociated alpha is more common in image formats and makes the
handling easier for the rest of it.

Also removed a redundant call to `decode_frame_config`, since it's
called implicitly when we call `decode_frame` right after.
2025-01-17 14:38:38 -05:00
AltCode
018a888578 macOS: add link to release notes on appcast 2025-01-17 15:18:22 +01:00
Jeffrey C. Ollie
b7d76fe26f gtk: always set the title on the underlying window when using adwaita 2025-01-16 23:51:40 -06:00
Albert Dong
da5ac6aeeb Set alert to nil when modal interacted with 2025-01-16 20:57:41 -08:00
Mitchell Hashimoto
72d085525b fix(Metal): fix incorrect premultiplication of colors (#5172)
`load_color` was multiplying the alpha channel by itself as well, rather
than just the `rgb` channels.

Also made sure to divide alpha out before applying gamma encoding back
to text color when not using linear blending, which was another source
of error.

Probably fixes the problem observed in #5133 -- I couldn't reproduce the
exact color shift, but did see a similar one with some of my test
colors. This most visibly affects dimmed text since it uses an alpha of
less than 1 for the text color.
2025-01-16 20:44:38 -07:00
Qwerasd
2a1b51ec94 fix(Metal): fix incorrect premultiplication of colors
Also make sure to divide alpha out before applying gamma encoding back
to text color when not using linear blending.
2025-01-16 22:28:22 -05:00
Gabriel Holodak
85b1cfa44f fix(gtk): confirm tab close on close_tab action 2025-01-16 18:42:33 -05:00
Albert Dong
860f1f635c Manually call orderOut on terminal close alert
Allowing the alert to be automatically closed after the completion handler finishes doesn't seem to play well when the completion handler closes the window on which the alert is attached
2025-01-16 14:14:48 -08:00
Mitchell Hashimoto
a185ce317b macos: respect the "auto" window decoration setting (#5159) 2025-01-16 13:16:07 -08:00
Mitchell Hashimoto
b4a3ca999a bash: improve prior_trap processing (#5142)
We use `trap` to bootstrap our installation function (__bp_install). We
remove our code upon first execution but need to restore any preexisting
trap calls. We previously used `sed` to process the trap string, but
that had two downsides:

1. `sed` is an external command dependency. It needs to exist on the
system, and we need to invoke it in a subshell (which has some runtime
cost).
2. The regular expression pattern was imperfect and didn't handle
trickier cases like `'` characters in the trap string:

        $ (trap "echo 'hello'" DEBUG; trap -p DEBUG)
        hello
        trap -- 'echo '\''hello'\''' DEBUG

This change removes the dependency on `sed` by locally evaluating the
trap string and extracting any prior trap. This works reliably because
we control the format our trap string, which looks like this (with
newlines expanded):

    __bp_trap_string="$(trap -p DEBUG)"
    trap - DEBUG
    __bp_install

Upstream: https://github.com/rcaloras/bash-preexec/pull/170
2025-01-16 13:04:57 -08:00
Mitchell Hashimoto
d1eb8ccc52 bash: remove sed dependency for history processing (#5141)
We post-process history 1's output to extract the current command. This
processing needs to strip the leading history number, an optional *
character indicating whether the entry was modified (or a space), and
then a space separating character.

We were previously using sed(1) for this, but we can implement an
equivalent transformation using bash's native parameter expansion
syntax.

This also results in ~4x reduction in per-prompt command overhead.

Upstream: https://github.com/rcaloras/bash-preexec/pull/167
2025-01-16 13:04:24 -08:00
Mitchell Hashimoto
b9939611d3 bash: less intrusive automatic shell integration (#5075)
We now use a temporary function (__ghostty_bash_startup) to perform the
bash startup sequence. This gives us a local function scope in which to
store some temporary values (like rcfile). This way, they won't leak
into the sourced files' scopes.

Also, use `~/` instead of `$HOME` for home directory paths as a simpler
shorthand notation.
2025-01-16 13:03:57 -08:00
Mitchell Hashimoto
a5853c4de8 macos: respect the "auto" window decoration setting 2025-01-16 14:03:04 -07:00
Mitchell Hashimoto
6c2c436917 gtk(wayland): respect compositor SSD preferences (#5124)
Compositors can actually tell us whether they want to use CSD or SSD!

(Ignore the context menu changes — they will most likely be unified
after #4952 anyway)
2025-01-16 13:01:31 -08:00
Mitchell Hashimoto
62d3786c66 fix(Metal): handle non-extended padding color transparency (#5126)
We were returning bg colors when we shouldn't have since when we have
background color transparency we need to return any bg color cells as
fully transparent rather than their actual color.

This caused a problem of overly opaque padding when background opacity
was < 1.0
2025-01-16 12:58:46 -08:00
Jon Parise
df2d0b33cc bash: improve prior_trap processing
We use `trap` to bootstrap our installation function (__bp_install). We
remove our code upon first execution but need to restore any preexisting
trap calls. We previously used `sed` to process the trap string, but
that had two downsides:

1. `sed` is an external command dependency. It needs to exist on the
    system, and we need to invoke it in a subshell (which has some
    runtime cost).
2. The regular expression pattern was imperfect and didn't handle
    trickier cases like `'` characters in the trap string:

        $ (trap "echo 'hello'" DEBUG; trap -p DEBUG)
        hello
        trap -- 'echo '\''hello'\''' DEBUG

This change removes the dependency on `sed` by locally evaluating the
trap string and extracting any prior trap. This works reliably because
we control the format our trap string, which looks like this (with
newlines expanded):

    __bp_trap_string="$(trap -p DEBUG)"
    trap - DEBUG
    __bp_install
2025-01-16 08:30:27 -05:00
Jon Parise
07994d10e9 bash: remove sed dependency for history processing
We post-process history 1's output to extract the current command. This
processing needs to strip the leading history number, an optional *
character indicating whether the entry was modified (or a space), and
then a space separating character.

We were previously using sed(1) for this, but we can implement an
equivalent transformation using bash's native parameter expansion
syntax.

This also results in ~4x reduction in per-prompt command overhead.
2025-01-16 08:22:40 -05:00
Jon Parise
6af1850ab4 bash: less intrusive automatic shell integration
We now use a temporary function (__ghostty_bash_startup) to perform the
bash startup sequence. This gives us a local function scope in which to
store some temporary values (like rcfile). This way, they won't leak
into the sourced files' scopes.

Also, use `~/` instead of `$HOME` for home directory paths as a simpler
shorthand notation.
2025-01-16 08:11:26 -05:00
Jeffrey C. Ollie
3159a7bec7 nix: add x11 xfce vm 2025-01-15 20:37:31 -06:00
Qwerasd
b1becb12c0 fix(Metal): handle non-extended padding color transparency
We were returning bg colors when we shouldn't have since when we have
background color transparency we need to return any bg color cells as
fully transparent rather than their actual color.
2025-01-15 18:08:11 -05:00
Leah Amelia Chen
7716f98856 gtk(wayland): respect compositor SSD preferences
Compositors can actually tell us whether they want to use CSD or SSD!
2025-01-15 23:22:52 +01:00
Jeffrey C. Ollie
423133bc3c nix: document how to create custom vms 2025-01-15 11:56:19 -06:00
otomist
1eeb914a4f Merge branch 'main' into fix-triple-clicking-drag 2025-01-15 11:54:22 -05:00
Jeffrey C. Ollie
1ac56a7ac2 nix vm: +fish +zsh -zig 2025-01-15 10:25:03 -06:00
Jeffrey C. Ollie
321119e001 nix vm: more reusability 2025-01-15 10:25:03 -06:00
Jeffrey C. Ollie
e1e2f94681 nix vm: try and make vm creation more re-usable 2025-01-15 10:25:03 -06:00
Jeffrey C. Ollie
268fc1a040 nix vm: simplify vm definition 2025-01-15 10:25:03 -06:00
Jeffrey C. Ollie
450c019b4e nix vm: add plasma and cinnamon vms 2025-01-15 10:25:03 -06:00
Jeffrey C. Ollie
26f6b3ea82 fix typo 2025-01-15 10:25:03 -06:00
Jeffrey C. Ollie
6be0902c09 nix vm: add documentation, add Gnome/X11 VM 2025-01-15 10:25:02 -06:00
Jeffrey C. Ollie
0b16c1eeba nix vm: maybe get vms working on darwin 2025-01-15 10:25:02 -06:00
Jeffrey C. Ollie
dddc2a50a8 nix vm: more slimming 2025-01-15 10:25:02 -06:00
Jeffrey C. Ollie
4bfb1f616c nix: disable geary 2025-01-15 10:25:02 -06:00
Jeffrey C. Ollie
4ff7f6df06 nix: fix dark mode setting 2025-01-15 10:25:02 -06:00
Jeffrey C. Ollie
0b456d14a4 nix: vms for testing ghostty 2025-01-15 10:24:58 -06:00
Mitchell Hashimoto
ff9414d9ea fix(macos): prevent transparency leakage/flash in new/resized surfaces (#5083)
Fixes #4516

By using the `CAMetalLayer`'s `backgroundColor` property instead of
drawing the background color in our shader, it is always stretched to
cover the full surface, even when live-resizing, and it doesn't require
us to draw a frame for it to be initialized so there's no transparent
flash when a new surface is created (as in a new split/tab).

This commit also allows for hot reload of `background-opacity`,
`window-vsync`, and `window-colorspace`.
2025-01-14 19:20:12 -08:00
otomist
4f93864db7 Merge branch 'main' into fix-triple-clicking-drag 2025-01-14 22:17:18 -05:00
Qwerasd
34abe2ceba fix(macos): prevent transparency leakage/flash in new/resized surfaces
By using the `CAMetalLayer`'s `backgroundColor` property instead of
drawing the background color in our shader, it is always stretched to
cover the full surface, even when live-resizing, and it doesn't require
us to draw a frame for it to be initialized so there's no transparent
flash when a new surface is created (as in a new split/tab).

This commit also allows for hot reload of `background-opacity`,
`window-vsync`, and `window-colorspace`.
2025-01-14 20:23:21 -05:00
Mitchell Hashimoto
331b7c754c config: fix window-decoration enum parsing to allow client, none (#5082) 2025-01-14 15:53:09 -08:00
Mitchell Hashimoto
f5670d81d4 config: fix window-decoration enum parsing to allow client, none 2025-01-14 15:40:52 -08:00
Mitchell Hashimoto
9a47cda892 fix: building on systems with older adwaita (#5077)
3cdb9a7 made ghostty unable to compile on my debian system (adwaita
1.2.2) due to `adw_tab_overview_set_show_start_title_buttons` and
`adw_tab_overview_set_show_end_title_buttons` which are since 1.3.0. I
increased the comptime check to 1.4.0, this is fine since the assignment
of `tab_overview` is [already comptime guarded by
1.4.0](3cdb9a7dfe/src/apprt/gtk/Window.zig (L130))
2025-01-14 13:54:03 -08:00
Michael Himing
08a0423b78 fix: building on systems with older adwaita 2025-01-15 08:33:48 +11:00
otomist
daeed453dc Merge branch 'main' into fix-triple-clicking-drag 2025-01-14 14:00:25 -05:00
Mitchell Hashimoto
3cdb9a7dfe gtk(wayland): implement server-sided decorations (#4724)
Fixes #4630 fully unless there exists an X11 API I haven't found yet :p 

~~Depends on first commit of #4723, which is duplicated here for now~~
2025-01-14 10:08:53 -08:00
Leah Amelia Chen
4e0d9b1b27 gtk(wayland): implement server-side decorations 2025-01-14 09:57:59 -08:00
otomist
95debc59d1 add and use flag for selecting empty lines in the selectLine function 2025-01-14 12:04:43 -05:00
Mitchell Hashimoto
d1fd22ae80 fix: Ensure file paths are properly escaped during pasteboard paste operations (#5036)
This PR ensures file paths derived from pasteboard operations are
properly escaped:

## Before fix:
Attempting to cd into these paths rendered the following error: `cd: too
many arguments` because the directory name includes spaces or special
characters, which need to be handled correctly when running the cd
command.


https://github.com/user-attachments/assets/4d03a94a-fa14-4ac8-9c1e-1ad8b0b939a7

## Afer fix

File paths are now properly escaped and we can correctly `cd` into these
paths.


https://github.com/user-attachments/assets/72e794c0-31e4-43b8-bebf-76c161924bd1

This change ensures Ghostty has the same behaviour as Iterm2.
2025-01-13 19:59:26 -08:00
Alexandre Antonio Juca
39bb949973 fix: Ensure file paths derived from pasteboard operations are properly escaped 2025-01-14 00:18:28 +01:00
Mitchell Hashimoto
5081e65570 Metal alpha blending fixes + color handling improvements (#4913)
This PR addresses #2125 for the Metal renderer. Both options are
available: "Apple-style" blending where colors are blended in a wide
gamut color space, which reduces but does not eliminate artifacts; and
linear blending where colors are blended in linear RGB.

Because this doesn't add support for linear blending on Linux, I don't
know whether the issue should be closed or not.

### List of changes in no particular order
- We now set the layer's color space in the renderer not in the apprt
- We always set the layer to Display P3 color spaces
- If the user hasn't configured their `window-colorspace` to
`display-p3` then terminal colors are automatically converted from sRGB
to the corresponding Display P3 color in the shader
- Background color is not set with the clear color anymore, instead we
explicitly set all bg cell colors since this is needed for minimum
contrast to not break with dark text on the default bg color (try it
out, it forces it fully white right now), and we just draw the
background as a part of the bg cells shader. Note: We may want to move
the main background color to be the `backgroundColor` property on the
`CAMetalLayer`, because this should fix the flash of transparency during
startup (#4516) and the weirdness at the edge of the window when
resizing. I didn't make that a part of this PR because it requires
further changes and my changes are already pretty significant, but I can
make it a follow-up.
- Added a config option for changing alpha blending between "native"
blending, where colors are just blended directly in sRGB (or Display P3)
and linear blending, where colors are blended in linear space.
- Added a config option for an experimental technique that I think works
pretty well which compensates for the perceptual thinning and thickening
of dark and light text respectively when using linear blending.
- Custom shaders can now be hot reloaded with config reloads.
- Fixed a bug that was revealed when I changed how we handle
backgrounds, page widths weren't being set while cloning the screen.

### Main takeaways
Color blending now matches nearly identically to Apple apps like
Terminal.app and TextEdit, not *quite* identical in worst case
scenarios, off by the tiniest bit, because the default color space is
*slightly* different than Display P3.

Linear alpha blending is now available for mac users who prefer more
accurate color reproduction, and personally I think it looks very nice
with the alpha correction turned on, I will be daily driving that
configuration.

### Future work
- Handle primary background color with `CALayer.backgroundColor` instead
of in shader, to avoid issues around edges when resizing.
- Parse color space info directly from ICC profiles and compute the
color conversion matrix dynamically, and pass it as a uniform to the
shaders.
- Port linear blending option to OpenGL.
- Maybe support wide gamut images (right now all images are assumed to
be sRGB).
2025-01-13 14:12:37 -08:00
Mitchell Hashimoto
c1938d12f0 gtk: add config entry to hide titlebar when the window is maximized (#5028)
Fixes #3381

Note that #4936 will need to be merged or you'll need to rely on Gnome's
default keybinding for unmaximizing a window (super+down).
2025-01-13 14:05:50 -08:00
Mitchell Hashimoto
a8b9c5bea5 config: remove experimental linear and merge into text-blending 2025-01-13 13:59:37 -08:00
Jeffrey C. Ollie
f24d70b7ec gtk: add config entry to hide titlebar when the window is maximized
Fixes #3381

Note that #4936 will need to be merged or you'll need to rely on Gnome's
default keybinding for unmaximizing a window (super+down).
2025-01-13 15:53:20 -06:00
Qwerasd
fca336c32d Metal: blend in Display P3 color space, add option for linear blending
This commit is quite large because it's fairly interconnected and can't
be split up in a logical way. The main part of this commit is that alpha
blending is now always done in the Display P3 color space, and depending
on the configured `window-colorspace` colors will be converted from sRGB
or assumed to already be Display P3 colors. In addition, a config option
`text-blending` has been added which allows the user to configure linear
blending (AKA "gamma correction"). Linear alpha blending also applies to
images and makes custom shaders receive linear colors rather than sRGB.

In addition, an experimental option has been added which corrects linear
blending's tendency to make dark text look too thin and bright text look
too thick. Essentially it's a correction curve on the alpha channel that
depends on the luminance of the glyph being drawn.
2025-01-13 13:50:29 -08:00
Qwerasd
5cf7575967 fix(PageList): when cloning, explicitly set cols
Otherwise pages may have the wrong width if they were resized down with
a fast path that just chanes the size without adjusting capacity at all.
2025-01-13 13:50:29 -08:00
Mitchell Hashimoto
844f20d01f Handle setting _NET_WM_STATE (#4936)
As recommended in
https://github.com/ghostty-org/ghostty/pull/4927#issuecomment-2585003934,
adds a config option `maximize` for starting a window in a maximized
state in terms of window properties. Also adds a `toggle_maximize`
keybind to allow users to manually toggle this feature on and off.

It might make more sense to make this an optional config value so that
we don't toggle the state off if the WM already handles that for us, but
I'll let a reviewer decide.

Closes https://github.com/ghostty-org/ghostty/issues/4646
2025-01-13 13:14:31 -08:00
Mitchell Hashimoto
e3ced14393 fix(window): ensure last_tab action on linux navigates to last tab (#5004)
Previously, the logic navigated to the second-to-last tab instead of the
last tab due to an off-by-one error. This updates the implementation so
that the index calculation to accurately target the last tab. In the
`gotoLastTab` method there was a decrement in the number of max number
of tabs and another increment in the `goToTab` method to get the actual
tab index.
2025-01-13 13:12:49 -08:00
Mitchell Hashimoto
b7eb9bfef1 Fix URL handling in pasteboard operations (#5029)
## Description
When pasting URLs from clipboard, the behavior varies based on the
source:

1. From browser address bar:

   - Pasteboard types: ["public.utf8-plain-text", "NSStringPboardType"]
   
   - Handled as plain text, resulting in correct full URL paste

2. From clipboard history tools, such as Raycast clipboard history:
   
   - Pasteboard types include "public.url" and related URL types
   
- URL was being processed through NSURL, which only extracted the path
component
   
   - This resulted in incomplete URLs

## Changes

- Modified `getOpinionatedStringContents()` to differentiate URL types:

- For file URLs (`file://`): preserve existing behavior, return path
only
  
- For web URLs (`http://`, `https://`): return full URL string via
`absoluteString`
  
  - For non-URL content: maintain existing plain text handling

## Related Issues

Fixes #5026

@caarlos0 Could you please help check if this resolves the issue you
were encountering?
2025-01-13 13:06:11 -08:00
Mitchell Hashimoto
592efb4b97 terminal: keep track of colon vs semicolon state in CSI params (#5033)
Fixes #5022

The CSI SGR sequence (CSI m) is unique in that its the only CSI sequence
that allows colons as delimiters between some parameters, and the colon
vs. semicolon changes the semantics of the parameters.

Previously, Ghostty assumed that an SGR sequence was either all colons
or all semicolons, and would change its behavior based on the first
delimiter it encountered.

This is incorrect. It is perfectly valid for an SGR sequence to have
both colons and semicolons as delimiters. For example, Kakoune sends the
following:

    ;4:3;38;2;175;175;215;58:2::190:80:70m

This is equivalent to:

  - unset (0)
  - curly underline (4:3)
  - foreground color (38;2;175;175;215)
  - underline color (58:2::190:80:70)

This commit changes the behavior of Ghostty to track the delimiter per
parameter, rather than per sequence. It also updates the SGR parser to
be more robust and handle the various edge cases that can occur. Tests
were added for the new cases.
2025-01-13 13:00:28 -08:00
Mitchell Hashimoto
ca5471fb03 gtk: hide titlebar if fullscreened (#5008)
Partially addresses #3381
2025-01-13 12:58:47 -08:00
Mitchell Hashimoto
7aed08be40 terminal: keep track of colon vs semicolon state in CSI params
Fixes #5022

The CSI SGR sequence (CSI m) is unique in that its the only CSI sequence
that allows colons as delimiters between some parameters, and the colon
vs. semicolon changes the semantics of the parameters.

Previously, Ghostty assumed that an SGR sequence was either all colons
or all semicolons, and would change its behavior based on the first
delimiter it encountered.

This is incorrect. It is perfectly valid for an SGR sequence to have
both colons and semicolons as delimiters. For example, Kakoune sends
the following:

    ;4:3;38;2;175;175;215;58:2::190:80:70m

This is equivalent to:

  - unset (0)
  - curly underline (4:3)
  - foreground color (38;2;175;175;215)
  - underline color (58:2::190:80:70)

This commit changes the behavior of Ghostty to track the delimiter per
parameter, rather than per sequence. It also updates the SGR parser to
be more robust and handle the various edge cases that can occur. Tests
were added for the new cases.
2025-01-13 12:47:07 -08:00
Bryan Lee
08314d414f Preserve full URL when pasting from clipboard 2025-01-14 00:48:56 +08:00
Mitchell Hashimoto
132c4f1f68 Fix backslash comment in ghostty.h (#5021)
The other half of https://github.com/ghostty-org/ghostty/pull/3173. :)
2025-01-13 07:07:03 -08:00
Andreas Skielboe
e288096c26 Fix backslash comment in ghostty.h 2025-01-13 11:49:21 +01:00
Jeffrey C. Ollie
7ac017b154 gtk: hide titlebar if fullscreened
Partially addresses #3381
2025-01-12 19:34:20 -06:00
Pavlos Karakalidis
5cd990bec5 fix(window): ensure last_tab action on linux navigates to last tab
Previously, the logic navigated to the second-to-last tab instead of the
last tab due to an off-by-one error. This updates the implementation so
that the index calculation to accurately target the last tab.
In the `gotoLastTab` method there was a decrement in the number of max
number of tabs and another increment in the `goToTab` method to get the
actual tab index.
2025-01-12 23:48:15 +02:00
Mitchell Hashimoto
a2445359c4 macos: only set quick terminal level to popUpMenu during animation (#5000)
Fixes #4999

We need to set the level to popUpMenu so that we can move the window
offscreen and animate it over the main menu, but we must reset it back
to floating after the animation is complete so that other higher-level
windows can be shown on top of it such as IME windows.
2025-01-12 13:02:25 -08:00
Mitchell Hashimoto
ea0704148d macos: only set quick terminal level to popUpMenu during animation
Fixes #4999

We need to set the level to popUpMenu so that we can move the window
offscreen and animate it over the main menu, but we must reset it back
to floating after the animation is complete so that other higher-level
windows can be shown on top of it such as IME windows.
2025-01-12 12:49:43 -08:00
Mitchell Hashimoto
caddf59db5 For GTK runtime, don't call cursorPosCallback if cursor pos didn't actually change. (#4973)
Fixes #3345

Piggybacking on the logic introduced PR
https://github.com/ghostty-org/ghostty/pull/3997, this patch prevents
mouse motion events with the same cursor position triggered by the
window title updating from un-hiding the mouse even when
`mouse-hide-while-typing` config item is true.
2025-01-12 07:21:39 -08:00
Mitchell Hashimoto
e3b6bb71a0 fix(gtk): fix segfault on ctrl-d on older adw (#4971)
Ghostty with libadwaita older than 1.4.0 (in my case, that of debian
bookworm) will segfault when you exit ghostty with ctrl+d. This was
already fixed by #3694 but it re-appeared during a refactor. The fix
simply uses a reference to `tab.window.window` to call
`gtk_window_destroy`, because `tab.window` has already been [destroyed
by a signal
handler](6cbd69da78/src/apprt/gtk/Tab.zig (L130-L137))
triggered in the old libadwaita path.

Original issue: #3135
2025-01-11 22:45:00 -08:00
james
faea09bbde for GTK runtime, don't call cursorPosCallback in mouse motion callback if the cursor hasn't actually moved 2025-01-11 23:53:19 -05:00
Michael Himing
a3bb2df94f fix(gtk): fix segfault on ctrl-d on older adw 2025-01-12 14:46:05 +11:00
Mitchell Hashimoto
d4190c9c02 Update iTerm2 colorschemes (#4967)
Upstream revision:
25cb3c3f52
2025-01-11 19:45:09 -08:00
Mitchell Hashimoto
c5dfabb15b feat: ensure text, files and URLs can be drag and dropped to terminal (#4962)
This PR ensures one can drag and drop the following things to the
terminal window:

- File (Inserts the file path into the terminal)
- URL
- Text


This resolves #4932
2025-01-11 19:40:08 -08:00
Mitchell Hashimoto
a2d2cfea59 macos: move drop implementation to separate extension 2025-01-11 19:27:36 -08:00
Alexandre Antonio Juca
a06fc4ff11 feat: ensure text, files and URLs can be drag and dropped to terminal window 2025-01-11 19:04:45 -08:00
mitchellh
50e33a6665 deps: Update iTerm2 color schemes 2025-01-12 01:01:09 +00:00
Mitchell Hashimoto
bdbd0263a1 Correct IME position calculation with window padding (#4949)
This PR addresses two IME-related issues:

1. Fix IME position calculation with window padding, fixed
https://github.com/ghostty-org/ghostty/issues/4933

- Add padding offset to IME position calculation to correctly position
the IME window
   
- This ensures IME window appears at the right position when
`window-padding-x/window-padding-y` are set

2. Clear selection when IME input starts

- Previously, when using CJK IME with text selected, the IME window
would appear near the selection
   
   - Now it clears selection and shows IME window at cursor position
   
   - This matches the behavior of iTerm2 and Terminal.app


## Before


https://github.com/user-attachments/assets/cd5c7b55-2083-40ce-a528-9a98898a6498

## After


https://github.com/user-attachments/assets/9f2c17c6-e885-45f3-9ab1-a9c3858690ec
2025-01-11 14:19:04 -08:00
Mitchell Hashimoto
f7b50ce727 macos: paste multiple files separated by space (#4956)
https://github.com/ghostty-org/ghostty/discussions/4892#discussioncomment-11808631
2025-01-11 14:10:57 -08:00
Mitchell Hashimoto
bfe56d04d5 parse ConEmu OSC9;5 (#4948)
| Sequence | Description |
| - | - |
ESC ] 9 ; 5 ST | Wait for Enter/Space/Esc. Set environment variable
“ConEmuWaitKey” to “ENTER”/”SPACE”/”ESC” on exit.

Source:
https://conemu.github.io/en/AnsiEscapeCodes.html#OSC_Operating_system_commands

#3125
2025-01-11 14:09:12 -08:00
Mitchell Hashimoto
6c5c5b2ec0 core: clear selection whenever preedit is changed 2025-01-11 14:06:42 -08:00
Mitchell Hashimoto
0811b1d5ac macos: paste multiple files separated by space
https://github.com/ghostty-org/ghostty/discussions/4892#discussioncomment-11808631
2025-01-11 13:58:09 -08:00
Damien Mehala
fc99c99b74 code review 2025-01-11 22:19:42 +01:00
Bryan Lee
af5e423ea5 Clear selection when IME input starts 2025-01-12 01:48:48 +08:00
Bryan Lee
2409d46600 Correct IME position calculation with window padding 2025-01-12 01:15:53 +08:00
Damien Mehala
95fc1d64c8 parse ConEmu OSC9;5 2025-01-11 17:24:13 +01:00
Adam Wolf
c9636598fc chore: rename config value to maximize and move startup logic to proper location 2025-01-10 23:24:00 -06:00
Adam Wolf
8102fddceb apprt/gtk: add toggle_maximize keybind and window-maximize config option 2025-01-10 22:42:41 -06:00
Mitchell Hashimoto
918ccdba5c declare StartupWMClass in .desktop (#4930)
This allows the app to be pinned in the dock/task manager in several
common desktop environments.

When not set this happens when you open the pinned application: 


![image](https://github.com/user-attachments/assets/81316c9c-4cd4-4a7c-ba3e-74b9a7abc1fa)
2025-01-10 19:28:28 -08:00
Samuel
941915b862 declare StartupWMClass in .desktop 2025-01-10 22:26:48 -04:00
Mitchell Hashimoto
5a4aac7e09 gtk: fix crash due to accessing invalidated pointer to adwaita notebook (#4926)
#4235 introduced a crash when you closed the last tab in a window. In
`NotebookAdw.closeTab` a `defer` was added that references `self`. If
the last tab is closed we destroy the window. As part of that process
`self` becomes invalid because the window has been de-initialized. The
`defer` fires at the end of the function, referencing the invalid
pointer and causing a crash.

```
info(surface): surface closed addr=7fffe400a000
debug(gtk): window destroy

(process:1032400): Gtk-CRITICAL **: 18:40:17.674: gtk_widget_unparent: assertion 'GTK_IS_WIDGET (widget)' failed
Segmentation fault at address 0x7ffff4dad040
/home/jeff/dev/ghostty/src/apprt/gtk/notebook_adw.zig:128:19: 0x340226a in closeTab (ghostty)
        defer self.forcing_close = false;
                  ^
/home/jeff/dev/ghostty/src/apprt/gtk/notebook.zig:157:40: 0x336ca86 in closeTab (ghostty)
            .adw => |*adw| adw.closeTab(tab),
                                       ^
/home/jeff/dev/ghostty/src/apprt/gtk/Window.zig:468:27: 0x327628d in closeTab (ghostty)
    self.notebook.closeTab(tab);
                          ^
/home/jeff/dev/ghostty/src/apprt/gtk/Tab.zig:121:25: 0x336581b in remove (ghostty)
    self.window.closeTab(self);
                        ^
/home/jeff/dev/ghostty/src/apprt/gtk/Surface.zig:207:34: 0x326a213 in remove (ghostty)
            .tab_ => |t| t.remove(),
                                 ^
/home/jeff/dev/ghostty/src/apprt/gtk/Surface.zig:722:30: 0x31e3a3a in close (ghostty)
        self.container.remove();
                             ^
/home/jeff/dev/ghostty/src/Surface.zig:733:26: 0x31e1dc4 in close (ghostty)
    self.rt_surface.close(self.needsConfirmQuit());
                         ^
/home/jeff/dev/ghostty/src/Surface.zig:925:23: 0x31e143e in handleMessage (ghostty)
            self.close();
                      ^
/home/jeff/dev/ghostty/src/App.zig:486:34: 0x31e2d05 in surfaceMessage (ghostty)
        try surface.handleMessage(msg);
                                 ^
/home/jeff/dev/ghostty/src/App.zig:252:62: 0x31e3005 in drainMailbox (ghostty)
            .surface_message => |msg| try self.surfaceMessage(msg.surface, msg.message),
                                                             ^
/home/jeff/dev/ghostty/src/App.zig:138:26: 0x31e378e in tick (ghostty)
    try self.drainMailbox(rt_app);
                         ^
/home/jeff/dev/ghostty/src/apprt/gtk/App.zig:1279:31: 0x31e3e42 in run (ghostty)
        try self.core_app.tick(self);
                              ^
/home/jeff/dev/ghostty/src/main_ghostty.zig:112:24: 0x31e52f4 in main (ghostty)
    try app_runtime.run();
                       ^
/nix/store/h6lccra69nr23676qq32h9qn1fba24v1-zig-0.13.0/lib/std/start.zig:524:37: 0x31e5e0e in main (ghostty)
            const result = root.main() catch |err| {
                                    ^
???:?:?: 0x7ffff682a1fb in ??? (libc.so.6)
Unwind information for `libc.so.6:0x7ffff682a1fb` was not available, trace may be incomplete

fish: Job 2, './zig-out/bin/ghostty --config-…' terminated by signal SIGABRT (Abort)
```
2025-01-10 17:31:24 -08:00
Jeffrey C. Ollie
0a26321e9d gtk: add some comments about closing the last tab invaldating self pointer 2025-01-10 19:19:11 -06:00
Jeffrey C. Ollie
16233b16e7 gtk: fix crash due to accessing invalidated pointer to adwaita notebook 2025-01-10 19:12:08 -06:00
Mitchell Hashimoto
da558f2678 fix(gtk): add close confirmation for tabs (#4235)
On the discord this bit of feedback came up from
[here](https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blob;f=misc/terminfo.src;h=ccd4c32099cf740d331eeaa3955f48f177435878;hb=a28a11d84d969cfdc876e158deae7870e8948a24#l8323)
```
8323 # - ghostty has tabs (imitating gnome-terminal); when closing a tab with a
8324 #   running process (e.g., a hung vttest), ghostty does not prompt about the 
8325 #   process to be killed.
```

This PR adds confirmation to the places where tabs are closed directly 

Fixes: https://github.com/ghostty-org/ghostty/issues/4234
2025-01-10 15:45:10 -08:00
Mitchell Hashimoto
00137c4189 apprt/gtk: adw tab view close confirmation 2025-01-10 15:32:32 -08:00
Leigh Oliver
8c1ad59de7 remove unnecessary struct 2025-01-10 15:05:11 -08:00
Leigh Oliver
b7b5b9bbf5 fix(gtk): add close confirmation for tabs 2025-01-10 15:05:11 -08:00
Mitchell Hashimoto
126c0505e2 fix: ensure terminal windows don't part from parent window when toggling visibility (#4789)
As of version 1.0.1 (macOS build) when running the toggle visibility
action a window with tabs is made into multiple windows.

This PR ensures terminal tabs are reconstructed and correctly placed
into its parent window.

# Demo


https://github.com/user-attachments/assets/44f14bca-15a1-4717-ba0a-44f0767feec3


FYI: I will create another PR to ensure the right tab is focused after
the main window is restored.

Solves #4329
2025-01-10 14:52:33 -08:00
Mitchell Hashimoto
200aee9acf macos: rework toggle_visibility to better match iTerm2
Two major changes:

1. Hiding uses `NSApp.hide` which hides all windows, preserves tabs, and
   yields focus to the next app.

2. Unhiding manually tracks and brings forward only the windows we hid.
   Proper focus should be retained.
2025-01-10 14:40:02 -08:00
Alexandre Antonio Juca
61a78efa83 chore: revert on TerminalManager changes 2025-01-10 14:16:47 -08:00
Alexandre Antonio Juca
3a5aecc216 fix: hide windows without calling orderOut API 2025-01-10 14:16:47 -08:00
Alexandre Antonio Juca
4dd9fe5cfd fix: ensure terminal tabs are reconstructed in main window after toggling visibility 2025-01-10 14:16:47 -08:00
Mitchell Hashimoto
d3de3448cc gtk: add config option to disable GTK OpenGL debug logging (#4662)
The only change from default should be that when running a ReleaseFast
binary you won't get OpenGL debugging.
2025-01-10 14:01:13 -08:00
Jeffrey C. Ollie
96e427cd6a gtk: default to opengl debugging only on debug builds 2025-01-10 15:48:20 -06:00
Mitchell Hashimoto
8e52c6d12b Reduce ghost emoji flash in title bar (#4804)
Fixes #4799

This PR attempts to reduce the flash caused by the ghost emoji in the
title bar when opening new windows.

## Changes:

- Initialize `SurfaceView.title` with empty string instead of ghost
emoji

- Simplify title computation logic in `TerminalView`

- Adding a 500ms fallback timer for "👻"

	- Canceling timer if title is set

## Current Status:

While these changes reduce the initial ghost emoji flash, there's still
a brief moment where a folder emoji appears alone in the title bar when
opening a new window. This suggests there might be a race condition or
timing issue with how the title is being set and updated.


https://github.com/user-attachments/assets/3688c9f3-1727-4379-b04d-0bd6ac105728

Would appreciate feedback on the remaining flash issue and suggestions
for further improvements.
2025-01-10 13:46:45 -08:00
Mitchell Hashimoto
b783e12b93 ci: required checks must be named separately (#4919) 2025-01-10 13:45:00 -08:00
Mitchell Hashimoto
f5add68100 ci: required checks must be named separately 2025-01-10 13:31:28 -08:00
Mitchell Hashimoto
4af44c5460 ci: avoid "successful failure" of status check job by inspecting needs (#4918)
Thanks to @ryanec for this tip.
2025-01-10 13:29:50 -08:00
Mitchell Hashimoto
6237377a59 ci: avoid "successful failure" of status check job by inspecting needs
Thanks to @ryanec for this tip.
2025-01-10 13:28:21 -08:00
Jeffrey C. Ollie
cd638588c4 gtk: better method for setting GDK env vars 2025-01-10 15:21:24 -06:00
Jeffrey C. Ollie
06a57842af gtk: add config option to control GSK_RENDERER env var 2025-01-10 15:21:24 -06:00
Jeffrey C. Ollie
13e96c7ec8 gtk: add config option to disable GTK OpenGL debug logging 2025-01-10 15:21:21 -06:00
Mitchell Hashimoto
c4ece2a141 GTK: refactor headerbar into separate Adwaita & GTK structs (#4850)
There's one behavioral change here. Before this patch, if
`gtk-titlebar=false` we _never_ created a headerbar. This explicitly
contradicted the comments in the source, and the documentation for
`gtk-titlebar` imply that if a window starts out without a titlebar it
can be brought back later with the `toggle_window_decorations` keybind
action.

After this patch, a headerbar is always created, but if
`gtk-titlebar=false` or `window-decoration=false` it's immediately
hidden.

I'm not sure how this interacts with the current SSD/CSD detection that
seems to happen when running Ghostty on non-Gnome DEs so it'll be
important to get #4724 merged (plus any follow ups) to enable more
explicit control of SSD/CSD.
2025-01-10 13:14:39 -08:00
Mitchell Hashimoto
96b3db0b8c docs: update copy-on-select documentation (#4909)
As per discussion in
https://github.com/ghostty-org/ghostty/discussions/4898, this updates
the docs for `copy-on-select` to correct the middle-click paste behavior
change as per https://github.com/ghostty-org/ghostty/pull/4733. I also
opted to remove the multiple "if supported by OS/ for supported systems"
statements since it's supported everywhere now, AFAIK.
2025-01-10 13:13:12 -08:00
Mitchell Hashimoto
e475560af0 vim: update configuration to include theme files (#4893)
Extends vim validation and file type detection to theme files

cc @gpanders @beaumccartney (as you were involved on the previous vim
PR)
2025-01-10 13:12:25 -08:00
Mitchell Hashimoto
61a6e670eb ci: add required checks jobs (#4916)
This is a hack to make it easier for our GitHub branching rules to
require a single check to pass before merging. This lets us describe the
required checks in code rather than via the GH UI.
2025-01-10 13:12:00 -08:00
Mitchell Hashimoto
2fb0d99f00 ci: add required checks jobs
This is a hack to make it easier for our GitHub branching rules to
require a single check to pass before merging. This lets us describe the
required checks in code rather than via the GH UI.
2025-01-10 12:57:41 -08:00
Mitchell Hashimoto
8b8c7ecf1d gtk: unify Wayland and X11 platforms, implement background blur for KDE X11 (#4723)
Part of #4626. Please let me know if the architecture looks alright —
I'm fairly convinced that I'm being unorthodox here.
2025-01-10 12:21:03 -08:00
Mitchell Hashimoto
d26c114b5d apprt/gtk: make sure noop winproto never initializes 2025-01-10 12:10:59 -08:00
Jeffrey C. Ollie
010f4d167d GTK: refactor headerbar into separate Adwaita & GTK structs 2025-01-10 13:46:29 -06:00
Mitchell Hashimoto
799f5b8239 Fix wayland-scanner/protocols packaging dependency (#4910)
By updating zig-wayland:
https://codeberg.org/ifreund/zig-wayland/issues/67
2025-01-10 10:10:42 -08:00
Mitchell Hashimoto
6e411d60f2 Fix wayland-scanner/protocols packaging dependency
By updating zig-wayland:
https://codeberg.org/ifreund/zig-wayland/issues/67
2025-01-10 09:57:29 -08:00
Mitchell Hashimoto
2f81c360bd ci: typos 2025-01-10 09:42:41 -08:00
Mitchell Hashimoto
be0370cb0e ci: test gtk-wayland in the GTK matrix 2025-01-10 09:41:14 -08:00
Mitchell Hashimoto
ed81b62ec2 apprt/gtk: winproto
Rename "protocol" to "winproto".
2025-01-10 09:39:34 -08:00
Onno Siemens
19cfd99439 docs: update copy-on-select documentation 2025-01-10 18:11:57 +01:00
Anund
c03828e032 vim: work with theme config files 2025-01-11 02:45:47 +11:00
Leah Amelia Chen
405a897230 gtk(x11): implement background blur for KDE/KWin on X11 2025-01-09 14:57:02 -08:00
Leah Amelia Chen
03fee2ac33 gtk: unify Wayland and X11 platforms 2025-01-09 14:57:02 -08:00
Mitchell Hashimoto
6ef757a8f8 Revert "termio/exec: fix SIGPIPE crash when reader exits early"
This reverts commit 3e24e96af5.
2025-01-09 12:43:41 -08:00
Mitchell Hashimoto
12ce9f2e3b feat(GTK): show menu in context menu if titlebar is disabled (#4864)
This PR addresses https://github.com/ghostty-org/ghostty/issues/4732.

While @tristan957 suggested alternative approaches, this implementation
provides a straightforward way to make the menu accessible when the
window decoration is disabled. It follows patterns seen in other GTK
apps for handling submenus, though not strictly in the context menu
format truth be told.

If there’s a better way to approach this or further refinements needed,
I’m happy to discuss and iterate. This has been a minor issue I’ve
encountered personally, and I’d like to help improve the experience for
others as well.

Small video of how it looks:


https://github.com/user-attachments/assets/59548fef-f11c-421f-b05b-be81eab6ce06
2025-01-09 07:28:42 -08:00
Ismael Arias
b25c593309 feat(GTK): remove comment 2025-01-09 12:52:35 +01:00
Ismael Arias
ae81edfcbf feat(gtk): show menu in context menu if titlebar is disabled 2025-01-09 12:50:49 +01:00
Mitchell Hashimoto
2d7706ec4f macos: Handle ctrl characters in IME input (#4854)
Fixes:
https://github.com/ghostty-org/ghostty/issues/4634#issuecomment-2573469532

This commit fixes two issues:

1. `libghostty` must not override ctrl+key inputs if we are in a preedit
state. This allows thigs like `ctrl+h` to work properly in an IME.

2. On macOS, when an IME commits text, we strip the control modifier
from the key event we send to libghostty. This is a bit of a hack but
this avoids triggering special ctrl+key handling.
2025-01-08 22:07:15 -08:00
Mitchell Hashimoto
1636ac88fc macos: Handle ctrl characters in IME input
Fixes: https://github.com/ghostty-org/ghostty/issues/4634#issuecomment-2573469532

This commit fixes two issues:

1. `libghostty` must not override ctrl+key inputs if we are in a preedit
   state. This allows thigs like `ctrl+h` to work properly in an IME.

2. On macOS, when an IME commits text, we strip the control modifier
   from the key event we send to libghostty. This is a bit of a hack but
   this avoids triggering special ctrl+key handling.
2025-01-08 21:54:34 -08:00
Mitchell Hashimoto
a7c108a11c macos: improve initial size calculation (#4851)
Fixes #4801

Our size calculation before improperly used a screens frame instead of
its visibleFrame. Additionally, we didn't properly account for origin
needing to move in order to fit the window on the screen.

Apparently, setting a frame height to high crashes AppKit. The width
gets clamped by AppKit but the height does not. Fun!
2025-01-08 21:33:01 -08:00
Mitchell Hashimoto
aafe7deae7 macos: improve initial size calculation
Fixes #4801

Our size calculation before improperly used a screens frame instead of
its visibleFrame. Additionally, we didn't properly account for origin
needing to move in order to fit the window on the screen.

Apparently, setting a frame height to high crashes AppKit. The width
gets clamped by AppKit but the height does not. Fun!
2025-01-08 21:20:58 -08:00
Mitchell Hashimoto
1057fd23be build: update zig-wayland to use new LazyPath API (#4849)
This is a more idiomatic way to handle build paths in Zig 0.13 and
later.
2025-01-08 20:08:32 -08:00
Mitchell Hashimoto
622cc3f9c7 build: update zig-wayland to use new LazyPath API
This is a more idiomatic way to handle build paths in Zig 0.13 and
later.
2025-01-08 19:55:57 -08:00
Mitchell Hashimoto
19ffb0b51f Implement a size-limit function for GTK (#4840)
A "size-limit" function has been implemented for GTK which calls
gtk_widget_set_size_request() to set the minimum widget/window size.
Without
this function, it's left to GTK to set the minimum size which is usually
a lot larger than the documented 10x4 cell minimum size. This doesn't
fix the
issue completely as GTK retains the final say in how small a window can
be
but it gets closer.

Resolves: #4836
2025-01-08 18:49:05 -08:00
Mitchell Hashimoto
08a8bddd38 gtk: clean up context menu creation & refresh (#4834) 2025-01-08 18:48:47 -08:00
Mitchell Hashimoto
8c457fc992 fix: update zig version regex in windows build workflow (#4847)
The [Windows build job in the test workflow was
failing](https://github.com/ghostty-org/ghostty/actions/runs/12679651693/job/35339885415)
because it couldn't extract
the Zig version from _build.zig_. This was caused by a recent refactor
that changed
[how the Zig version is
specified](eb40cce45e (diff-f87bb3596894756629bc39d595fb18d479dc4edf168d93a911cadcb060f10fccR6))
in _build.zig_.
2025-01-08 18:48:38 -08:00
Bryan Lee
37256ec6a2 ci: move version output after variable definition 2025-01-09 10:31:41 +08:00
Bryan Lee
bec690532d ci: update zig version regex in windows build 2025-01-09 10:24:00 +08:00
George Joseph
dac13701e3 Implement a size-limit function for GTK
A "size-limit" function has been implemented for GTK which calls
gtk_widget_set_size_request() to set the minimum widget/window size. Without
this function, it's left to GTK to set the minimum size which is usually
a lot larger than the documented 10x4 cell minimum size.  This doesn't fix the
issue completely as GTK retains the final say in how small a window can be
but it gets closer.

Resolves: #4836
2025-01-08 15:50:18 -07:00
Bryan Lee
5bfb3925ba Add fallback timer for empty window title 2025-01-09 06:30:47 +08:00
Bryan Lee
ea7c54d79d Simplify let binding in TerminalView title logic 2025-01-09 06:30:47 +08:00
Bryan Lee
0651586339 Reduce ghost emoji flash in title bar 2025-01-09 06:30:46 +08:00
Jeffrey C. Ollie
c33629aae5 gtk: clean up context menu creation & refresh 2025-01-08 16:01:31 -06:00
Mitchell Hashimoto
eb40cce45e build: requireZig cleanup 2025-01-08 13:49:54 -08:00
Mitchell Hashimoto
e14bc5b64e Add keybind action copy_url_to_clipboard (#4676)
## Description

This PR implements the `copy_url_to_clipboard` keybind action. This
action allows users to copy URLs directly to the clipboard without
needing to select them first.

### Features

- Works with both regex-matched URLs and OSC8 hyperlinks
- Copies only the URL portion, not the surrounding text
- Respects the `clipboard_trim_trailing_spaces` configuration
- Provides clear error feedback
- Follows the same patterns as other clipboard operations


https://github.com/user-attachments/assets/c9c82e3d-dfc5-4171-b367-d6799305d87f

Resolves https://github.com/ghostty-org/ghostty/issues/4633
2025-01-08 13:35:26 -08:00
Mitchell Hashimoto
90d1023783 correct default keybinding cmd+backspace for macOS (#4249)
This produces the following keybind, which I believe was intended.
> keybind = cmd+backspace=text:\x15

Matches the cmd+left and cmd+right which are a few lines up.

#3679 
#3646
2025-01-08 13:32:08 -08:00
Mitchell Hashimoto
d969a6b6b7 linux: add "Open in Ghostty" shortcut for nautilus (#4816) 2025-01-08 13:26:34 -08:00
Bryan Lee
5213edfa6c Add keybind action copy_url_to_clipboard 2025-01-08 13:22:33 -08:00
Mitchell Hashimoto
ef12d90b74 gtk: implement dropping files and strings (#4211)
This allows dropping files and strings onto Ghostty in the GTK apprt. If
you drop files onto Ghostty it will be pasted as a list of shell-escaped
paths separated by newlines. If you drop a string onto Ghostty it will
paste the string. Normal rules for pasting (bracketed pasts, unsafe
pastes) apply.
2025-01-08 13:12:09 -08:00
Mitchell Hashimoto
6f720a0b11 termio/exec: fix 100% CPU usage after wait-after-command process exits (#4171)
read(2) returning 0 means that the other end of the pipe/pty has been
closed (EOF), so there cannot be any more output to read on the pipe,
and
the io reader thread can just exit.

If exec.wait_after_command=false, the read thread's quit pipe is
immediately written to after the child process dies, so all is well.
However, if wait_after_command=true (which is the case when using
Ghostty
to run a .command/.sh file on macOS), the read thread keeps spinning,
causing persistent 100% CPU usage per exited process.

Fix it by exiting the reader thread on EOF.
2025-01-08 13:10:20 -08:00
Danny Lin
3e24e96af5 termio/exec: fix SIGPIPE crash when reader exits early
If the read thread has already exited, it will have closed the read end
of the quit pipe. Unless SIGPIPE is masked with signal(SIGPIPE, SIG_IGN),
or the macOS-specific fcntl(F_SETNOSIGPIPE), writing to the write end of
a broken pipe kills the writer with SIGPIPE instead of returning -EPIPE
as an error. This causes a crash if the read thread exits before
threadExit.

This was already a possible race condition if read() returns
error.NotOpenForReading or error.InputOutput, but it's now much easier to
trigger due to the recent "termio/exec: fix 100% CPU usage after
wait-after-command process exits" fix.

Fix this by closing the quit pipe instead of writing to it.
2025-01-08 13:06:14 -08:00
Mitchell Hashimoto
542c655348 Add close_tab keybinding action for macOS (#4395)
This PR adds support for a dedicated `close_tab` keybinding action,
allowing users to bind specific keys for closing tabs. The
implementation:

- Adds `close_tab` as a new keybinding action
- Preserves all existing confirmation dialogs for running processes
- Works seamlessly with macOS native tab system

### Testing
- [x] Tested with single tabs
- [x] Tested with multiple tabs
- [x] Tested with running processes (confirmation dialog)
- [x] Tested with splits within tabs

<img width="797" alt="image"
src="https://github.com/user-attachments/assets/8e09eea3-1f71-40a3-a835-76de14013a29"
/>


https://github.com/user-attachments/assets/155210f7-20fe-4a96-8800-6969df214871

Partially resolved #4331
2025-01-08 12:17:27 -08:00
Bryan Lee
140ac93884 Add close_tab keybinding action for macOS
Implement `close_tab` keybinding action to close the current tab and all splits within that tab.
2025-01-08 12:04:40 -08:00
Mitchell Hashimoto
e4033ca4df config: close_tab on macOS should be cmd+opt+w to match iTerm2 2025-01-08 11:47:37 -08:00
Mitchell Hashimoto
0d679951bc fix (macOS): Show quick terminal on full-screen app (#4049)
closes #2721  

This PR resolves the issue where the Quick Terminal was not visible when
pressing the global keybind while a full-screen app was active.

### Changes  
- Added new configuration options for `quick-terminal-space-behavior`
- The Quick Terminal will now overlay properly on top of full-screen
applications

#### Behavior

##### `quick-terminal-space-behavior = remain`

- The Quick Terminal will be remain open on the space when switching
spaces.

##### `quick-terminal-space-behavior = move`

- The Quick Terminal will be moved to active space when switching
spaces.
2025-01-08 11:43:58 -08:00
Mitchell Hashimoto
6ebc02b68d macos: animate in even if remain on another space 2025-01-08 11:30:26 -08:00
Mitchell Hashimoto
6e54589db4 misc cleanups 2025-01-08 11:28:09 -08:00
Jeffrey C. Ollie
c85c277415 core: add docs for ShellEscapeWriter 2025-01-08 13:19:00 -06:00
Sabarigirish Manikandan
306c7ea2be close_tab keybind (gtk apprt only) (#4033)
Title. Adds a close_tab keybind that essentially behaves the exact same
as clicking the tab close button on the tab bar.
2025-01-08 19:07:00 +00:00
Soh Satoh
37db4578c8 Fix the issue that the quick term not shown on first call 2025-01-08 11:03:18 -08:00
Soh Satoh
0ddc1a21a6 fix the comment (quick-terminal-space-behavior) 2025-01-08 11:03:18 -08:00
Soh Satoh
7bb3c31cee Move the quick terminal to active space if toggle() called while opening on another space 2025-01-08 11:03:18 -08:00
Soh Satoh
1493c55348 Merge branch 'main' into show-quick-term-on-another-app 2025-01-08 11:03:18 -08:00
Soh Satoh
e2523c25cb Add quick-terminal-space-behavior option 2025-01-08 11:02:19 -08:00
Soh Satoh
2206c509be Show quick terminal on another full-screen app 2025-01-08 10:59:56 -08:00
Mitchell Hashimoto
34a0b206f8 apprt/gtk: use a subtitle to mark the current working directory (#3570)
If the title is already the current working directory, hide the
subtitle. Otherwise show the current working directory, like if a
command is running for instance.

This is a re-opening of my original PR because I had to delete my fork
and re-fork it.
2025-01-08 10:10:19 -08:00
Mitchell Hashimoto
051fed0c24 chore: replace adwaita-1 to libadwaita-1 for better pkg-config handling (#4818)
The reasoning for this PR is discussed at
https://github.com/ghostty-org/ghostty/discussions/3667

But in short, `pkg-config` queries `libadwaita-1` successfully, but not
for `adwaita-1`, hence renaming it would result in better integration
with pkg-config.
2025-01-08 09:42:10 -08:00
Mitchell Hashimoto
ef833b3861 Implement "Paste Selection" on macOS like Terminal.app (#4733)
As discussed in #2670 and #2722

- This uses an NSPasteboard with the name
`com.mitchellh.ghostty.selection` as a dedicated 'selection' clipboard
- Sets `supports_selection_clipboard` to true for macOS
- Sets the default `copy-on-select` config to `.true` for macOS
- Adds a "Paste Selection" menu item and default cmd+shift+v key binding
for macOS (to match Terminal.app)
2025-01-08 09:39:21 -08:00
Kwee Lung Sin
40442ac02f chore: replace adwaita-1 to libadwaita-1 for better pkg-config handling 2025-01-08 09:28:42 -08:00
Wes Campaigne
e86b9a112e Implement "Paste Selection" on macOS like Terminal.app 2025-01-08 09:26:13 -08:00
Jeffrey C. Ollie
e7c71df0b7 gtk: implement dropping files and strings 2025-01-08 08:39:28 -06:00
Gabriele Musco
c972051611 linux: add "Open in Ghostty" shortcut for nautilus 2025-01-08 13:49:32 +01:00
Mitchell Hashimoto
29c2f095a6 macOS: Add Bluetooth permission description. (#4668)
Adds the missing Bluetooth permission to Ghostty's application playlist
manifest file, as it was missing. Tweaks have also been made to existing
permission descriptions to be more readable. Should fix an abrupt crash
in Fastfetch and any other apps that may request Bluetooth permissions.
2025-01-07 20:53:12 -08:00
Mitchell Hashimoto
3731b099bb apprt/gtk: Move most version checks to runtime (#4783)
### apprt/gtk: Add version.runtimeAtLeast

This will be used for version checks that are independent of the version
of GTK we built against.

### apprt/gtk: Move most version checks to runtime

Unless we are guarding against symbols added in new versions we now
check against the runtime version of GTK to handle them even when we
didn't build against that version.
2025-01-07 20:48:25 -08:00
Mitchell Hashimoto
6e42cb152e build.zig Improvements (#4797)
This is a major refactor of `build.zig` aimed at improving
maintainability while also fixing some bugs.

The major idea behind the refactor is to split the `build.zig` file up
into distinct `src/build/*.zig` files. By doing so, we can improve
readability of the primary `build.zig` while also enabling better reuse
of steps. Our `build.zig` is now less than 150 lines of code (of course,
it calls into a lot more lines but they're neatly organized now).

Ignore the commit messages I'm going to squash this whole thing.

Improvements:

  * `build.zig` is less than 150 lines of readable code.
* Help strings and unicode table generators are only run once when
multiple artifacts are built since the results are the same regardless
of target.
* Metal lib is only built once per architecture (rather than once per
artifact)
* Resources (shell integration, terminfo, etc.) and docs are only
built/installed for artifacts that need them
  * Wayland protocols no longer required for source build

Breaking changes:

  * Removed broken wasm build (@gabydd will re-add)
* Removed conformance files, shell scripts are better and we don't run
these anymore
* Removed macOS app bundle creation, we don't use this anymore since we
use Xcode

## Extra: Some History

Our `build.zig` hasn't been significantly refactored since the project
started, when Zig was _version 0.10_. Since then, the build system has
changed significantly. We've only ever duct taped the `build.zig` as we
needed to support new Zig versions, new features, etc. It was a mess.

The major improvement is adapting the entire Ghostty `build.zig` to the
Step and LazyPath changes introduced way back in Zig 0.12. This lets us
better take advantage of parallelism and the dependency graph so that
steps are only executed as they're needed.

As such, you can see in the build.zig that we initialize a lot of
things, but unless a final target (i.e. install, run) references those
steps, _they'll never be executed_. This lets us clean up a lot.
2025-01-07 20:45:14 -08:00
Mitchell Hashimoto
2f6bef7ff2 bash: drop automatic shell integration with --posix (#4785)
'--posix' starts bash in POSIX mode (like /bin/sh). This is rarely used
for interactive shells, and removing automatic shell integration support
for this option allows us to simply/remove some exceptional code paths.

Users are still able to manually source the shell integration script.

Also fix an issue where we would still inject GHOSTTY_BASH_RCFILE if we
aborted the automatic shell integration path _after_ seeing an --rcfile
or --init-file argument.
2025-01-07 20:45:04 -08:00
Mitchell Hashimoto
300884d50f refactor(font): move Metrics from Face to Collection (#4739)
This refactor enables two very significant improvements to our font
handling, which I will be implementing next:
1. Automatically adjust size of fallback faces to better align with the
primary face, a la CSS
[`font-size-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size-adjust).
[^1]
2. Move glyph resizing/positioning out of GPU-land and in to
`renderGlyph` and apply alignment/resizing rules from the nerd fonts
patcher[^2] to the glyphs at rasterization time, so that we can ensure
exact cell fits and swap out our embedded JB Mono with an unpatched
version with a separate dedicated symbols-only nerd font.

In addition to being necessary prep work for those two changes, this PR
is also a minor but real stand-alone improvement. By only computing the
cell metrics for our primary font, we avoid a *lot* of wasted work when
loading fallback fonts, and also avoid that being a source of load
errors, which we don't yet handle gracefully[^3].

To validate this PR I've run the full set of font backend tests locally
on my Mac with no failures, and did a sanity check of running Ghostty
with both renderers and with CoreText and FreeType font backends and
then `cat`ing a file that requires fallback fonts to render, and
everything looks correct.

[^1]: #3029

[^2]: The alignment and resizing rules for the nerd font symbols are
defined in the patcher
[here](6d0b8ba05a/font-patcher (L866-L1151))

[^3]: #2991
2025-01-07 20:43:48 -08:00
Mitchell Hashimoto
c83733c533 apprt/gtk: continue cleanup of window-decoration code (#4465)
Remove all window corner artifacting. Now we also respond to changes
when the window becomes decorated or not.
2025-01-07 20:38:27 -08:00
Mitchell Hashimoto
0bb0bf9b85 core: improve desktop environment detection (#4697) 2025-01-07 20:35:57 -08:00
Mitchell Hashimoto
d78eb7d28f apprt/gtk: fix window colors when window-theme=ghostty (#4745)
Before:

![image](https://github.com/user-attachments/assets/53819255-40a6-41f3-bdce-0e37c69b4d77)

After:

![image](https://github.com/user-attachments/assets/e2041832-7dd4-4adc-b0b2-093b4d18ed9b)
2025-01-07 20:35:18 -08:00
Mitchell Hashimoto
e00e991080 bash: set the title command in preexec (#4775)
PS0 is evaluated after a command is read but before it is executed. The
'preexec' hook (from bash-preexec) is equivalent for our title-updating
purposes and conveniently provides the current command as an argument
(from its own `history 1` call).
2025-01-07 20:34:56 -08:00
Mitchell Hashimoto
4eaf346090 renderer: respect reverse with cursor-invert-fg-bg (#4777)
Resolves: https://github.com/ghostty-org/ghostty/issues/4771
2025-01-07 20:34:32 -08:00
Mitchell Hashimoto
bade7be021 Use build.zig.zon for Wayland protocols 2025-01-07 20:26:26 -08:00
Mitchell Hashimoto
8bf5c4ed7f This is a major refactor of build.zig.
The major idea behind the refactor is to split the `build.zig` file up into
distinct `src/build/*.zig` files. By doing so, we can improve readability of
the primary `build.zig` while also enabling better reuse of steps. Our
`build.zig` is now less than 150 lines of code (of course, it calls into a lot
more lines but they're neatly organized now).

Improvements:

  * `build.zig` is less than 150 lines of readable code.
  * Help strings and unicode table generators are only run once when multiple
    artifacts are built since the results are the same regardless of target.
  * Metal lib is only built once per architecture (rather than once per artifact)
  * Resources (shell integration, terminfo, etc.) and docs are only
    built/installed for artifacts that need them

Breaking changes:

  * Removed broken wasm build (@gabydd will re-add)
  * Removed conformance files, shell scripts are better and we don't run
    these anymore
  * Removed macOS app bundle creation, we don't use this anymore since we
    use Xcode

## Some History

Our `build.zig` hasn't been significantly refactored since the project started,
when Zig was _version 0.10_. Since then, the build system has changed
significantly. We've only ever duct taped the `build.zig` as we needed to
support new Zig versions, new features, etc. It was a mess.

The major improvement is adapting the entire Ghostty `build.zig` to the Step
and LazyPath changes introduced way back in Zig 0.12. This lets us better take
advantage of parallelism and the dependency graph so that steps are only
executed as they're needed.

As such, you can see in the build.zig that we initialize a lot of things, but
unless a final target (i.e. install, run) references those steps, _they'll
never be executed_. This lets us clean up a lot.
2025-01-07 19:47:43 -08:00
Jon Parise
1b91a667fb bash: drop automatic shell integration with --posix
'--posix' starts bash in POSIX mode (like /bin/sh). This is rarely used
for interactive shells, and removing automatic shell integration support
for this option allows us to simply/remove some exceptional code paths.

Users are still able to manually source the shell integration script.

Also fix an issue where we would still inject GHOSTTY_BASH_RCFILE if we
aborted the automatic shell integration path _after_ seeing an --rcfile
or --init-file argument.
2025-01-07 16:09:03 -05:00
sin-ack
093bdf640a apprt/gtk: Move most version checks to runtime
Unless we are guarding against symbols added in new versions we now
check against the runtime version of GTK to handle them even when we
didn't build against that version.
2025-01-07 20:01:19 +00:00
sin-ack
a115e848c6 apprt/gtk: Add version.runtimeAtLeast
This will be used for version checks that are independent of the version
of GTK we built against.
2025-01-07 20:01:19 +00:00
Gregory Anders
b2716375ac renderer: respect reverse with cursor-invert-fg-bg 2025-01-07 12:01:45 -06:00
Mitchell Hashimoto
0065aae6b6 Add IPv6 URL pattern support with comprehensive test cases (#4749)
## Description

This PR adds support for IPv6 URL detection in terminal output,
addressing issue #4743. The implementation enhances URL detection to
properly handle IPv6 addresses in URLs, including various formats and
use cases.

## Changes

- Added dedicated IPv6 URL pattern matching

- Integrated IPv6 pattern with existing URL regex

- Added comprehensive test suite for IPv6 URL scenarios

## Test Cases
The implementation includes test cases for:
- Basic IPv6 URLs (e.g., `http://[::]:8000/`)
- Full IPv6 addresses with ports
- Compressed IPv6 forms
- URLs with paths and query parameters
- Special cases (link-local, multicast)
- Context-specific scenarios (markdown)

## Related Issues

Resolves #4743
2025-01-07 08:59:44 -08:00
Mitchell Hashimoto
9b21de2fe7 bash: add license declaration for kitty-derived code (#4776) 2025-01-07 08:51:24 -08:00
Jon Parise
41201068ef bash: add license declaration for kitty-derived code 2025-01-07 11:34:39 -05:00
Jon Parise
bf3597a519 bash: set the title command in preexec
PS0 is evaluated after a command is read but before it is executed. The
'preexec' hook (from bash-preexec) is equivalent for our title-updating
purposes and conveniently provides the current command as an argument
(from its own `history 1` call).
2025-01-07 11:20:12 -05:00
Bryan Lee
c8d5b2da45 Add IPv6 URL pattern support with comprehensive test cases
- Add IPv6 URL pattern matching to support URLs like http://[::]:8000/
- Separate IPv6 URL pattern from main regex for better maintainability
- Add extensive test cases covering:
  - Basic IPv6 URLs with ports
  - URLs with paths and query parameters
  - Compressed IPv6 forms
  - Link-local and multicast addresses
  - Mixed scenarios and markdown contexts
2025-01-07 14:54:16 +08:00
Tristan Partin
a52f469e16 apprt/gtk: fix window colors when window-theme=ghostty
Signed-off-by: Tristan Partin <tristan@partin.io>
2025-01-06 22:56:41 -06:00
Qwerasd
298aeb7536 refactor(font): move ownership of Metrics to Collection
This sets the stage for dynamically adjusting the sizes of fallback
fonts based on the primary font's face metrics. It also removes a lot of
unnecessary work when loading fallback fonts, since we only actually use
the metrics based on the parimary font.
2025-01-06 20:13:45 -05:00
Qwerasd
540fcc0b69 refactor(font): move Metrics out of face
in preparation to move ownership of metrics from faces to collections
2025-01-06 20:13:45 -05:00
Mitchell Hashimoto
a3837a1e4e termio: don't leak VTE_VERSION into child processes (#4710)
This variable is set by gnome-terminal and other VTE-based terminals. We
don't want our child processes to think we're running under VTE.

See #4494
2025-01-06 16:38:40 -08:00
Jon Parise
29dd5ae605 termio: explain why we're removing VTE_VERSION 2025-01-06 19:11:54 -05:00
Mitchell Hashimoto
0974705dd9 Fix minimum initial window size (#4688)
Change the calculation of minimum initial window size so it agrees with
the documented 10x4 cells instead of 640x480 px.

Resolves: #4655
2025-01-06 16:03:13 -08:00
Mitchell Hashimoto
1cf1b886cd bash: narrow the scope of GHOSTTY_BASH_ENV (#4690)
GHOSTTY_BASH_ENV is only set in the '--posix' path. This change is a
code organization improvement and doesn't change the script's behavior.
2025-01-06 16:01:41 -08:00
Mitchell Hashimoto
262c76eace feat: open files without file:// protocol (#4713)
**Overview**: add support for file paths starts with `../` `./` and `/`

To implement this I extended the existing regex variable in the
`src/config` dir. In a few test cases, extra space is added
intentionally to verify we don't include space in the URL as it looks &
feels odd.

Here is the text file content used for testing
```console
➜  ~ cat test.txt
https://google.com
file:///Users/ABC/oss/ghostty/build.zig
file:///Users/ABC/oss/ghostty/debug.sh


/Users/ABC/oss/ghostty/src/../debug.sh
../../Applications/Zed.app/Contents/Info.plist
[link](/home/user/ghostty.user/example) -- non-existent
[link](/Users/ABC/oss/ghostty/src/../debug.sh)
first time ../../Applications/Zed.app/Contents/Info.plist contributor
➜  ~
```

Here is the screen recording of the changes.

[![Screencast](https://img.youtube.com/vi/Q8qdwdBVbWk/0.jpg)](https://www.youtube.com/watch?v=Q8qdwdBVbWk)
2025-01-06 16:00:40 -08:00
Mitchell Hashimoto
9732a92d7a terminal: ConEmu OSC9 parsing is more robust and correct (#4727)
Related to #4485

This commit matches ConEmu's parsing logic[^1] more faithfully. For any
substate that requires a progress, ConEmu parses so long as there is a
number and then just ignores the rest.

For substates that don't require a progress, ConEmu literally ignores
everything after the state.

Tests cover both.

[^1]:
740b09c363/src/ConEmuCD/ConAnsiImpl.cpp (L2264)
2025-01-06 15:56:32 -08:00
Tristan Partin
c71da8338b apprt/gtk: continue cleanup of window-decoration code
Remove all window corner artifacting. Now we also respond to changes
when the window becomes decorated or not.

Signed-off-by: Tristan Partin <tristan@partin.io>
2025-01-06 17:44:05 -06:00
Mitchell Hashimoto
7ae94e145d terminal: ConEmu OSC9 parsing is more robust and correct
Related to #4485

This commit matches ConEmu's parsing logic[^1] more faithfully. For any
substate that requires a progress, ConEmu parses so long as there is a
number and then just ignores the rest.

For substates that don't require a progress, ConEmu literally ignores
everything after the state.

Tests cover both.

[^1]: 740b09c363/src/ConEmuCD/ConAnsiImpl.cpp (L2264)
2025-01-06 15:41:44 -08:00
Mustaque Ahmed
85743aebd5 feat: add support for file paths starts with ../ ./ and /
To implement this I extended the existing regex variable in the `src/config` dir. In a few test cases, extra space is added intentionally to verify we don't include space in the URL as it looks & feels odd.
2025-01-07 01:13:29 +05:30
Jon Parise
15f82858b7 termio: don't leak VTE_VERSION into child processes
This variable is used by gnome-terminal and other VTE-based terminals.
We don't want our child processes to think we're running under VTE.
2025-01-06 14:00:38 -05:00
Mitchell Hashimoto
037de64ea2 config: unbind keybind triggers unbinds both translated and physical (#4705)
Fixes #4703

This changes `unbind` so it always removes all keybinds with the given
trigger pattern regardless of if it is translated or physical.

The previous behavior was technically correct, but this implements the
pattern of least surprise. I can't think of a scenario where you really
want to be exact about what key you're unbinding. And if that scenario
does exist, you can always fix it by rebinding after unbind.
2025-01-06 10:06:21 -08:00
Mitchell Hashimoto
359c390218 config: unbind keybind triggers unbinds both translated and physical
Fixes #4703

This changes `unbind` so it always removes all keybinds with the given
trigger pattern regardless of if it is translated or physical.

The previous behavior was technically correct, but this implements the pattern
of least surprise. I can't think of a scenario where you really want to
be exact about what key you're unbinding. And if that scenario does
exist, you can always fix it by rebinding after unbind.
2025-01-06 09:47:21 -08:00
Mitchell Hashimoto
0929500360 Set the paste button as default in the GTK paste confirmation dialog (#4225)
A simple change to make it so that, in the GTK4 paste confirmation
dialog, the user can just hit enter/space to confirm the paste.

After some playing around, it seems as though GTK4 needs you to set the
focus on a widget after the entire view has been configured, meaning
that we have to create more references to pass the `confirm_button`
GtkWidget up and up. Not 100% certain if this is the best way to do so,
but:

1. Add the `ButtonsView` to the `PrimaryView` struct (as
`PrimaryView.buttons`)
2. Add the `confirm_button` to the `ButtonsView` struct (as
`ButtonsView.confirm_button`)
3. Call `c.gtk_widget_grab_focus` on (the now-accessible)
`view.buttons.confirm_button`

This seems to work as expected, but I'm not sure if:

1. We should also make `cancel_button` available?
2. There's a better way to expose `confirm_button` to `PrimaryView`?
3. I did a good Zig?

I've never written (or read) Zig code before today so I hope this makes
sense. Feedback welcome!
2025-01-06 09:43:18 -08:00
Daniel Fox
d79a02db44 Add destructive/suggested action classes 2025-01-06 09:25:42 -08:00
Daniel Fox
4fb253a300 Expose clipboard cancel button and focus it 2025-01-06 09:06:10 -08:00
Daniel Fox
d3973b8fad Set the paste button in the GTK dialog as default 2025-01-06 09:06:10 -08:00
Jeffrey C. Ollie
6d90a181ce core: improve desktop environment detection 2025-01-06 10:38:07 -06:00
Mitchell Hashimoto
3461204741 iOS: Fix crash on device (#4684)
Currently causes a crash on launch in iOS devices (not Simulators) since
[isHeadless](https://developer.apple.com/documentation/metal/mtldevice/1433377-isheadless),
[isRemovable](https://developer.apple.com/documentation/metal/mtldevice/2889851-isremovable)
and
[isLowPower](https://developer.apple.com/documentation/metal/mtldevice/1433409-islowpower)
are only available on macOS (raises `[AGXG16GDevice isHeadless]:
unrecognized selector sent to instance ... `).

The [preferred
method](https://developer.apple.com/documentation/metal/gpu_devices_and_work_submission/getting_the_default_gpu)
in iOS is to access the single GPU device by attempting to create a
default one.

Fixes #4685.
2025-01-06 07:31:21 -08:00
Mitchell Hashimoto
4838bcbb8f macos: halt NSEvent processing at app scope only if event is handled (#4691)
Fixes #4677
2025-01-06 07:28:04 -08:00
Mitchell Hashimoto
ae0c4d927a macos: halt NSEvent processing at app scope only if event is handled
Fixes #4677
2025-01-06 07:27:44 -08:00
Mitchell Hashimoto
b04cb2585d macos: fix retain cycle preventing window from freeing (#4689) 2025-01-06 07:17:48 -08:00
Jon Parise
237c941395 bash: narrow the scope of GHOSTTY_BASH_ENV
GHOSTTY_BASH_ENV is only set in the '--posix' path. This change is a
code organization improvement and doesn't change the script's behavior.
2025-01-06 10:02:24 -05:00
Mitchell Hashimoto
f0c2d3d75a macos: fix retain cycle preventing window from freeing 2025-01-06 07:02:04 -08:00
Evelyn Harthbrooke
dc4774c147 macOS: fixup Photo Library description to make more sense. 2025-01-06 07:13:53 -07:00
George Joseph
c127daa552 Fix minimum initial window size
Change the calculation of minimum initial window size so it agrees with
the documented 10x4 cells instead of 640x480 px.

Resolves: #4655
2025-01-06 07:13:51 -07:00
Mitchell Hashimoto
d0b06bd55f don't error if gtk4 pkg-config configuration could not be found (#4683)
this prevents issues with people running `zig build --help` without gtk4
development stuff installed

same as #4546
2025-01-06 06:12:36 -08:00
Evelyn Harthbrooke
ae0248b5bc macOS: Add Bluetooth permission description; fixup other descs.
Adds the missing Bluetooth permission description to ghostty's Xcode
project description, and fixes up existing permissions to be clearer.

Closes #3995 and #4512.
2025-01-06 07:10:50 -07:00
Tristan Partin
3698b37588 apprt/gtk: use a subtitle to mark the current working directory
If the title is already the current working directory, hide the
subtitle. Otherwise show the current working directory, like if
a command is running for instance.

Signed-off-by: Tristan Partin <tristan@partin.io>
2025-01-06 08:05:24 -06:00
yonihemi
63a47d0ba5 iOS: Fix crash on device 2025-01-06 22:01:41 +08:00
Jan200101
781159af7d don't error if gtk4 pkg-config configuration could not be found
this prevents issues with people running `zig build --help` without gtk4 installed

same as #4546
2025-01-06 14:53:57 +01:00
Mitchell Hashimoto
6181487bad vim compiler plugin for ghostty filetype (#4645)
Basically integrates `ghostty +validate-config` with vim's compiler
feature. This allows validating the config from vim and navigating to
errors e.g. with the quickfix list.
2025-01-05 18:52:59 -08:00
Mitchell Hashimoto
53468541f7 Use \w instead of $PWD for title bar (#4656)
The `PS1=` variable in the bash integration for the title bar could use
[`\w`](https://www.man7.org/linux/man-pages//man1/bash.1.html#PROMPTING)
rather than `$PWD` to indicate the working directory.

The effect is to replace any leading instance of `$HOME` with a tilde,
so `/Users/patrick/code` becomes `~/code`. It also respects
[`$PROMPT_DIRTRIM`](https://www.man7.org/linux/man-pages//man1/bash.1.html#PARAMETERS),
if any. It looks cleaner to me, especially if there are multiple tabs
open.

Implements #4643 (cc discussion #4601).
2025-01-05 18:51:23 -08:00
Beau McCartney
94bf448eda just reset makeprg and errorformat 2025-01-05 17:08:52 -07:00
Mitchell Hashimoto
2485482aec config: store non-reproducible diagnostics in replay steps (#4652)
Fixes #4509

Our config has a replay system so that we can make changes and reproduce
the configuration as if we were reloading all the files. This is useful
because it lets us "reload" the config under various conditions (system
theme change, etc.) without risking failures due to world state changing
(i.e. config files change or disappear).

The replay system assumed that all diagnostics were reproducible, but
this is not the case. For example, we don't reload `config-file` so we
can't reproduce diagnostics that come from it.

This commit adds a new `diagnostic` replay step that can be used to
store non-reproducible diagnostics and `config-file` is updated to use
it.
2025-01-05 14:14:20 -08:00
Mitchell Hashimoto
f6d85baadb config: store non-reproducible diagnostics in replay steps
Fixes #4509

Our config has a replay system so that we can make changes and reproduce
the configuration as if we were reloading all the files. This is useful
because it lets us "reload" the config under various conditions (system
theme change, etc.) without risking failures due to world state changing
(i.e. config files change or disappear).

The replay system assumed that all diagnostics were reproducible, but
this is not the case. For example, we don't reload `config-file` so we
can't reproduce diagnostics that come from it.

This commit adds a new `diagnostic` replay step that can be used to
store non-reproducible diagnostics and `config-file` is updated to use
it.
2025-01-05 13:59:38 -08:00
Mitchell Hashimoto
c9c5ad43a5 macos: ignore modifier changes while IM is active (#4649)
Fixes #4634
2025-01-05 13:46:11 -08:00
Mitchell Hashimoto
ed221f32fe macos: ignore modifier changes while IM is active
Fixes #4634
2025-01-05 13:30:50 -08:00
Mitchell Hashimoto
143c01edcb feat: parse ConEmu OSC9;1 (#4327)
# Description

This PR implements support for the [ConEmu OSC9;1 escape
sequence](https://conemu.github.io/en/AnsiEscapeCodes.html#OSC_Operating_system_commands).

Based on my understanding of [ConEmu's source
code](740b09c363/src/ConEmuCD/ConAnsiImpl.cpp (L705-L724)):
- The default timeout is set to `100` milliseconds if no value is
specified.
- The timeout value is clamped to a maximum of `10000` milliseconds.

#3125
2025-01-05 13:25:41 -08:00
Damien Mehala
ead241f38c Merge branch 'main' into dmehala/conemu-osc9 2025-01-05 21:58:45 +01:00
Mitchell Hashimoto
e3c94210f2 docs: improve terminal page list documentation (#4648)
This PR enhances clarity and comprehension for the terminal page list
documentation.
2025-01-05 12:53:55 -08:00
Mitchell Hashimoto
f14c0f5a63 gtk(wayland): add support for background blur on KDE Plasma (#4403)
Also establishes a foundation for Wayland support and fixes a minor bug
(GTK windows remaining opaque when `background-opacity` is set to 1 on
startup and later updated to less than 1 with a config reload)

Can't update the Zig cache hash myself since I'm currently in China and
my proxy's broken for some reason :(

See also #4361, part of #4626
2025-01-05 12:53:39 -08:00
Mitchell Hashimoto
62dd468500 font(coretext): add config to adjust strength of font-thicken. (#4531)
This is achieved by rendering to an alpha-only context rather than a
normal single-channel context, and adjusting the brightness at which
CoreText thinks it's drawing the glyph, which affects how it applies
font smoothing (which is what `font-thicken` enables).
2025-01-05 12:53:14 -08:00
Alexandre Antonio Juca
057b196024 docs: improve terminal page list documentation 2025-01-05 21:50:20 +01:00
Mitchell Hashimoto
2fbe680aed config: fix tests 2025-01-05 12:38:24 -08:00
Mitchell Hashimoto
ce77b91bf6 nix fmt 2025-01-05 12:37:56 -08:00
Mitchell Hashimoto
bb83a14d7a config: minor config changes 2025-01-05 12:35:50 -08:00
Mitchell Hashimoto
68624e6c45 parse ConEmu OSC9;3 (#4644)
This PR implements support for the [ConEmu OSC9;3 escape
sequence](https://conemu.github.io/en/AnsiEscapeCodes.html#OSC_Operating_system_commands).

| Sequence | Description |
| - | - |
ESC ] 9 ; 3 ; ”txt“ ST | Change ConEmu Tab to txt. Set empty string to
return original Tab text

#3125
2025-01-05 12:35:43 -08:00
Mitchell Hashimoto
0ae8d9ed42 nix: update hash 2025-01-05 12:30:16 -08:00
Leah Amelia Chen
f2c357a209 config: allow booleans for background-blur-radius 2025-01-05 12:27:11 -08:00
Leah Amelia Chen
cd90821b93 fix(gtk): adjust background CSS class dynamically on config reload
Currently the `background` CSS class is added once on startup and never removed
or re-added. This is problematic as that if Ghostty was started with an opaque
window but then its config was reloaded with a `background-opacity` less than 1,
the window won't actually become translucent, and it would only appear as if the
background colors had become faded (because the window is still styled to be
opaque).
2025-01-05 12:27:11 -08:00
Leah Amelia Chen
9184395cba gtk(wayland): add support for background blur on KDE Plasma 2025-01-05 12:27:11 -08:00
Leah Amelia Chen
31439f311d build: add wayland 2025-01-05 12:27:11 -08:00
Beau McCartney
8d7e57f64b vim compiler plugin for ghostty filetype - validates config
`:make` will call `ghostty +validate-config` and populate the quickfix
list with the errors that can be navigated to (e.g. with `:cnext`)

`:h write-compiler-plugin`, and neovim's built in ftplugin/ and
compiler/ plugins were used as references
2025-01-05 13:06:32 -07:00
Mitchell Hashimoto
f4a9b65f78 gtk: fix copy keybinds (#4635)
- **gtk: send copy_to_clipboard toast from Surface**

    Move the toast we send when copying to the clipboard to the Surface
implementation. Previously, we only called this from the gtk accelerator
callback which we only call when the *last set* keybind is activated.

We also only send a toast if we have copied to the standard clipboard,
    as opposed to the selection clipboard. By default, we have
copy-to-clipboard true for linux, which sets the selection keyboard on
    any select. This becomes *very* noisy.

- **config: rearrange default copy_to_clipboard keybinds**

    Move the newly added *+insert keybinds to before the ctrl+shift+*
    keybinds. This is needed to have the ctrl+shift keybinds be the ones
    that show up in the menu.
2025-01-05 07:56:49 -08:00
Tim Culverhouse
9cf9e0639f config: rearrange default copy_to_clipboard keybinds
Move the newly added *+insert keybinds to before the ctrl+shift+*
keybinds. This is needed to have the ctrl+shift keybinds be the ones
that show up in the menu.
2025-01-05 09:37:47 -06:00
Tim Culverhouse
4d4b785a58 gtk: send copy_to_clipboard toast from Surface
Move the toast we send when copying to the clipboard to the Surface
implementation. Previously, we only called this from the gtk accelerator
callback which we only call when the *last set* keybind is activated.

We also only send a toast if we have copied to the standard clipboard,
as opposed to the selection clipboard. By default, we have
copy-to-clipboard true for linux, which sets the selection keyboard on
any select. This becomes *very* noisy.
2025-01-05 09:25:47 -06:00
Mitchell Hashimoto
7a27af8bfc macos: detect IME input source change as part of keyDown event (#4609)
Fixes #4539

AquaSKK is a Japanese IME (Input Method Editor) for macOS. It uses
keyboard inputs to switch between input modes. I don't know any other
IMEs that do this, but it's possible that there are others. Prior to
this change, the keyboard inputs to switch between input modes were
being sent to the terminal, resulting in erroneous characters being
written.

This change adds a check during keyDown events to see if the input
source changed _during the event_. If it did, we assume an IME captured
it and we don't pass the event to the terminal.

This makes AquaSKK functional in Ghostty.
2025-01-04 22:11:31 -08:00
Mitchell Hashimoto
4ffd281de3 macos: detect IME input source change as part of keyDown event
Fixes #4539

AquaSKK is a Japanese IME (Input Method Editor) for macOS. It uses
keyboard inputs to switch between input modes. I don't know any other
IMEs that do this, but it's possible that there are others. Prior to
this change, the keyboard inputs to switch between input modes were
being sent to the terminal, resulting in erroneous characters being
written.

This change adds a check during keyDown events to see if the input
source changed _during the event_. If it did, we assume an IME captured
it and we don't pass the event to the terminal.

This makes AquaSKK functional in Ghostty.
2025-01-04 21:45:25 -08:00
Mitchell Hashimoto
5d523116bf macos: alphabetize resources in xcode project (#4607) 2025-01-04 20:45:35 -08:00
Mitchell Hashimoto
8f5cbed46f fix: macos incorrect quick terminal position (#4501)
As this discussion: #4353
2025-01-04 20:41:53 -08:00
Mitchell Hashimoto
948cbfbf0e gtk: enable window-title-font-family (#4560) 2025-01-04 20:35:44 -08:00
Mitchell Hashimoto
0063dc3925 Implement configuration option split-divider-color for macOS (#4595)
Addresses #4326 for macOS only, should be easy to combine PR #4593 doing
the same for GTK
2025-01-04 20:34:19 -08:00
Mitchell Hashimoto
e05c3b6fd7 macos: alphabetize resources in xcode project 2025-01-04 20:30:42 -08:00
Jeffrey C. Ollie
51c42795fc gtk: enable window-title-font-family 2025-01-04 20:20:52 -08:00
Christian Schneider
a670836d7a Remove outdated comment 2025-01-04 20:18:00 -08:00
Christian Schneider
da80531c22 Implement configuration option split-divider-color for macOS 2025-01-04 20:18:00 -08:00
Mitchell Hashimoto
0df4012edc gtk: add split-separator-color config (#4593)
Fixes #4326


![image](https://github.com/user-attachments/assets/eaf5280b-be08-4927-9536-19b96d206ad6)
2025-01-04 20:16:51 -08:00
Mitchell Hashimoto
d936e7106a Update iTerm2 colorschemes (#4602)
Upstream revision:
4762ad5bd6
2025-01-04 20:15:27 -08:00
mitchellh
6db39e827e deps: Update iTerm2 color schemes 2025-01-05 01:00:16 +00:00
Patrick Reynolds
5fa9e88482 Use \w instead of $PWD for title bar 2025-01-04 19:26:14 -05:00
Jeffrey C. Ollie
f3cb95ac1f gtk: add split-separator-color config
Fixes #4326 for GTK
2025-01-04 16:56:52 -06:00
Mitchell Hashimoto
0306c592a7 Feat: display memory size in Terminal Inspector using bytes and kibibytes (#4549)
This PR ensures that we can view all memory-related information in the
Terminal Inspector in bytes and kibibytes for improved readability at a
glance.

<img width="561" alt="Screenshot 2025-01-03 at 23 32 07"
src="https://github.com/user-attachments/assets/b1075a0e-f8e1-44e8-8386-8462e35e2c46"
/>
2025-01-04 14:55:29 -08:00
Alexandre Antonio Juca
62fae29395 chore: rename file 2025-01-04 23:37:54 +01:00
Mitchell Hashimoto
f5f30605a8 feat: parse ConEmu OSC9;2 (#4447)
This PR implements support for the [ConEmu OSC9;2 escape
sequence](https://conemu.github.io/en/AnsiEscapeCodes.html#OSC_Operating_system_commands).

| Sequence | Description |
| - | - |
ESC ] 9 ; 2 ; ”txt“ ST | Show GUI MessageBox ( txt ) for any purposes.
2025-01-04 14:37:01 -08:00
Mitchell Hashimoto
9f9248fd28 Ensure all search results are visible in theme list (#4473)
## Changes

- Add a threshold to determine when to reset window position

- Reset window position to show all results from the top when result set
is small

- Maintain scroll position for larger result sets to preserve navigation
context


https://github.com/user-attachments/assets/826a2411-9b31-4adb-b1b4-f55b05aa911d

Resolves https://github.com/ghostty-org/ghostty/discussions/4472
2025-01-04 14:34:35 -08:00
Mitchell Hashimoto
23b0f7dec0 don't build freetype2 when system integration is enabled (#4543)
yet another follow up to #4534

some notes:
- different parts of the build system link against freetype2 or freetype
with freetype2 being the name for the pkg-config file. Because of the
include path in freetype-zig.h the pkg-config is needed otherwise it
would be unable to find the headers. The change isn't technically needed
for the harfbuzz and fontconfig modules however I think its best to keep
them all consistent since otherwise it might cause build errors in non
standard setups
- looking back, I initially modelled buildLib after the build function
and kept the pub, none of them need to be public so I've gone ahead and
removed all of that

test logic was kept just as they were before with a setup exact like it
was done for oniguruma

the main program and the testsall seem to work just fine both with and
without system integration
2025-01-04 14:30:30 -08:00
Mitchell Hashimoto
305e5b3533 fix typo: CSI header (#4565)
Fixes a typo in the keybindings documentation comments.
Originally opened on the website repo at
https://github.com/ghostty-org/website/pull/259
2025-01-04 14:27:23 -08:00
Mitchell Hashimoto
32c4a9d65e macOS: Input Improvements (#4591)
Sorry for the vague title. This PR addresses multiple issues:

1. Fixes #4540 
2. #4522 is fixed for macOS only
3. Fixes #4590 
4. Fixes an untracked issue where `command+key` events will not send
release events for Kitty keyboard protocol, something I only noticed
while working on this.

There are multiple components to this PR.

## Part 1: `App/Surface.keyEventIsBinding`

This new API (also available in libghostty as
`ghostty_surface_key_is_binding`) returns a boolean true if the given
key event would match a binding trigger if it was the next key event
sent. It does not process the binding now.

This can be used by event handlers that intercept key events to
determine if it should send the event to Ghostty. This helps resolve
#4590 for us but is also part of all resolved issues.

## Part 2: macOS `performKeyEquivalent` changes

macOS calls `performKeyEquivalent` for any key combination that may
trigger a key equivalent. if this returns `true` then it is handled and
macOS ceases processing the event.

We were already using this to intercept things like `Ctrl+/` which
triggers a context menu in macOS Sequoia. But we now expand this to
intercept all events to check for bindings. This lets us fix #4590.

Additionally, it's been changed to special case `cmd+period`. I'm sure
more need to be added.

## Part 3: NSEvent local listener for command keyUp events

macOS simply doesn't send `keyUp` events for key events with command
pressed. The only way to work around this is to register an `NSEvent`
local listener. We now do this. This fixes the untracked issue noted
above.
2025-01-04 14:22:44 -08:00
Mitchell Hashimoto
7e1260c9e9 Improve the documentation for move_tab keybind action (#4583)
As a new user of ghostty, it was not intuitive to figure out how to
provide the `offset` parameter. It makes sense when you look more of the
rest of the options but I think we can still make these docs cleaner,
like I have done in this PR.

Thanks!
2025-01-04 14:21:05 -08:00
Mitchell Hashimoto
40bdea7335 macos: handle overridden system bindings with no focused window 2025-01-04 14:07:47 -08:00
Mitchell Hashimoto
1bcfff3b79 macos: manual send keyUp event for command key 2025-01-04 14:02:16 -08:00
Mitchell Hashimoto
3e89c4c2f4 Key events return boolean if handled 2025-01-04 13:36:40 -08:00
Mitchell Hashimoto
4031815a8d macos: if a key event would result in an immediate binding then do it 2025-01-04 12:45:16 -08:00
Mitchell Hashimoto
8b8c53fc4c macos: add NSEvent extension to convert to libghostty key events 2025-01-04 12:45:15 -08:00
Mitchell Hashimoto
4d103ca16d core: add keyEventIsBinding
This API can be used to determine if the next key event, if given as-is,
would result in a key binding being triggered.
2025-01-04 12:45:15 -08:00
Kiril Angov
2dc518d8b0 Improve the documentation for move_tab keybind action 2025-01-04 12:57:38 -05:00
Mitchell Hashimoto
8f5f432ab6 Move app quit to apprt action (#4577)
This changes quit signaling from a boolean return from core app `tick()`
to an apprt action. This simplifies the API and conceptually makes more
sense to me now.

This wasn't done just for that; this change was also needed so that
macOS can quit cleanly while fixing #4540 since we may no longer trigger
menu items. I wanted to split this out into a separate commit/PR because
it adds complexity making the diff harder to read.
2025-01-04 07:37:54 -08:00
Mitchell Hashimoto
6b30736776 Move app quit to apprt action
This changes quit signaling from a boolean return from core app `tick()`
to an apprt action. This simplifies the API and conceptually makes more
sense to me now.

This wasn't done just for that; this change was also needed so that
macOS can quit cleanly while fixing #4540 since we may no longer trigger
menu items. I wanted to split this out into a separate commit/PR because
it adds complexity making the diff harder to read.
2025-01-04 07:22:28 -08:00
Damien Mehala
d3334ecb06 [3/12] parse ConEmu OSC9;3 2025-01-04 16:19:19 +01:00
Mitchell Hashimoto
e8811ac6fb Move app quit to apprt action
This changes quit signaling from a boolean return from core app `tick()`
to an apprt action. This simplifies the API and conceptually makes more
sense to me now.

This wasn't done just for that; this change was also needed so that
macOS can quit cleanly while fixing #4540 since we may no longer trigger
menu items. I wanted to split this out into a separate commit/PR because
it adds complexity making the diff harder to read.
2025-01-04 07:18:53 -08:00
Alexandre Antonio Juca
0599f73fac chore: use KiB notation for representing memory size 2025-01-04 09:02:25 +01:00
dkmar
69e4428d80 fix typo: CSI header 2025-01-03 23:34:39 -08:00
Alexandre Antonio Juca
0e63dc18ff Merge branch 'feature/display-memory-size-in-bytes-and-kb' of github.com:AlexJuca/ghostty into feature/display-memory-size-in-bytes-and-kb 2025-01-03 23:42:21 +01:00
Alexandre Antonio Juca
f14371e909 Merge branch 'main' of github.com:AlexJuca/ghostty into feature/display-memory-size-in-bytes-and-kb 2025-01-03 23:37:34 +01:00
Alexandre Antonio Juca
78cdc7d0de Merge branch 'main' of https://github.com/AlexJuca/ghostty into feature/display-memory-size-in-bytes-and-kb 2025-01-03 23:34:52 +01:00
Mitchell Hashimoto
1baf8928a0 don't error when gtk4 could not be found via pkg-config (#4546)
running `zig build --help` was crashing for some people, I narrowed it
down to gtk4 not being installed however that shouldn't make the help
message not nor should it block glfw builds
2025-01-03 14:28:11 -08:00
Jan200101
6fd901fd3d don't error when gtk4 could not be found via pkg-config 2025-01-03 23:12:11 +01:00
Jan200101
0493b79caf don't make library building logic public 2025-01-03 22:42:29 +01:00
Jan200101
1dc9157727 always link system freetype2 using pkg-config 2025-01-03 22:42:01 +01:00
Jan200101
72e0fb14fe don't build freetype2 when system integration is enabled 2025-01-03 22:41:15 +01:00
Mitchell Hashimoto
fa30a04f2a Docs: update goto_split documentation (#4536)
In #4388, documentation was added for goto_split but in #3427 this
documentation was made outdated but not updated. This makes the
documentation up to date and brings the ordering in line with new_split
2025-01-03 12:54:57 -08:00
Mitchell Hashimoto
2f6e7d6ecd gtk: add class names to the rest of the windows (#4533)
This sets up for theming the other windows in the future.
2025-01-03 12:51:13 -08:00
Mitchell Hashimoto
a014eee968 don't build oniguruma when system integration is enabled (#4534)
follow-up to #4520

all the same stuff for the previous two

the tests for this only run for the native target and was added for the
iOS build (3360a008cd), I've made a second
version of this commit to remove the native check if thats more desired
(d247a22de036140297942701090e0eafb3d1a72d)

ghostty and all tests appear to run on my system both with and without
system integration
2025-01-03 12:44:43 -08:00
Caleb Norton
2610f5b4e2 Docs: update goto_split documentation
In #4388, documentation was added for goto_split but in #3427 this
documentation was made outdated but not updated. This makes the
documentation up to date and brings the ordering in line with new_split
2025-01-03 14:32:39 -06:00
Jan200101
dc90ef776e don't build oniguruma when system integration is enabled 2025-01-03 21:27:22 +01:00
Jeffrey C. Ollie
063868b311 gtk: add class names to the rest of the windows 2025-01-03 14:08:00 -06:00
Qwerasd
25a112469c font(coretext): add config to adjust strength of font-thicken.
This is achieved by rendering to an alpha-only context rather than a
normal single-channel context, and adjusting the brightness at which
coretext thinks it's drawing the glyph, which affects how it applies
font smoothing (which is what `font-thicken` enables).
2025-01-03 14:19:19 -05:00
Mitchell Hashimoto
ab9b14215c input: legacy encoding falls back to mapping of logical key for ctrlseq (#4528)
Fixes #4518

If our UTF8 encoding is not recognized, we fall back to the ASCII
mapping of the logical key for the control sequence. This allows
cyrillic control characters to work.

I also verified that non-cyrllic (US) and alternate layouts (Dvorak)
work as expected still.
2025-01-03 11:09:09 -08:00
Mitchell Hashimoto
74386be017 don't build fontconfig when system integration is enabled (#4520)
same as #4205 but for fontconfig

it follows the same pattern with one addition:
the module needs a known target to be able to link a system library
I don't think this will affect anything 

ghostty and all tests appear to run on my system both with and without
system integration
2025-01-03 11:01:31 -08:00
Mitchell Hashimoto
1bf8b262ea os: directory functions should prefer cached home if available (#4527)
This fixes tests as well if env vars are set.
2025-01-03 11:01:03 -08:00
Peter Cock
6459e5c8ca Fixing a few typos in the source code comments (#4529) 2025-01-03 11:00:45 -08:00
Mitchell Hashimoto
45d005ce65 input: legacy encoding falls back to mapping of logical key for ctrlseq
Fixes #4518

If our UTF8 encoding is not recognized, we fall back to the ASCII
mapping of the logical key for the control sequence. This allows
cyrillic control characters to work.

I also verified that non-cyrllic (US) and alternate layouts (Dvorak)
work as expected still.
2025-01-03 10:53:54 -08:00
Mitchell Hashimoto
e03c428728 os: directory functions should prefer cached home if available
This fixes tests as well if env vars are set.
2025-01-03 10:39:03 -08:00
Jan200101
9d286de834 don't build fontconfig when system integration is enabled 2025-01-03 18:39:11 +01:00
Mitchell Hashimoto
7eb35d7275 Fix: Correct version strings for simdutf and cimgui to match vendored files (#4468)
The cimgui version listed does not match the upstream commit or the
vendored cimgui files
Checking the upstream `git log` the commit corresponds to `commit
e391fe2e66eb1c96b1624ae8444dc64c23146ef4 (tag: v1.90.6-docking)` however
the `build.zig.zon` is outdated.
The vendored cimgui files also contain the header
```cpp
// This file is automatically generated by generator.lua from
// https://github.com/cimgui/cimgui based on imgui.h file version "1.90.6" 19060
// from Dear ImGui https://github.com/ocornut/imgui with imgui_internal.h api
// docking branch
```

I wasn't too clear with what the comment meant:
```
// This should be kept in sync with the submodule in the cimgui source
// code to be safe that they're compatible.
```
and assumed it was referring to the vendored cimgui files, added a
comment pointing out where to find the cimgui source mentioned.
2025-01-03 09:13:52 -08:00
acehinnnqru
b0404867b7 fix: macos incorrect quick terminal position 2025-01-03 22:44:26 +08:00
Alexandre Antonio Juca
e1bc6477b1 Merge branch 'main' of https://github.com/AlexJuca/ghostty into feature/display-memory-size-in-bytes-and-kb 2025-01-03 12:49:35 +01:00
Alexandre Antonio Juca
6b4e6d2fa5 feat: Display memory usage and and limit in both bytes and Kb for improved readability 2025-01-03 12:48:52 +01:00
Damien Mehala
8a3aae2caf code review
- Change show_message_box from struct to string.
- Add tests:
  - Blank message
  - Spaces only message
  - No trailing semicolon OSC 9;2
2025-01-03 12:01:50 +01:00
Bryan Lee
9fa404c390 Ensure all search results are visible in theme list
When searching in the theme list (e.g., searching for "Snazzy"), some matching themes might be hidden due to incorrect window position handling.

This fix ensures all matching themes are visible by adjusting the window position logic.
2025-01-03 16:24:53 +08:00
azhn
65a0fa4f35 Fix: Update pkg/simdutf/build.zig.zon to match vendored version 2025-01-03 18:22:24 +11:00
azhn
29f040716c Fix cimgui version string to match pkg/cimgui/vendor/ and the upstream version 2025-01-03 17:47:50 +11:00
Mitchell Hashimoto
e2f9eb6a6f keybindings: improve sort to include key value 1,2,3,4... (#4399)
Current sort used by `+list-keybinds` doesn't include the value of the
key:
```
ctrl  + shift + v                      paste_from_clipboard
ctrl  + shift + a                      select_all
...
ctrl  + shift + q                      quit
ctrl  + shift + n                      new_window
...
alt   + five                           goto_tab:5
alt   + eight                          goto_tab:8
...
alt   + six                            goto_tab:6
alt   + seven                          goto_tab:7
```
adding the key value improves the sort order

```
ctrl  + shift + a                      select_all
ctrl  + shift + c                      copy_to_clipboard
...
ctrl  + shift + n                      new_window
ctrl  + shift + o                      new_split:right
ctrl  + shift + q                      quit
...
alt   + one                            goto_tab:1
alt   + two                            goto_tab:2
alt   + three                          goto_tab:3
...
alt   + eight                          goto_tab:8
alt   + nine                           last_tab
alt   + f4                             close_window
2025-01-02 19:43:19 -08:00
Mitchell Hashimoto
f2d255d423 Correct typos and update typos.toml (#4456)
I noticed that the version of `typos` in nixpkgs is still at
[1.28.4](https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/by-name/ty/typos/package.nix#L12)
(as of [2 weeks
ago](https://github.com/crate-ci/typos/releases/tag/v1.28.4)), while the
latest version is
[1.29.3](https://github.com/crate-ci/typos/releases/tag/v1.29.3). For
tools like this where the dictionary is constantly being updated, we
might want to consider using `uses: crate-ci/typos@master` directly in
our CI workflow instead of the version from nixpkgs.

I understand the current setup using `devShell.nix` helps maintain
consistency between local development and CI environments. However, I
found some typos that can be detected by the new version of typos, but
were missed by the old version of typos on nixpkgs.

<img width="690" alt="image"
src="https://github.com/user-attachments/assets/d9e7f45a-8980-4acd-a093-a1e45521a28d"
/>

## Proposal:

1. Use `crate-ci/typos` action in CI while keeping nixpkgs version for
local development (catching more typos in CI but introducing some
inconsistency)
2025-01-02 19:29:26 -08:00
Mitchell Hashimoto
bec46fc2fc Revert "gtk: equalize on double clicking the split handle (#3557)"
This reverts commit 09470ede55, reversing
changes made to 6139cb00cf.
2025-01-02 19:17:34 -08:00
Mitchell Hashimoto
bc5cbf3e87 kittygfx: Ensure temporary files are named per spec (#4451)
Temporary files used with Kitty graphics must have
"tty-graphics-protocol" somewhere in their full path.


https://sw.kovidgoyal.net/kitty/graphics-protocol/#the-transmission-medium
2025-01-02 19:13:19 -08:00
Bryan Lee
ac524b6c34 Correct typos and update typos.toml 2025-01-03 09:55:21 +08:00
David Leadbeater
4cb2fd4f79 Add negative test for temporary filename and fix other tests 2025-01-03 12:09:49 +11:00
Mitchell Hashimoto
a10b45fb1f core: detect what desktop environment the user is using (#4343) 2025-01-02 16:44:16 -08:00
Jeffrey C. Ollie
3c93f00d04 cli: only print out DE when using the GTK apprt 2025-01-02 18:29:33 -06:00
Jeffrey C. Ollie
c89df01e13 core: prohibit checking for the desktop environment on linux during comptime 2025-01-02 18:29:33 -06:00
Jeffrey C. Ollie
5c39d09053 core: detect what desktop environment the user is using 2025-01-02 18:29:31 -06:00
Mitchell Hashimoto
cde8b7e810 chore: fix typos (#4407)
Fixes:

- te -> the
- require you restart -> require you to restart
- neovim -> Neovim
2025-01-02 16:15:42 -08:00
David Leadbeater
c9dfcd2781 kittygfx: Ensure temporary files are named per spec
Temporary files used with Kitty graphics must have
"tty-graphics-protocol" somewhere in their full path.

https://sw.kovidgoyal.net/kitty/graphics-protocol/#the-transmission-medium
2025-01-03 11:15:03 +11:00
Mitchell Hashimoto
7e1b7bb8b3 performable: prefix (#4345)
closes #4328
closes #3970

makes this possible now
```
keybind = performable:ctrl+c=copy_to_clipboard # copy if theres a selection else send sigint
keybind = ctrl+v=paste_from_clipboard
```
2025-01-02 16:11:24 -08:00
Mitchell Hashimoto
bcd4b3a680 config: improve adw-toast docs 2025-01-02 16:07:58 -08:00
Mitchell Hashimoto
d42e67bdad gtk: fix non-notebook separator colors (#4421)
Before: 
![Screenshot From 2025-01-02
12-25-55](https://github.com/user-attachments/assets/62c3f15b-e717-499b-963f-c72ec8988f25)
After:
![Screenshot From 2025-01-02
12-28-02](https://github.com/user-attachments/assets/9e04b249-2414-4cae-8ea2-94c86bba5be6)
2025-01-02 16:04:54 -08:00
Mitchell Hashimoto
e6399c947a update our default bindings that are performable 2025-01-02 15:54:09 -08:00
Jeffrey C. Ollie
a0de1be65f gtk: fix non-notebook separator colors 2025-01-02 17:49:15 -06:00
Mitchell Hashimoto
b65c26966a macos: fix window borders on dark mode (#4308)
After updating to 1.0.1 I noticed something different in the terminal,
which turned out being the window borders - it appeared as if Ghostty
was using light-mode style borders (dark/black outline with a thin light
stroke at the top) instead of the entire light outline from before:

| 1.0.0 | 1.0.1 |
| - | - |
| <img width="308" alt="Screenshot 2025-01-01 at 2 28 12 PM"
src="https://github.com/user-attachments/assets/d8bc5bdd-c3b2-401c-a8ed-9da0b768cb3d"
/> | <img width="308" alt="Screenshot 2025-01-01 at 2 29 07 PM"
src="https://github.com/user-attachments/assets/fd710bed-1756-4f66-8402-bfbdd25218ab"
/> |

After digging a bit, I found #3834, which fixes fullscreen background
colors through alpha channels by appending a `withAlphaComponent(0.0)`
to `backgroundColor` - for reasons I may be entirely unaware of (since
I'm not a Swift developer), this seems to cause the dark-mode border
style to go away.

Some lines above that, I noticed the `.clear` callout from line 266,
which talks about matching Terminal.app's styles, and it _also_ has a
`withAlphaComponent` but set to `0.001` - if I understand correctly, and
the fix from #3834 works by setting the alpha component to a
_practically_ zero value, then I thought perhaps a really small number
like `0.001` could do the trick as well. This ended up working and
bringing back the right borders again.

Not sure again if this may make a difference anywhere else in the app or
bring any undesired behavior, but if anyone who is well-versed in Swift
would like chime in with more details or perhaps a better approach, I'd
greatly appreciate it!
2025-01-02 15:41:57 -08:00
Mitchell Hashimoto
95b73f197f Add docs for performable 2025-01-02 15:41:01 -08:00
Mitchell Hashimoto
89e0e7e69c support different base for palette keys in config (#4298)
motivated by the desire to align config lines

https://ziglang.org/documentation/master/std/#std.fmt.parseInt

unaligned

```ini
palette = 0=#333333

palette = 1=#FF9999
palette = 2=#99FF99
palette = 4=#9999FF

palette = 3=#FFFF33
palette = 5=#FF33FF
palette = 6=#33FFFF

palette = 7=#CCCCCC
palette = 8=#666666

palette = 9=#FF66CC
palette = 10=#CCFF66
palette = 12=#66CCFF

palette = 11=#FFCC66
palette = 13=#CC66FF
palette = 14=#66FFCC

palette = 15=#FFFFFF
```

expecting

```ini
palette = 0x0=#333

palette = 0x1=#F99
palette = 0x2=#9F9
palette = 0x4=#99F

palette = 0x3=#FF3
palette = 0x5=#F3F
palette = 0x6=#3FF

palette = 0x7=#CCC
palette = 0x8=#666

palette = 0x9=#F6C
palette = 0xA=#CF6
palette = 0xC=#6CF

palette = 0xB=#FC6
palette = 0xD=#C6F
palette = 0xE=#6FC

palette = 0xF=#FFF
```
2025-01-02 15:37:20 -08:00
Gabriel Moreno
82695edaff macos: fix window borders on dark mode 2025-01-02 15:27:01 -08:00
Damien Mehala
8d7ed3e0fc feat: parse ConEmu OSC9;2 2025-01-03 00:26:06 +01:00
Mitchell Hashimoto
405fe377d2 wuffs: update, add jpeg decoding, add simple tests (#4250)
1. Update wuffs to v0.4.0-alpha.9
2. Add JPEG decoding
3. Add basic unit tests for image decoding
4. Add CI jobs to run wuffs unit tests.
2025-01-02 15:24:28 -08:00
roshal
fe9bbec92e config: allow other base numbers for palette indexes 2025-01-02 15:21:57 -08:00
Mitchell Hashimoto
263146ebe2 core: if we change RLIMIT_NOFILE, reset it when executing commands (#4241)
Fixes #4232 .
2025-01-02 15:19:52 -08:00
Mitchell Hashimoto
8827b6e738 Partial fix for #1938, add GDK_DEBUG=gl-no-fractional (#4255)
GSK_RENDERER=opengl does not properly handle fractional scaling and thus
we need to set GDK_DEBUG=gl-no-fractional.

Potential fix for #1938
2025-01-02 15:17:25 -08:00
Mitchell Hashimoto
0ef24f3c75 ci: only test pkgs on Linux 2025-01-02 15:08:42 -08:00
Mitchell Hashimoto
8e47d0267b Move resource limits to a dedicated struct, restore before preexec 2025-01-02 15:05:10 -08:00
Damien Mehala
9d9fa60ece code review
- Default to 100 if the value can't be parsed as an integer or
  is missing entirely.
2025-01-02 23:57:53 +01:00
Mitchell Hashimoto
602e4eb606 Implement loading custom css in the GTK app (#4200)
Closes https://github.com/ghostty-org/ghostty/issues/4089
Gave it a shot and implemented the custom css loading.
My general idea is to use a provider for each stylesheet the user wants
to load and then when the config changes unload them and create new
providers.
A separate provider has to be used for each stylesheet the user wants to
load, since when the provider loads the css it clears all the previously
loaded styles, so in effect we cannot use one provider to load multiple
stylesheets, but maybe there is a better way to overcome this limitation
which I'm not seeing.
2025-01-02 14:34:28 -08:00
Mitchell Hashimoto
764a2365af don't build harfbuzz when system integration is enabled (#4205)
makes use of the system harfbuzz if system integration is enabled
otherwise it builds the library and uses it in the module

this has the added benefit that package maintainers don't have to ship a
separate copy of harfbuzz and worry about compatibility with the system
library

Some notes:
- the logic to build the library has been split into a separate function
- needed options are passed as an anonymous struct because its so
minimal that there really is not need for an explicit type
- unlike `Build.systemIntegrationOption`, `Build.option` cannot be
called mutiple times to declare it before its used

tests appear to run on my system both with and without system
integration
2025-01-02 14:33:12 -08:00
Mitchell Hashimoto
8c74b80704 config: Add the option toast_on_clipboard_copy (#4185)
Add a config option to enable/disable the toast shown on clipboard copy

Also suggested in
https://github.com/ghostty-org/ghostty/discussions/4165
2025-01-02 14:31:13 -08:00
Mitchell Hashimoto
509cf306f5 config: improve documentation for color configuration (#4184)
The documentation used to say e.g. "The format of the color is the same
as the `background` configuration; see that for more information.", yet
`background` left the format actually undocumented.

To avoid people having to jump around the docs to find out the supported
formats, the prose for the formats is repeated for each color.

I dug around a bit to find out that named colors from the default X11
map are also a supported format (`cursor-color = purple` works fine), so
that's now documented too.
2025-01-02 14:23:09 -08:00
Mitchell Hashimoto
fb8c83e07c config: change toast config to packed struct 2025-01-02 14:15:16 -08:00
Mitchell Hashimoto
d28024bb60 Clarify CLI vs. Keybind Actions documentation (#4116)
https://github.com/ghostty-org/ghostty/discussions/4107#discussioncomment-11699228

I was confused about not being able to run `ghostty +new_window` since I
hadn't read the docs closely enough to understand the distinction
between Keybind Actions and CLI Actions.

I think if the error messages I've modified here would have read this
way to begin with I would've had a better chance of discovering this
distinction on my own.

I did read the Contributing guidelines but I avoided opening an Issue
since the changes here are minimal enough that I felt it would just add
noise. If that's a mistake then I'm happy to close this out and return
to the original discussion and/or create a new Issue.
2025-01-02 14:10:23 -08:00
Yotam Gurfinkel
e6bb1a56eb config: Add the option toast_on_clipboard_copy
Add a config option to enable/disable the toast shown on clipboard copy
2025-01-02 14:09:29 -08:00
Mitchell Hashimoto
5293e8a819 Merge branch 'patch-1' 2025-01-02 14:06:41 -08:00
Mitchell Hashimoto
bed37ac844 update wording 2025-01-02 14:06:23 -08:00
Mitchell Hashimoto
e7354e7308 Update src/config/Config.zig
Co-authored-by: Aarni Koskela <akx@iki.fi>
2025-01-02 14:05:49 -08:00
Mitchell Hashimoto
18001c3251 font: allow non-boolean font feature settings (#4139)
\+ much more flexible syntax and lenient parser
\+ allows comma-separated list as a single config value

This allows, e.g. `cv01 = 2` to select the second variant of `cv01`.

Resolves #3128 

Parser could probably be a little smaller than it is- would be a lot
cleaner with the labeled switch continue pattern from Zig 0.14. Maybe
should've put it in its own file too...

I spent *much* too long trying to test this with `cv01` with
[monaspace](https://github.com/githubnext/monaspace) before realizing
that the README refers to v1.2 but the latest released version (and
hence the one I had installed) was v1.101 -- I installed the v1.2
version and tested with both CoreText and HarfBuzz and successfully set
`cv01 = 2` and got the expected result.

Feel free to make any stylistic changes you feel necessary before
merging.
2025-01-02 14:04:20 -08:00
Mitchell Hashimoto
3a01beb050 Don't steal focus on mouse events that are within 1 px (#3997)
Fixes #3229


Gets the desired behavior for #3229, I'm unsure if there is a nicer way
to omit the mouse motion events from the event controller
when the glarea is resized due to the splitting behavior. Thresholding
to 1px is required because just checking for equality results
in the focus still being stolen sometimes. This is kinda of a hack so a
nicer solution would be much appreciated!
2025-01-02 14:01:27 -08:00
Mitchell Hashimoto
fc545cd048 fix: handle intermediate bytes in CSI and ESC sequences (#4063)
This adds missing handling for CSI and ESC commands.

Fixes: https://github.com/ghostty-org/ghostty/issues/3122

Supersedes: #3132
2025-01-02 13:53:19 -08:00
Mitchell Hashimoto
f5f887efd9 fix: selected text remains after clear_screen action (#4040)
Fixes #3414
2025-01-02 13:50:09 -08:00
Mitchell Hashimoto
1a530cb96a core: add build option to disable sentry (#3934)
This disables compiling/linking Sentry automatically on platforms other
than macOS, which removes a potential blocker for getting Ghostty
running on *BSD or other systems.

This is also useful for the security paranoid that don't want any chance
that sensitive information could be captured in a crash dump.
2025-01-02 13:49:54 -08:00
Matt Rochford
6a4842f110 Don't steal focus on mouse events that are within 1 px 2025-01-02 13:46:15 -08:00
Mitchell Hashimoto
f60068eabd add option to strip build regardless of optimization (#3945)
adds the option "strip" which can be used to override the default strip
setting, which is based on the optimization mode.

Useful for a distro setting where you want a release build but still
keep symbols.

Also reuses the option for the shared and static library
2025-01-02 13:41:47 -08:00
Mitchell Hashimoto
7eb6b29d4c macos: make auto-update optional (#4436)
When unset, we use Sparkle's default behavior, which is based on the
user's preference stored in the standard user defaults.

The rest of the previous behavior is preserved:
- When SUEnableAutomaticChecks is explicitly false, auto-updates are
disabled.
- When 'auto-update' is set, use its value to set Sparkle's auto-update
behavior.

Fixes #4433
2025-01-02 13:38:15 -08:00
Mitchell Hashimoto
0d2a6c7346 gtk: refactor gtk & adw notebook implementations (#3578)
Put GTK and libadwaita notebook implementations into separate structs/
files for clarity.
2025-01-02 13:37:29 -08:00
Mitchell Hashimoto
898d988799 Set an initial start position (#3929)
Allow the ability to set an initial start position from the config. Adds
`window-initial-position-{x,y}` to the config as an optional i16 value
(see swift docs in [this
comment](https://github.com/ghostty-org/ghostty/pull/3929#discussion_r1899266607)
for the reasoning behind this if needed) and handles setting the
position when the initial window is created

Closes https://github.com/ghostty-org/ghostty/issues/3362
2025-01-02 13:35:13 -08:00
Mitchell Hashimoto
7a5ef3da2b remove sentry test for macOS, remove windows check 2025-01-02 13:34:23 -08:00
Jeffrey C. Ollie
cb8d30f938 core: add build option to disable sentry 2025-01-02 13:32:45 -08:00
Jeffrey C. Ollie
0778c67429 gtk: refactor gtk & adw notebook implementations
Put GTK and libadwaita notebook implementations into separate structs/
files for clarity.
2025-01-02 15:21:11 -06:00
Mitchell Hashimoto
29b96be84f tweaks to window position 2025-01-02 13:18:53 -08:00
Mitchell Hashimoto
e634eb102e feat: expand tildes ~ in config file paths to HOME (#3811)
Fixes https://github.com/ghostty-org/ghostty/issues/3328
2025-01-02 13:11:09 -08:00
Adam Wolf
f49a029c49 Merge branch 'ghostty-org:main' into InitialStartPosition 2025-01-02 12:58:40 -08:00
Adam Wolf
f9250e28b5 chore: rename window-position-{x,y} to window-initial-position-{x,y} 2025-01-02 12:58:40 -08:00
Adam Wolf
16bf3b8820 docs: update config docs to reflect window positioning changes 2025-01-02 12:58:40 -08:00
Adam Wolf
200d0d642b macos: handle setting initial window position when window is created 2025-01-02 12:58:40 -08:00
Adam Wolf
970e45559b apprt/glfw: handle setting initial window position when window is
created
2025-01-02 12:58:40 -08:00
Adam Wolf
13d935a401 revert: renaming of window-position-{x,y} 2025-01-02 12:58:40 -08:00
Adam Wolf
a7e3e5915c docs: fix spelling of macOS 2025-01-02 12:58:40 -08:00
Adam Wolf
568f1f9d72 chore: removed setInitialWindowPosition from gtk and renamed window-position-{x,y} to start-position-{x,y} for clarity 2025-01-02 12:58:40 -08:00
Adam Wolf
7195bda7a2 chore: add missing case in switch statement 2025-01-02 12:58:40 -08:00
Adam Wolf
9a58de6d5a feat(macos): allow setting an intial start position 2025-01-02 12:58:40 -08:00
Adam Wolf
5ced72498e feat(linux): allow setting an intial start position 2025-01-02 12:58:40 -08:00
Mitchell Hashimoto
d58b618c74 config: windows can't expand homedir (yet) 2025-01-02 12:55:47 -08:00
Jon Parise
713dd24ab9 macos: make auto-update optional
When unset, we use Sparkle's default behavior, which is based on the
user's preference stored in the standard user defaults.

The rest of the previous behavior is preserved:
- When SUEnableAutomaticChecks is explicitly false, auto-updates are
  disabled.
- When 'auto-update' is set, use its value to set Sparkle's auto-update
  behavior.
2025-01-02 15:53:01 -05:00
Mitchell Hashimoto
a94cf4b3a2 config: make diagnostic if homedir expansion fails 2025-01-02 12:44:03 -08:00
Mitchell Hashimoto
600e417154 Restore hidden titlebar after fullscreen (#3572)
This fixes https://github.com/ghostty-org/ghostty/issues/3535 .

There exists an issue in ghostty on mac where if you have hidden your
titlebar, then enter fullscreen, the titlebar will reappear after
exiting fullscreen.

The reason for this is that after exiting fullscreen macos reapplies
some styling on the new window created after exiting fullscreen. To
combat this we will reapply the styling to hide the titlebar after
exiting fullscreen.

Required config:
```
macos-titlebar-style = hidden
macos-non-native-fullscreen = true
```

Steps to reproduce:

- Open Ghostty
- Enter fullscreen (non-native)
- Exit fullscreen

On main you will see the titlebar reappearing after exiting fullscreen,
while that does not happen with this patch.
2025-01-02 12:40:11 -08:00
z-jxy
7bd842a530 add test for expandHomeUnix 2025-01-02 12:33:34 -08:00
z-jxy
5ae2cc01ac move current expandHome functionality into separate expandHomeUnix function 2025-01-02 12:33:34 -08:00
z-jxy
138a8f1602 move tilde expansion functionality to os/homedir 2025-01-02 12:33:34 -08:00
z-jxy
d27761a499 use home() from os/homedir, check for ~/ rather than ~ 2025-01-02 12:33:34 -08:00
z-jxy
7c9c982df7 refactor: handle tilde before checking realpath 2025-01-02 12:33:34 -08:00
z-jxy
f184258f0e expand tilde to HOME in config 2025-01-02 12:33:34 -08:00
Christoffer Tønnessen
88674a1957 Restore hidden titlebar after fullscreen
This fixes https://github.com/ghostty-org/ghostty/issues/3535 .

There exists an issue in ghostty on mac where if you have hidden your
titlebar, then enter fullscreen, the titlebar will reappear after
exiting fullscreen.

The reason for this is that after exiting fullscreen macos reapplies
some styling on the new window created after exiting fullscreen. To
combat this we will reapply the styling to hide the titlebar after
exiting fullscreen.

Required config:
```
macos-titlebar-style = hidden
macos-non-native-fullscreen = true
```

Steps to reproduce:

- Open Ghostty
- Enter fullscreen (non-native)
- Exit fullscreen

On main you will see the titlebar reappearing after exiting fullscreen,
while that does not happen with this patch.
2025-01-02 12:25:02 -08:00
Mitchell Hashimoto
b68e9a10e0 gtk: Always read gtk-xft-dpi for font scaling (#4424)
Commit ad503b8c4f ("linux: consider Xft.dpi to scale the content")
introduced reading gtk-xft-dpi when the X11 build option is enabled.

While the name suggests it is X11-specific (perhaps it was at one
point), gtk-xft-dpi is a GTK setting that can be modified regardless of
GDK backend. GNOME’s Large Text accessibility setting ultimately
modifies it. Outside of desktop environments, it can be set via GTK
configuration files.

Remove the conditional gating the code on X11, since none of the code is
actually X11-specific. While we’re here, document scaling behaviors
under Config.font-size.

Fixes: ad503b8c4f ("linux: consider Xft.dpi to scale the content")
Fixes: https://github.com/ghostty-org/ghostty/issues/4338
Link: https://docs.gtk.org/gtk4/class.Settings.html
Link: https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html
2025-01-02 12:10:24 -08:00
Mitchell Hashimoto
afdaaf1825 Use platform-specific cache paths and respect XDG_CACHE_HOME (#3458)
## Description:

Use proper platform-specific methods to determine cache directory paths:

1. First check `XDG_CACHE_HOME` environment variable

2. On macOS:

- Use `NSFileManager.URLForDirectory` to get system cache path
(`~/Library/Caches`)
   
- Use bundle ID (`com.mitchellh.ghostty`) as base directory to follow
macOS conventions
   
3. Fall back to XDG spec defaults for other platforms (`~/.cache`)

## Changes:

- Extract common NSFileManager path lookup logic into `makeCommonPath`
helper

- Use `NSFileManager.URLForDirectory` to get proper macOS cache
directory

- Use bundle ID for macOS cache directory to match system conventions
and `appSupportDir` behavior

- Follows the [same pattern as our configuration file
path](https://ghostty.org/docs/config#macos-specific-path-(macos-only):)
(`~/Library/Application Support/com.mitchellh.ghostty/config`)
  
- Aligns with `appSupportDir` implementation which already uses bundle
ID

- Add tests to verify path construction for different platforms

## Questions for reviewers:

1. Should we add migration logic for existing cache files?

2. Or should we document this as a breaking change and let users
manually clean up?

## Testing:

1. Added unit tests for path construction:

   - macOS: verifies `~/Library/Caches/com.mitchellh.ghostty` path
   
   - Linux: verifies `~/.cache/ghostty` path (XDG spec)
   
2. Verified tests pass with `zig build test`

Fixes #3202
2025-01-02 12:06:12 -08:00
Mitchell Hashimoto
57af5f3106 crash: prefer XDG cache dir if available 2025-01-02 11:50:22 -08:00
Liam Hupfer
aa34b91856 gtk: Always read gtk-xft-dpi for font scaling
Commit ad503b8c4f ("linux: consider Xft.dpi to scale the content")
introduced reading gtk-xft-dpi when the X11 build option is enabled.

While the name suggests it is X11-specific (perhaps it was at one
point), gtk-xft-dpi is a GTK setting that can be modified regardless of
GDK backend. GNOME’s Large Text accessibility setting ultimately
modifies it. Outside of desktop environments, it can be set via GTK
configuration files.

Remove the conditional gating the code on X11, since none of the code is
actually X11-specific. While we’re here, document scaling behaviors
under Config.font-size.

Fixes: ad503b8c4f ("linux: consider Xft.dpi to scale the content")
Fixes: https://github.com/ghostty-org/ghostty/issues/4338
Link: https://docs.gtk.org/gtk4/class.Settings.html
Link: https://docs.gtk.org/gtk4/property.Settings.gtk-xft-dpi.html
2025-01-02 12:48:28 -06:00
Nhan Luu
0c10db9f14 chore: fix typos 2025-01-02 23:41:57 +07:00
Bryan Lee
6fca26972b Remove the redundant check and directly use dir() 2025-01-03 00:24:14 +08:00
Bryan Lee
9f44ec7c21 Use bundle ID for macOS cache directory path 2025-01-03 00:24:14 +08:00
Bryan Lee
67794d3f6f Respect XDG_CACHE_HOME and useNSFileManager for cache paths 2025-01-03 00:24:14 +08:00
Bryan Lee
1941a440d8 Use $HOME/Library/Caches on macOS instead of $HOME/.cache 2025-01-03 00:24:14 +08:00
Mitchell Hashimoto
80c3833031 Add alt keybindings for ctrl+ins = Copy and shift+ins = Paste (#2870)
On non-MacOS desktop environments (Windows, Gnome, KDE, Xfce, VS Code,
...), `ctrl+ins` and `shift+ins` are system-wide alternate keybindings
(except for terminals) for `Copy` and `Paste` respectively. This PR
explicitly defines them as such in Ghostty's default keybindings.

Using `ctrl+ins` as an alt-keybinding for `Copy` allows
static/context-unaware remapping of `Copy` to `cmd+c` for Linux systems
using Mac keyboards via
[keyd](https://github.com/NixOS/nixpkgs/issues/137698#issuecomment-2404192700);
with the default `ctrl+shift+c` keybinding for copy this is basically
impossible (because that binding only applies to terminals).
2025-01-02 07:57:57 -08:00
Mitchell Hashimoto
9503c9fe50 Rename goto_split top/bottom directions to up/down. (#3427)
Renames the top/bottom directions of `goto_split` to up/down. I have
tested this on linux (nixos) but given that `goto_split` is broken on
linux anyway (#2866) there's not a whole lot to test.

I have no way to build on macOS so I can't verify that I've changed
everything correctly for that.

Closes #3237
2025-01-02 07:08:16 -08:00
Mitchell Hashimoto
2030599e1d Fix the format string of font size in points in the inspector (#4371)
It's an `f32` which was previously formatted with `%d`.

Credits to @gabydd who found this over at
https://discord.com/channels/1005603569187160125/1324249888514506752/1324275061380874250.
2025-01-02 07:05:04 -08:00
Mitchell Hashimoto
75571fb804 Improve resize_split documentation (#4393) 2025-01-02 07:02:33 -08:00
Mitchell Hashimoto
4047c89c24 Improve new_split documentation (#4389) 2025-01-02 07:02:23 -08:00
Mitchell Hashimoto
e55444e6fe Improve goto_split documentation (#4388)
This wasn't immediately obvious to me when configuring goto_split
2025-01-02 07:02:14 -08:00
Anund
913a1404be keybindings: improve sort to include key value 1,2,3,4... 2025-01-03 01:53:07 +11:00
Jade
355ac91c0a Improve resize_split documentation 2025-01-02 21:33:47 +08:00
Jade
cd809106c4 Improve new_split documentation 2025-01-02 21:05:56 +08:00
Jade
80fe32be32 Update Binding.zig 2025-01-02 20:58:50 +08:00
Jade
15ceb18fcb Improve goto_split documentation 2025-01-02 20:54:42 +08:00
Kat
78b914b3d8 Fix format string of font size in points in the inspector
Credits to @gabydd who found this over at https://discord.com/channels/1005603569187160125/1324249888514506752/1324275061380874250.

Co-authored-by:  Gabriel Dinner-David  <gabydinnerdavid@gmail.com>
2025-01-02 07:44:40 +00:00
Jeffrey C. Ollie
22c2fe9610 wuffs: use common struct to return decoded image data 2025-01-01 22:48:30 -06:00
Jeffrey C. Ollie
652079b26c wuffs: update, add jpeg decoding, add simple tests 2025-01-01 22:48:30 -06:00
Ethan Conneely
f38d1585e8 Do nothing if action not performed with flag 2025-01-02 01:14:47 +00:00
Ethan Conneely
98aa046a4d Add performable flag 2025-01-02 00:18:39 +00:00
Ethan Conneely
46097617b4 copy_to_clipboard return false if not performed 2025-01-02 00:18:05 +00:00
Damien Mehala
c98d207eb9 code review
- Add test with invalid value.
- Fix inspector compilation.
2025-01-02 00:13:55 +01:00
Damien MEHALA
b52e76334e feat: parse ConEmu OSC9;1 2025-01-01 22:21:47 +01:00
Mitchell Hashimoto
94599102e9 Try to create parent directory when writing default config (#4295)
Fixes https://github.com/ghostty-org/ghostty/issues/4277
2025-01-01 13:02:08 -08:00
Mitchell Hashimoto
89982912c1 Fix: typo in comment (#4257)
Fix: 'becauseonce' -> 'because once'
2025-01-01 13:01:29 -08:00
Mitchell Hashimoto
bec9483892 Adding default.nix for flake-compat (#4279)
This way non-flake users can access packages with niv or npins easily,
and as flake-compat is already a dependency, this is just a "glue".
Copy-pasted from
[here](ff81ac966b (usage)).

An example with npins would be:
`npins add github ghostty-org ghostty`

and then in the config:
```nix
{pkgs, ...}: let
    sources = import ./npins;
    ghostty = import sources.ghostty;
in {
    environment.systemPackages = [ ghostty.packages.${pkgs.system}.default ];
}
```
2025-01-01 13:01:14 -08:00
Mitchell Hashimoto
028eeb442c Fix typo in freetype-load-flags documentation (#4291) 2025-01-01 13:00:54 -08:00
Mitchell Hashimoto
f1ab7bf0f6 Don't install 1024x1024 icons for Flatpak (#4313)
Per the Flatpak spec
(https://docs.flatpak.org/en/latest/conventions.html#application-icons)
the maximum icon size is 512x512. Trying to build a Flatpak with an icon
larger than this will fail.

To solve this, installing the icon is skipped when building with
-Dflatpak=true.
2025-01-01 12:58:26 -08:00
Jeffrey C. Ollie
9ea0aa4934 core: if we change RLIMIT_NOFILE, reset it when executing commands 2025-01-01 14:31:15 -06:00
Yorick Peterse
96fd18f994 Don't install 1024x1024 icons for Flatpak
Per the Flatpak spec (https://docs.flatpak.org/en/latest/conventions.html#application-icons)
the maximum icon size is 512x512. Trying to build a Flatpak with an icon
larger than this will fail.

To solve this, installing the icon is skipped when building with
-Dflatpak=true.
2025-01-01 20:43:52 +01:00
Tim Visher
57ace2d0b8 Clarify CLI vs. Keybind Actions documentation
https://github.com/ghostty-org/ghostty/discussions/4107#discussioncomment-11699228
2025-01-01 12:34:55 -05:00
Maciej Bartczak
cdf51b1304 Try to create parent directory when writing default config 2025-01-01 18:28:26 +01:00
Sebastian Lövdahl
120fffa42c Fix typo in freetype-load-flags documentation 2025-01-01 19:07:23 +02:00
arthsmn
180db3c77b Adding default.nix for flake-compat 2025-01-01 12:36:32 -03:00
moritz-john
b1a197ef57 Fix: typo in comment
Fix: 'becauseonce' -> 'because once'
2025-01-01 09:46:06 +01:00
Morgan
d203075a2e Fix for #1938, add GDK_DEBUG=gl-no-fractional 2025-01-01 17:15:15 +09:00
Maciej Bartczak
9ce4e36aa2 code review - revert explicit error handling 2025-01-01 08:56:58 +01:00
Ryan Rotter
c16dbc01f0 correct default keybinding cmd+backspace for macOS
cmd+backspace=text:\x15
2024-12-31 22:59:32 -05:00
Mitchell Hashimoto
60611b8a4a Set the paste preview text in GTK4 to monospace (#4227)
A simple change to make the text preview in the paste confirmation
dialog monospace; this feels like something that most users would want,
or at least very few users would dislike.

We just call `gtk_text_view_set_monospace` and let GTK use whatever
default monospace font it wants to use. Theoretically we could probably
tell it to use whatever font the user has configured, but this should be
sufficient for most users.
2024-12-31 15:54:43 -08:00
Mitchell Hashimoto
e9e82d94ac PACKAGING: Note GLFW is not for distribution 2024-12-31 14:34:26 -08:00
Daniel Fox
41719aa48c Set the paste preview to monospace 2024-12-31 14:26:03 -08:00
Mitchell Hashimoto
1d71196de3 up version to 1.0.2 for development 2024-12-31 14:12:00 -08:00
Maciej Bartczak
4ccd564849 code review:
- initialize custom_css_providers using a default value
- remove usage of buffered reader
- document maximum file size
- handle exceptions explicitly
2024-12-31 21:21:44 +01:00
Jan200101
5957e1101c don't build harfbuzz when system integration is enabled 2024-12-31 21:09:13 +01:00
Maciej Bartczak
973467b1ca code review:
- use ArrayListUnmanaged
- read the stylesheet file using zig api
- use proper css_provider_load_ function depending on the GTK version
2024-12-31 20:08:12 +01:00
Maciej Bartczak
27c3382a6a Implement loading custom css in the GTK app 2024-12-31 15:53:10 +01:00
Aarni Koskela
e20ec96fee config: improve documentation for color configuration 2024-12-31 14:50:37 +02:00
Jan200101
6a8b31571b reuse strip option for libraries 2024-12-31 12:09:11 +01:00
Jan200101
061a730dd3 move strip switch behind option 2024-12-31 12:06:45 +01:00
Qwerasd
2d174f9bff font: allow non-boolean font feature settings
+ much more flexible syntax and lenient parser
+ allows comma-separated list as a single config value

This allows, e.g. `cv01 = 2` to select the second variant of `cv01`.
2024-12-30 21:15:25 -05:00
Hugo Gouveia
3971c460d1 doc: add background-opacity needs restart 2024-12-30 23:05:21 -03:00
Jan200101
c87e3e98a3 give strip option a proper description 2024-12-30 18:37:59 +01:00
Ayman Bagabas
66681f94e0 fix: handle intermediate bytes in CSI and ESC sequences
This adds missing handling for CSI and ESC commands.

Fixes: https://github.com/ghostty-org/ghostty/issues/3122
2024-12-30 18:26:05 +03:00
Damien Mehala
33b1131a14 fix: selected text remains after clear_screen action
Fixes #3414
2024-12-30 11:03:59 +01:00
Daniel Patterson
3e11476d32 Add "top" and "bottom" aliases 2024-12-29 22:05:28 +00:00
Jan200101
2bb3353672 add option to strip build regardless of optimization 2024-12-29 22:06:30 +01:00
Bryan Lee
8da600a62e Add keyboard navigation for Terminal IO window
- Add J/K and arrow keys navigation in Terminal IO window
2024-12-30 01:00:09 +08:00
Daniel Patterson
8cbf8d5003 Fix broken macOS changes 2024-12-27 16:16:30 +00:00
Daniel Patterson
a4daabb28a Rename goto_split top/bottom directions to up/down. 2024-12-27 14:56:48 +00:00
deftdawg
a469191311 Merge branch 'ghostty-org:main' into alt-keybindings-copy-and-paste 2024-12-22 02:06:54 -05:00
deftdawg
9117842a45 Merge branch 'ghostty-org:main' into alt-keybindings-copy-and-paste 2024-12-15 00:21:20 -05:00
deftdawg
9252378c82 Merge branch 'ghostty-org:main' into alt-keybindings-copy-and-paste 2024-12-09 23:04:31 -05:00
DeftDawg
1e5b02302b - Add alt keybindings for ctrl+ins = Copy and shift+ins = Paste for non-MacOS systems 2024-12-02 02:47:42 -05:00
259 changed files with 15906 additions and 6036 deletions

1
.gitattributes vendored
View File

@@ -1,3 +1,4 @@
build.zig.zon.nix linguist-generated=true
vendor/** linguist-vendored
website/** linguist-documentation
pkg/breakpad/vendor/** linguist-vendored

View File

@@ -1,6 +1,31 @@
on: [push, pull_request]
name: Nix
jobs:
required:
name: "Required Checks: Nix"
runs-on: namespace-profile-ghostty-sm
needs:
- check-zig-cache-hash
steps:
- id: status
name: Determine status
run: |
results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}')
if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then
result="failed"
else
result="success"
fi
{
echo "result=${result}"
echo "results=${results}"
} | tee -a "$GITHUB_OUTPUT"
- if: always() && steps.status.outputs.result != 'success'
name: Check for failed status
run: |
echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}"
exit 1
check-zig-cache-hash:
if: github.repository == 'ghostty-org/ghostty'
runs-on: namespace-profile-ghostty-sm
@@ -25,5 +50,5 @@ jobs:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
useDaemon: false # sometimes fails on short jobs
- name: Check Zig cache hash
run: nix develop -c ./nix/build-support/check-zig-cache-hash.sh
- name: Check Zig cache
run: nix develop -c ./nix/build-support/check-zig-cache.sh

74
.github/workflows/publish-tag.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
on:
workflow_dispatch:
inputs:
version:
description: "Version to deploy (format: vX.Y.Z)"
required: true
name: Publish Tagged Release
# We must only run one release workflow at a time to prevent corrupting
# our release artifacts.
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
setup:
runs-on: namespace-profile-ghostty-sm
outputs:
version: ${{ steps.extract_version.outputs.version }}
steps:
- name: Validate Version Input
run: |
if [[ ! "${{ github.event.inputs.version }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must follow the format vX.Y.Z (e.g., v1.0.0)."
exit 1
fi
echo "Version is valid: ${{ github.event.inputs.version }}"
- name: Exract the Version
id: extract_version
run: |
VERSION=${{ github.event.inputs.version }}
VERSION=${VERSION#v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
upload:
needs: [setup]
runs-on: namespace-profile-ghostty-sm
env:
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Validate Release Files
run: |
BASE="https://release.files.ghostty.org/${GHOSTTY_VERSION}"
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz" | grep -q "^200$" || exit 1
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz.minisig" | grep -q "^200$" || exit 1
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz" | grep -q "^200$" || exit 1
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz.minisig" | grep -q "^200$" || exit 1
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal.zip" | grep -q "^200$" || exit 1
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal-dsym.zip" | grep -q "^200$" || exit 1
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/Ghostty.dmg" | grep -q "^200$" || exit 1
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/appcast-staged.xml" | grep -q "^200$" || exit 1
- name: Download Staged Appcast
run: |
curl -L https://release.files.ghostty.org/${GHOSTTY_VERSION}/appcast-staged.xml > appcast-staged.xml
mv appcast-staged.xml appcast.xml
- name: Upload Appcast
run: |
rm -rf blob
mkdir blob
mv appcast.xml blob/appcast.xml
- name: Upload Appcast to R2
uses: ryand56/r2-upload-action@latest
with:
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }}
r2-bucket: ghostty-release
source-dir: blob
destination-dir: ./

View File

@@ -68,7 +68,7 @@ jobs:
# Setup Sparkle
- name: Setup Sparkle
env:
SPARKLE_VERSION: 2.6.3
SPARKLE_VERSION: 2.6.4
run: |
mkdir -p .action/sparkle
cd .action/sparkle

View File

@@ -7,6 +7,7 @@ on:
upload:
description: "Upload final artifacts to R2"
default: false
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
@@ -135,7 +136,7 @@ jobs:
- name: Setup Sparkle
env:
SPARKLE_VERSION: 2.6.3
SPARKLE_VERSION: 2.6.4
run: |
mkdir -p .action/sparkle
cd .action/sparkle
@@ -297,7 +298,7 @@ jobs:
- name: Setup Sparkle
env:
SPARKLE_VERSION: 2.6.3
SPARKLE_VERSION: 2.6.4
run: |
mkdir -p .action/sparkle
cd .action/sparkle
@@ -367,6 +368,7 @@ jobs:
mv ghostty-macos-universal.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal.zip
mv ghostty-macos-universal-dsym.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal-dsym.zip
mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg
mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml
- name: Upload to R2
uses: ryand56/r2-upload-action@latest
with:
@@ -376,18 +378,3 @@ jobs:
r2-bucket: ghostty-release
source-dir: blob
destination-dir: ./
- name: Prep Appcast
run: |
rm -rf blob
mkdir blob
mv appcast.xml blob/appcast.xml
- name: Upload Appcast to R2
uses: ryand56/r2-upload-action@latest
with:
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }}
r2-bucket: ghostty-release
source-dir: blob
destination-dir: ./

View File

@@ -164,7 +164,7 @@ jobs:
# Setup Sparkle
- name: Setup Sparkle
env:
SPARKLE_VERSION: 2.6.3
SPARKLE_VERSION: 2.6.4
run: |
mkdir -p .action/sparkle
cd .action/sparkle

View File

@@ -6,6 +6,45 @@ on:
name: Test
jobs:
required:
name: "Required Checks: Test"
runs-on: namespace-profile-ghostty-sm
needs:
- build
- build-bench
- build-linux-libghostty
- build-nix
- build-macos
- build-macos-matrix
- build-windows
- test
- test-gtk
- test-sentry-linux
- test-macos
- prettier
- alejandra
- typos
- test-pkg-linux
steps:
- id: status
name: Determine status
run: |
results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}')
if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then
result="failed"
else
result="success"
fi
{
echo "result=${result}"
echo "results=${results}"
} | tee -a "$GITHUB_OUTPUT"
- if: always() && steps.status.outputs.result != 'success'
name: Check for failed status
run: |
echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}"
exit 1
build:
strategy:
fail-fast: false
@@ -163,10 +202,14 @@ jobs:
- name: XCode Select
run: sudo xcode-select -s /Applications/Xcode_16.0.app
- name: get the Zig deps
id: deps
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
# GhosttyKit is the framework that is built from Zig for our native
# Mac app to access.
- name: Build GhosttyKit
run: nix develop -c zig build
run: nix develop -c zig build --system ${{ steps.deps.outputs.deps }}
# The native app is built with native XCode tooling. This also does
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
@@ -199,35 +242,39 @@ jobs:
- name: XCode Select
run: sudo xcode-select -s /Applications/Xcode_16.0.app
- name: get the Zig deps
id: deps
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
- name: Test All
run: |
# OpenGL
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
# Metal
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
- name: Build All
run: |
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
build-windows:
runs-on: windows-2022
@@ -247,10 +294,10 @@ jobs:
run: |
# Get the zig version from build.zig so that it only needs to be updated
$fileContent = Get-Content -Path "build.zig" -Raw
$pattern = 'const required_zig = "(.*?)";'
$pattern = 'buildpkg\.requireZig\("(.*?)"\);'
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
Write-Output $version
$version = "zig-windows-x86_64-$zigVersion"
Write-Output $version
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
@@ -342,7 +389,8 @@ jobs:
matrix:
adwaita: ["true", "false"]
x11: ["true", "false"]
name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }}
wayland: ["true", "false"]
name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }} wayland=${{ matrix.wayland }}
runs-on: namespace-profile-ghostty-sm
needs: test
env:
@@ -374,7 +422,43 @@ jobs:
zig build \
-Dapp-runtime=gtk \
-Dgtk-adwaita=${{ matrix.adwaita }} \
-Dgtk-x11=${{ matrix.x11 }}
-Dgtk-x11=${{ matrix.x11 }} \
-Dgtk-wayland=${{ matrix.wayland }}
test-sentry-linux:
strategy:
fail-fast: false
matrix:
sentry: ["true", "false"]
name: Build -Dsentry=${{ matrix.sentry }}
runs-on: namespace-profile-ghostty-sm
needs: test
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@v1.2.0
with:
path: |
/nix
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Test Sentry Build
run: |
nix develop -c zig build -Dsentry=${{ matrix.sentry }}
test-macos:
runs-on: namespace-profile-ghostty-macos
@@ -395,8 +479,12 @@ jobs:
- name: XCode Select
run: sudo xcode-select -s /Applications/Xcode_16.0.app
- name: get the Zig deps
id: deps
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
- name: test
run: nix develop -c zig build test
run: nix develop -c zig build test --system ${{ steps.deps.outputs.deps }}
prettier:
if: github.repository == 'ghostty-org/ghostty'
@@ -478,3 +566,38 @@ jobs:
useDaemon: false # sometimes fails on short jobs
- name: typos check
run: nix develop -c typos
test-pkg-linux:
strategy:
fail-fast: false
matrix:
pkg: ["wuffs"]
name: Test pkg/${{ matrix.pkg }}
runs-on: namespace-profile-ghostty-sm
needs: test
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@v1.2.0
with:
path: |
/nix
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Test ${{ matrix.pkg }} Build
run: |
nix develop -c sh -c "cd pkg/${{ matrix.pkg }} ; zig build test"

View File

@@ -48,14 +48,14 @@ jobs:
run: |
# Only proceed if build.zig.zon has changed
if ! git diff --exit-code build.zig.zon; then
nix develop -c ./nix/build-support/check-zig-cache-hash.sh --update
nix develop -c ./nix/build-support/check-zig-cache-hash.sh
nix develop -c ./nix/build-support/check-zig-cache.sh --update
nix develop -c ./nix/build-support/check-zig-cache.sh
fi
# Verify the build still works. We choose an arbitrary build type
# as a canary instead of testing all build types.
- name: Test Build
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true
run: nix build .#ghostty
- name: Create pull request
uses: peter-evans/create-pull-request@v7
@@ -66,7 +66,7 @@ jobs:
commit-message: "deps: Update iTerm2 color schemes"
add-paths: |
build.zig.zon
nix/zigCacheHash.nix
build.zig.zon.nix
body: |
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
labels: dependencies

2
.gitignore vendored
View File

@@ -17,3 +17,5 @@ test/cases/**/*.actual.png
glad.zip
/Box_test.ppm
/Box_test_diff.ppm
/ghostty.qcow2
/build.zig.zon2json-lock

149
CODEOWNERS Normal file
View File

@@ -0,0 +1,149 @@
# This file documents the subsystem maintainers of the Ghostty project
# along with the responsibilities of a maintainer and how one can become
# a maintainer.
#
# Ghostty follows a subsystem maintainer model where distinguished
# contributors (with mutual agreement) are designated as maintainers of a
# specific subset of the project. A subsystem maintainer has more privileges
# and authority over a specific part of the project than a regular
# contributor and deference is given to them when making decisions about
# their subsystem.
#
# Ultimately Ghostty has a BDFL (Benevolent Dictator For Life) model
# currently with @mitchellh as the BDFL. The BDFL has the final say in all
# decisions and may override a maintainer's decision if necessary. I like to
# say its a BDFLFN (Benevolent Dictator For Life "For Now") model because
# long term I'd like to see the project be more community driven. But for
# now, early in its life, we're going with this model.
#
# ## Privileges
#
# - Authority to approve or reject pull requests in their subsystem.
# - Authority to moderate issues and discussions in their subsystem.
# - Authority to make roadmap and design decisions about their subsystem
# with input only from other subsystem maintainers.
#
# In all scenarios, the BDFL doesn't need to be consulted for decisions
# but may revert or override decisions if necessary. The expectation is
# that maintainers will be trusted to make the right decisions for their
# subsystem and this will be rare.
#
# ## Responsibilities
#
# Subsystem maintainership is a voluntary role and maintainers are not
# expected to dedicate any amount of time to the project. However, if a
# maintainer is inactive for a long period of time, they may be removed from
# the maintainers list to avoid bitrot or outdated information.
#
# Maintainers are expected to be exemplary members of the community and
# should be respectful, helpful, and professional in all interactions.
# This is both in regards to the community at large as well as other
# subsystem maintainers as well as @mitchellh.
#
# As technical leaders, maintainers are expected to be mindful about
# breaking changes, performance, user impact, and other technical
# considerations in their subsystem. They should be considerate of large
# changes and should be able to justify their decisions.
#
# Notably, maintainers have NO OBLIGATION to review pull requests or issues
# in their subsystem. They have full discretion to review or not review
# anything they want. This isn't a job! It is a role of trust and authority
# and the expectation is that maintainers will use their best judgement.
#
# ## Becoming a Maintainer
#
# Maintainer candidates are noticed and proposed by the community. Anyone
# may propose themselves or someone else as a maintainer. The BDFL along
# with existing maintainers will discuss and decide.
#
# Generally, we want to see consistent high quality contributions to a
# specific subsystem before considering someone as a maintainer. There isn't
# an exact number of contributions or time period required but generally
# we're looking for an order of a dozen or more contributions over a period of
# months, at least.
#
# # Subsystem List
#
# The subsystems don't fully cover the entirety of the Ghostty project but
# are created organically as experts in certain areas emerge. If you feel
# you are an expert in a certain area and would like to be a maintainer,
# please reach out to @mitchellh on Discord.
#
# (Alphabetical order)
#
# - @ghostty-org/font - All things font related including discovery,
# rasterization, shaping, coloring, etc.
#
# - @ghostty-org/gtk - Anything GTK-related in the project, primarily
# the GTK apprt. Also includes X11/Wayland integrations and general
# Linux support.
#
# - @ghostty-org/macos - The Ghostty macOS app and any macOS-specific
# features, configurations, etc.
#
# - @ghostty-org/renderer - Ghostty rendering subsystem, including the
# rendering abstractions as well as specific renderers like OpenGL
# and Metal.
#
# - @ghostty-org/shell - Ghostty shell integration, including shell
# completions, shell detection, and any other shell interactions.
#
# - @ghostty-org/terminal - The terminal emulator subsystem, including
# subprocess management and pty handling, escape sequence parsing,
# key encoding, etc.
#
# ## Outside of Ghostty
#
# Other "subsystems" exist outside of Ghostty and will not be represented
# in this CODEOWNERS file:
#
# - @ghostty-org/discord-bot - Maintainers of the Ghostty Discord bot.
#
# - @ghostty-org/website - Maintainers of the Ghostty website.
# Font
/src/font/ @ghostty-org/font
/pkg/fontconfig/ @ghostty-org/font
/pkg/freetype/ @ghostty-org/font
/pkg/harfbuzz/ @ghostty-org/font
# GTK
/src/apprt/gtk/ @ghostty-org/gtk
/src/os/cgroup.zig @ghostty-org/gtk
/src/os/flatpak.zig @ghostty-org/gtk
/dist/linux/ @ghostty-org/gtk
# macOS
#
# This includes libghostty because the macOS apprt is built on top of
# libghostty and often requires or is impacted by changes to libghostty.
# macOS subsystem maintainers are expected to only work on libghostty
# insofar as it impacts the macOS apprt.
/include/ghostty.h @ghostty-org/macos
/src/apprt/embedded.zig @ghostty-org/macos
/src/os/cf_release_thread.zig @ghostty-org/macos
/src/os/macos.zig @ghostty-org/macos
/macos/ @ghostty-org/macos
/dist/macos/ @ghostty-org/macos
/pkg/apple-sdk/ @ghostty-org/macos
/pkg/macos/ @ghostty-org/macos
# Renderer
/src/renderer.zig @ghostty-org/renderer
/src/renderer/ @ghostty-org/renderer
/pkg/glslang/ @ghostty-org/renderer
/pkg/opengl/ @ghostty-org/renderer
/pkg/spirv-cross/ @ghostty-org/renderer
/pkg/wuffs/ @ghostty-org/renderer
# Shell
/src/shell-integration/ @ghostty-org/shell
/src/termio/shell-integration.zig @ghostty-org/shell
# Terminal
/src/simd/ @ghostty-org/terminal
/src/terminal/ @ghostty-org/terminal
/src/terminfo/ @ghostty-org/terminal
/src/unicode/ @ghostty-org/terminal
/src/Surface.zig @ghostty-org/terminal
/src/surface_mouse.zig @ghostty-org/terminal

View File

@@ -77,3 +77,183 @@ pull request will be accepted with a high degree of certainty.
> **Pull requests are NOT a place to discuss feature design.** Please do
> not open a WIP pull request to discuss a feature. Instead, use a discussion
> and link to your branch.
# Developer Guide
> [!NOTE]
>
> **The remainder of this file is dedicated to developers actively
> working on Ghostty.** If you're a user reporting an issue, you can
> ignore the rest of this document.
## Input Stack Testing
The input stack is the part of the codebase that starts with a
key event and ends with text encoding being sent to the pty (it
does not include _rendering_ the text, which is part of the
font or rendering stack).
If you modify any part of the input stack, you must manually verify
all the following input cases work properly. We unfortunately do
not automate this in any way, but if we can do that one day that'd
save a LOT of grief and time.
Note: this list may not be exhaustive, I'm still working on it.
### Linux IME
IME (Input Method Editors) are a common source of bugs in the input stack,
especially on Linux since there are multiple different IME systems
interacting with different windowing systems and application frameworks
all written by different organizations.
The following matrix should be tested to ensure that all IME input works
properly:
1. Wayland, X11
2. ibus, fcitx, none
3. Dead key input (e.g. Spanish), CJK (e.g. Japanese), Emoji, Unicode Hex
4. ibus versions: 1.5.29, 1.5.30, 1.5.31 (each exhibit slightly different behaviors)
> [!NOTE]
>
> This is a **work in progress**. I'm still working on this list and it
> is not complete. As I find more test cases, I will add them here.
#### Dead Key Input
Set your keyboard layout to "Spanish" (or another layout that uses dead keys).
1. Launch Ghostty
2. Press `'`
3. Press `a`
4. Verify that `á` is displayed
Note that the dead key may or may not show a preedit state visually.
For ibus and fcitx it does but for the "none" case it does not. Importantly,
the text should be correct when it is sent to the pty.
We should also test canceling dead key input:
1. Launch Ghostty
2. Press `'`
3. Press escape
4. Press `a`
5. Verify that `a` is displayed (no diacritic)
#### CJK Input
Configure fcitx or ibus with a keyboard layout like Japanese or Mozc. The
exact layout doesn't matter.
1. Launch Ghostty
2. Press `Ctrl+Shift` to switch to "Hiragana"
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
4. Press `Enter`
5. Verify that `こん` is displayed in the terminal.
We should also test switching input methods while preedit is active, which
should commit the text:
1. Launch Ghostty
2. Press `Ctrl+Shift` to switch to "Hiragana"
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
4. Press `Ctrl+Shift` to switch to another layout (any)
5. Verify that `こん` is displayed in the terminal as committed text.
## Nix Virtual Machines
Several Nix virtual machine definitions are provided by the project for testing
and developing Ghostty against multiple different Linux desktop environments.
Running these requires a working Nix installation, either Nix on your
favorite Linux distribution, NixOS, or macOS with nix-darwin installed. Further
requirements for macOS are detailed below.
VMs should only be run on your local desktop and then powered off when not in
use, which will discard any changes to the VM.
The VM definitions provide minimal software "out of the box" but additional
software can be installed by using standard Nix mechanisms like `nix run nixpkgs#<package>`.
### Linux
1. Check out the Ghostty source and change to the directory.
2. Run `nix run .#<vmtype>`. `<vmtype>` can be any of the VMs defined in the
`nix/vm` directory (without the `.nix` suffix) excluding any file prefixed
with `common` or `create`.
3. The VM will build and then launch. Depending on the speed of your system, this
can take a while, but eventually you should get a new VM window.
4. The Ghostty source directory should be mounted to `/tmp/shared` in the VM. Depending
on what UID and GID of the user that you launched the VM as, `/tmp/shared` _may_ be
writable by the VM user, so be careful!
### macOS
1. To run the VMs on macOS you will need to enable the Linux builder in your `nix-darwin`
config. This _should_ be as simple as adding `nix.linux-builder.enable=true` to your
configuration and then rebuilding. See [this](https://nixcademy.com/posts/macos-linux-builder/)
blog post for more information about the Linux builder and how to tune the performance.
2. Once the Linux builder has been enabled, you should be able to follow the Linux instructions
above to launch a VM.
### Custom VMs
To easily create a custom VM without modifying the Ghostty source, create a new
directory, then create a file called `flake.nix` with the following text in the
new directory.
```
{
inputs = {
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
ghostty.url = "github:ghostty-org/ghostty";
};
outputs = {
nixpkgs,
ghostty,
...
}: {
nixosConfigurations.custom-vm = ghostty.create-gnome-vm {
nixpkgs = nixpkgs;
system = "x86_64-linux";
overlay = ghostty.overlays.releasefast;
# module = ./configuration.nix # also works
module = {pkgs, ...}: {
environment.systemPackages = [
pkgs.btop
];
};
};
};
}
```
The custom VM can then be run with a command like this:
```
nix run .#nixosConfigurations.custom-vm.config.system.build.vm
```
A file named `ghostty.qcow2` will be created that is used to persist any changes
made in the VM. To "reset" the VM to default delete the file and it will be
recreated the next time you run the VM.
### Contributing new VM definitions
#### VM Acceptance Criteria
We welcome the contribution of new VM definitions, as long as they meet the following criteria:
1. The should be different enough from existing VM definitions that they represent a distinct
user (and developer) experience.
2. There's a significant Ghostty user population that uses a similar environment.
3. The VMs can be built using only packages from the current stable NixOS release.
#### VM Definition Criteria
1. VMs should be as minimal as possible so that they build and launch quickly.
Additional software can be added at runtime with a command like `nix run nixpkgs#<package name>`.
2. VMs should not expose any services to the network, or run any remote access
software like SSH daemons, VNC or RDP.
3. VMs should auto-login using the "ghostty" user.

View File

@@ -117,3 +117,11 @@ relevant to package maintainers:
often necessary for system packages to specify a specific minimum Linux
version, glibc, etc. Run `zig targets` to a get a full list of available
targets.
> [!WARNING]
>
> **The GLFW runtime is not meant for distribution.** The GLFW runtime
> (`-Dapp-runtime=glfw`) is meant for development and testing only. It is
> missing many features, has known memory leak scenarios, known crashes,
> and more. Please do not package the GLFW-based Ghostty runtime for
> distribution.

1838
build.zig

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,26 @@
.{
.name = "ghostty",
.version = "1.0.1",
.version = "1.1.2",
.paths = .{""},
.dependencies = .{
// Zig libs
.libxev = .{
.url = "https://github.com/mitchellh/libxev/archive/db6a52bafadf00360e675fefa7926e8e6c0e9931.tar.gz",
.hash = "12206029de146b685739f69b10a6f08baee86b3d0a5f9a659fa2b2b66c9602078bbf",
.url = "https://github.com/mitchellh/libxev/archive/31eed4e337fed7b0149319e5cdbb62b848c24fbd.tar.gz",
.hash = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c",
},
.mach_glfw = .{
.url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz",
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
.lazy = true,
},
.vaxis = .{
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
},
.z2d = .{
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
},
.zig_objc = .{
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
@@ -25,6 +33,18 @@
.url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
},
.zig_wayland = .{
.url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz",
.hash = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38",
},
.zf = .{
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
},
.gobject = .{
.url = "https://github.com/ianprime0509/zig-gobject/releases/download/v0.2.2/bindings-gnome47.tar.zst",
.hash = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d",
},
// C libs
.cimgui = .{ .path = "./pkg/cimgui" },
@@ -46,23 +66,25 @@
.glslang = .{ .path = "./pkg/glslang" },
.spirv_cross = .{ .path = "./pkg/spirv-cross" },
// Wayland
.wayland = .{
.url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz",
.hash = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f",
},
.wayland_protocols = .{
.url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
.hash = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef",
},
.plasma_wayland_protocols = .{
.url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86",
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
},
// Other
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
.iterm2_themes = .{
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/e030599a6a6e19fcd1ea047c7714021170129d56.tar.gz",
.hash = "1220cc25b537556a42b0948437c791214c229efb78b551c80b1e9b18d70bf0498620",
},
.vaxis = .{
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
},
.zf = .{
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
},
.z2d = .{
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz",
.hash = "12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3",
},
},
}

390
build.zig.zon.nix generated Normal file
View File

@@ -0,0 +1,390 @@
# generated by zon2nix (https://github.com/Cloudef/zig2nix)
{
lib,
linkFarm,
fetchurl,
fetchgit,
runCommandLocal,
zig,
name ? "zig-packages",
}:
with builtins;
with lib; let
unpackZigArtifact = {
name,
artifact,
}:
runCommandLocal name
{
nativeBuildInputs = [zig];
}
''
hash="$(zig fetch --global-cache-dir "$TMPDIR" ${artifact})"
mv "$TMPDIR/p/$hash" "$out"
chmod 755 "$out"
'';
fetchZig = {
name,
url,
hash,
}: let
artifact = fetchurl {inherit url hash;};
in
unpackZigArtifact {inherit name artifact;};
fetchGitZig = {
name,
url,
hash,
}: let
parts = splitString "#" url;
url_base = elemAt parts 0;
url_without_query = elemAt (splitString "?" url_base) 0;
rev_base = elemAt parts 1;
rev =
if match "^[a-fA-F0-9]{40}$" rev_base != null
then rev_base
else "refs/heads/${rev_base}";
in
fetchgit {
inherit name rev hash;
url = url_without_query;
deepClone = false;
};
fetchZigArtifact = {
name,
url,
hash,
}: let
parts = splitString "://" url;
proto = elemAt parts 0;
path = elemAt parts 1;
fetcher = {
"git+http" = fetchGitZig {
inherit name hash;
url = "http://${path}";
};
"git+https" = fetchGitZig {
inherit name hash;
url = "https://${path}";
};
http = fetchZig {
inherit name hash;
url = "http://${path}";
};
https = fetchZig {
inherit name hash;
url = "https://${path}";
};
};
in
fetcher.${proto};
in
linkFarm name [
{
name = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c";
path = fetchZigArtifact {
name = "libxev";
url = "https://github.com/mitchellh/libxev/archive/31eed4e337fed7b0149319e5cdbb62b848c24fbd.tar.gz";
hash = "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8=";
};
}
{
name = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62";
path = fetchZigArtifact {
name = "mach_glfw";
url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz";
hash = "sha256-HhXIvWUS8/CHWY4VXPG2ZEo+we8XOn3o5rYJCQ1n8Nk=";
};
}
{
name = "1220736fa4ba211162c7a0e46cc8fe04d95921927688bff64ab5da7420d098a7272d";
path = fetchZigArtifact {
name = "glfw";
url = "https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz";
hash = "sha256-IeBVAOQmtyFqVxzuXPek1onuPwIamcOyYtxqKpPEQjU=";
};
}
{
name = "12202adbfecdad671d585c9a5bfcbd5cdf821726779430047742ce1bf94ad67d19cb";
path = fetchZigArtifact {
name = "xcode_frameworks";
url = "https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz";
hash = "sha256-mP/I2coL57UJm/3+4Q8sPAgQwk8V4zM+S4VBBTrX2To=";
};
}
{
name = "122004bfd4c519dadfb8e6281a42fc34fd1aa15aea654ea8a492839046f9894fa2cf";
path = fetchZigArtifact {
name = "vulkan_headers";
url = "https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz";
hash = "sha256-K+zrRudgHFukOM6En1StRYRMNYkeRk+qHTXvrXaG+FU=";
};
}
{
name = "1220b3164434d2ec9db146a40bf3a30f490590d68fa8529776a3138074f0da2c11ca";
path = fetchZigArtifact {
name = "wayland_headers";
url = "https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz";
hash = "sha256-uFilLZinKkZt6RdVTV3lUmJpzpswDdFva22FvwU/XQI=";
};
}
{
name = "122089c326186c84aa2fd034b16abc38f3ebf4862d9ae106dc1847ac44f557b36465";
path = fetchZigArtifact {
name = "x11_headers";
url = "https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz";
hash = "sha256-EhV2bmTY/OMYN1wEul35gD0hQgS/Al262jO3pVr0O+c=";
};
}
{
name = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f";
path = fetchZigArtifact {
name = "vaxis";
url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b";
hash = "sha256-fFf79fCy4QQFVNcN722tSMjB6FyVEzCB36oH1olk9JQ=";
};
}
{
name = "1220dd654ef941fc76fd96f9ec6adadf83f69b9887a0d3f4ee5ac0a1a3e11be35cf5";
path = fetchZigArtifact {
name = "zigimg";
url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e";
hash = "sha256-oLf3YH3yeg4ikVO/GahMCDRMTU31AHkfSnF4rt7xTKo=";
};
}
{
name = "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40";
path = fetchZigArtifact {
name = "zg";
url = "https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz";
hash = "sha256-2x9hT7bYq9KJYWLVOf21a+QvTG/F7HWT+YK15IMRzNY=";
};
}
{
name = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a";
path = fetchZigArtifact {
name = "z2d";
url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a";
hash = "sha256-YpWXn1J3JKQSCrWB25mAfzd1/T56QstEZnhPzBwxgoM=";
};
}
{
name = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634";
path = fetchZigArtifact {
name = "zig_objc";
url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz";
hash = "sha256-H+HIbh2T23uzrsg9/1/vl9Ir1HCAa2pzeTx6zktJH9Q=";
};
}
{
name = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc";
path = fetchZigArtifact {
name = "zig_js";
url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz";
hash = "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0=";
};
}
{
name = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25";
path = fetchZigArtifact {
name = "ziglyph";
url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz";
hash = "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k=";
};
}
{
name = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38";
path = fetchZigArtifact {
name = "zig_wayland";
url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz";
hash = "sha256-RtAystqK/GRYIquTK1KfD7rRSCrfuzAvCD1Z9DE1ldc=";
};
}
{
name = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8";
path = fetchZigArtifact {
name = "zf";
url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd";
hash = "sha256-t6QNrEJZ4GZZsYixjYvpdrYoCmNbG8TTUmGs2MFa4sU=";
};
}
{
name = "1220c72c1697dd9008461ead702997a15d8a1c5810247f02e7983b9f74c6c6e4c087";
path = fetchZigArtifact {
name = "vaxis";
url = "git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423";
hash = "sha256-QWN4jOrA91KlbqmeEHHJ4HTnCC9nmfxt8DHUXJpAzLI=";
};
}
{
name = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d";
path = fetchZigArtifact {
name = "gobject";
url = "https://github.com/ianprime0509/zig-gobject/releases/download/v0.2.2/bindings-gnome47.tar.zst";
hash = "sha256-UU97kNv/bZzQPKz1djhEDLapLguvfBpFfWVb6FthtcI=";
};
}
{
name = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f";
path = fetchZigArtifact {
name = "wayland";
url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz";
hash = "sha256-m9G72jdG30KH2yQhNBcBJ46OnekzuX0i2uIOyczkpLk=";
};
}
{
name = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef";
path = fetchZigArtifact {
name = "wayland_protocols";
url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz";
hash = "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=";
};
}
{
name = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566";
path = fetchZigArtifact {
name = "plasma_wayland_protocols";
url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86";
hash = "sha256-iWRv3+OfmHxmeCJ8m0ChjgZX6mwXlcFmK8P/Vt8gDj4=";
};
}
{
name = "12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3";
path = fetchZigArtifact {
name = "iterm2_themes";
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz";
hash = "sha256-Iyf7U4rpvNkPX4AOEbYSYGte5+SjRwsWD2luOn1Hz8U=";
};
}
{
name = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402";
path = fetchZigArtifact {
name = "imgui";
url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz";
hash = "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA=";
};
}
{
name = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d";
path = fetchZigArtifact {
name = "freetype";
url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz";
hash = "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw=";
};
}
{
name = "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66";
path = fetchZigArtifact {
name = "libpng";
url = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.43.tar.gz";
hash = "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo=";
};
}
{
name = "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb";
path = fetchZigArtifact {
name = "zlib";
url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz";
hash = "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw=";
};
}
{
name = "12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7";
path = fetchZigArtifact {
name = "fontconfig";
url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz";
hash = "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU=";
};
}
{
name = "122032442d95c3b428ae8e526017fad881e7dc78eab4d558e9a58a80bfbd65a64f7d";
path = fetchZigArtifact {
name = "libxml2";
url = "https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz";
hash = "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU=";
};
}
{
name = "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122";
path = fetchZigArtifact {
name = "harfbuzz";
url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.4.0.tar.gz";
hash = "sha256-nxygiYE7BZRK0c6MfgGCEwJtNdybq0gKIeuHaDg5ZVY=";
};
}
{
name = "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b";
path = fetchZigArtifact {
name = "highway";
url = "https://github.com/google/highway/archive/refs/tags/1.1.0.tar.gz";
hash = "sha256-NUqLRTm1iOcLmOxwhEJz4/J0EwLEw3e8xOgbPRhm98k=";
};
}
{
name = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb";
path = fetchZigArtifact {
name = "oniguruma";
url = "https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz";
hash = "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA=";
};
}
{
name = "1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e";
path = fetchZigArtifact {
name = "sentry";
url = "https://github.com/getsentry/sentry-native/archive/refs/tags/0.7.8.tar.gz";
hash = "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8=";
};
}
{
name = "12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea";
path = fetchZigArtifact {
name = "breakpad";
url = "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz";
hash = "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk=";
};
}
{
name = "1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641";
path = fetchZigArtifact {
name = "utfcpp";
url = "https://github.com/nemtrif/utfcpp/archive/refs/tags/v4.0.5.tar.gz";
hash = "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8=";
};
}
{
name = "122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd";
path = fetchZigArtifact {
name = "wuffs";
url = "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.9.tar.gz";
hash = "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=";
};
}
{
name = "12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806";
path = fetchZigArtifact {
name = "pixels";
url = "git+https://github.com/make-github-pseudonymous-again/pixels?ref=main#d843c2714d32e15b48b8d7eeb480295af537f877";
hash = "sha256-kXYGO0qn2PfyOYCrRA49BHIgTzdmKhI8SNO1ZKfUYEE=";
};
}
{
name = "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1";
path = fetchZigArtifact {
name = "glslang";
url = "https://github.com/KhronosGroup/glslang/archive/refs/tags/14.2.0.tar.gz";
hash = "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U=";
};
}
{
name = "1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da";
path = fetchZigArtifact {
name = "spirv_cross";
url = "https://github.com/KhronosGroup/SPIRV-Cross/archive/476f384eb7d9e48613c45179e502a15ab95b6b49.tar.gz";
hash = "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M=";
};
}
]

View File

@@ -1,12 +0,0 @@
//! Reverse Index (RI) - ESC M
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("A\nB\nC", .{});
try stdout.print("\x1BM", .{});
try stdout.print("D\n\n", .{});
// const stdin = std.io.getStdIn().reader();
// _ = try stdin.readByte();
}

View File

@@ -1,23 +0,0 @@
//! Reverse Index (RI) - ESC M
//! Case: test that if the cursor is at the top, it scrolls down.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("A\nB\n\n", .{});
try stdout.print("\x1B[H", .{}); // Top-left
try stdout.print("\x1BM", .{}); // Reverse-Index
try stdout.print("D", .{});
try stdout.print("\x0D", .{}); // CR
try stdout.print("\x0A", .{}); // LF
try stdout.print("\x1B[H", .{}); // Top-left
try stdout.print("\x1BM", .{}); // Reverse-Index
try stdout.print("E", .{});
try stdout.print("\n", .{});
// const stdin = std.io.getStdIn().reader();
// _ = try stdin.readByte();
}

View File

@@ -1,99 +0,0 @@
//! Outputs various box glyphs for testing.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Box Drawing
{
try stdout.print("\x1b[4mBox Drawing\x1b[0m\n", .{});
var i: usize = 0x2500;
const step: usize = 32;
while (i <= 0x257F) : (i += step) {
var j: usize = 0;
while (j < step) : (j += 1) {
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
}
try stdout.print("\n\n", .{});
}
}
// Block Elements
{
try stdout.print("\x1b[4mBlock Elements\x1b[0m\n", .{});
var i: usize = 0x2580;
const step: usize = 32;
while (i <= 0x259f) : (i += step) {
var j: usize = 0;
while (j < step) : (j += 1) {
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
}
try stdout.print("\n\n", .{});
}
}
// Braille Elements
{
try stdout.print("\x1b[4mBraille\x1b[0m\n", .{});
var i: usize = 0x2800;
const step: usize = 32;
while (i <= 0x28FF) : (i += step) {
var j: usize = 0;
while (j < step) : (j += 1) {
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
}
try stdout.print("\n\n", .{});
}
}
{
try stdout.print("\x1b[4mSextants\x1b[0m\n", .{});
var i: usize = 0x1FB00;
const step: usize = 32;
const end = 0x1FB3B;
while (i <= end) : (i += step) {
var j: usize = 0;
while (j < step) : (j += 1) {
const v = i + j;
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
}
try stdout.print("\n\n", .{});
}
}
{
try stdout.print("\x1b[4mWedge Triangles\x1b[0m\n", .{});
var i: usize = 0x1FB3C;
const step: usize = 32;
const end = 0x1FB6B;
while (i <= end) : (i += step) {
var j: usize = 0;
while (j < step) : (j += 1) {
const v = i + j;
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
}
try stdout.print("\n\n", .{});
}
}
{
try stdout.print("\x1b[4mOther\x1b[0m\n", .{});
var i: usize = 0x1FB70;
const step: usize = 32;
const end = 0x1FB8B;
while (i <= end) : (i += step) {
var j: usize = 0;
while (j < step) : (j += 1) {
const v = i + j;
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
}
try stdout.print("\n\n", .{});
}
}
}

View File

@@ -1,15 +0,0 @@
//! Set Top and Bottom Margins (DECSTBM) - ESC [ r
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("A\nB\nC\nD", .{});
try stdout.print("\x1B[1;3r", .{}); // cursor up
try stdout.print("\x1B[1;1H", .{}); // top-left
try stdout.print("\x1B[M", .{}); // delete line
try stdout.print("E\n", .{});
try stdout.print("\x1B[7;1H", .{}); // cursor up
// const stdin = std.io.getStdIn().reader();
// _ = try stdin.readByte();
}

View File

@@ -1,14 +0,0 @@
//! Delete Line (DL) - Esc [ M
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("A\nB\nC\nD", .{});
try stdout.print("\x1B[2A", .{}); // cursor up
try stdout.print("\x1B[M", .{});
try stdout.print("E\n", .{});
try stdout.print("\x1B[B", .{});
// const stdin = std.io.getStdIn().reader();
// _ = try stdin.readByte();
}

View File

@@ -1,17 +0,0 @@
//! Insert Line (IL) - Esc [ L
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("\x1B[2J", .{}); // clear screen
try stdout.print("\x1B[1;1H", .{}); // set cursor position
try stdout.print("A\nB\nC\nD\nE", .{});
try stdout.print("\x1B[1;2r", .{}); // set scroll region
try stdout.print("\x1B[1;1H", .{}); // set cursor position
try stdout.print("\x1B[1L", .{}); // insert lines
try stdout.print("X", .{});
try stdout.print("\x1B[7;1H", .{}); // set cursor position
// const stdin = std.io.getStdIn().reader();
// _ = try stdin.readByte();
}

View File

@@ -1,10 +0,0 @@
//! DECALN - ESC # 8
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("\x1B#8", .{});
// const stdin = std.io.getStdIn().reader();
// _ = try stdin.readByte();
}

13
default.nix Normal file
View File

@@ -0,0 +1,13 @@
(import (
let
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
nodeName = lock.nodes.root.inputs.flake-compat;
in
fetchTarball {
url =
lock.nodes.${nodeName}.locked.url
or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
sha256 = lock.nodes.${nodeName}.locked.narHash;
}
) {src = ./.;})
.defaultNix

View File

@@ -7,6 +7,7 @@ Icon=com.mitchellh.ghostty
Categories=System;TerminalEmulator;
Keywords=terminal;tty;pty;
StartupNotify=true
StartupWMClass=com.mitchellh.ghostty
Terminal=false
Actions=new-window;
X-GNOME-UsesNotifications=true

0
dist/linux/ghostty_dolphin.desktop vendored Normal file → Executable file
View File

97
dist/linux/ghostty_nautilus.py vendored Normal file
View File

@@ -0,0 +1,97 @@
# Adapted from wezterm: https://github.com/wez/wezterm/blob/main/assets/wezterm-nautilus.py
# original copyright notice:
#
# Copyright (C) 2022 Sebastian Wiesner <sebastian@swsnr.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from os.path import isdir
from gi import require_version
from gi.repository import Nautilus, GObject, Gio, GLib
class OpenInGhosttyAction(GObject.GObject, Nautilus.MenuProvider):
def __init__(self):
super().__init__()
session = Gio.bus_get_sync(Gio.BusType.SESSION, None)
self._systemd = None
# Check if the this system runs under systemd, per sd_booted(3)
if isdir('/run/systemd/system/'):
self._systemd = Gio.DBusProxy.new_sync(session,
Gio.DBusProxyFlags.NONE,
None,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", None)
def _open_terminal(self, path):
cmd = ['ghostty', f'--working-directory={path}', '--gtk-single-instance=false']
child = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.NONE)
if self._systemd:
# Move new terminal into a dedicated systemd scope to make systemd
# track the terminal separately; in particular this makes systemd
# keep a separate CPU and memory account for the terminal which in turn
# ensures that oomd doesn't take nautilus down if a process in
# ghostty consumes a lot of memory.
pid = int(child.get_identifier())
props = [("PIDs", GLib.Variant('au', [pid])),
('CollectMode', GLib.Variant('s', 'inactive-or-failed'))]
name = 'app-nautilus-com.mitchellh.ghostty-{}.scope'.format(pid)
args = GLib.Variant('(ssa(sv)a(sa(sv)))', (name, 'fail', props, []))
self._systemd.call_sync('StartTransientUnit', args,
Gio.DBusCallFlags.NO_AUTO_START, 500, None)
def _menu_item_activated(self, _menu, paths):
for path in paths:
self._open_terminal(path)
def _make_item(self, name, paths):
item = Nautilus.MenuItem(name=name, label='Open in Ghostty',
icon='com.mitchellh.ghostty')
item.connect('activate', self._menu_item_activated, paths)
return item
def _paths_to_open(self, files):
paths = []
for file in files:
location = file.get_location() if file.is_directory() else file.get_parent_location()
path = location.get_path()
if path and path not in paths:
paths.append(path)
if 10 < len(paths):
# Let's not open anything if the user selected a lot of directories,
# to avoid accidentally spamming their desktop with dozends of
# new windows or tabs. Ten is a totally arbitrary limit :)
return []
else:
return paths
def get_file_items(self, *args):
# Nautilus 3.0 API passes args (window, files), 4.0 API just passes files
files = args[0] if len(args) == 1 else args[1]
paths = self._paths_to_open(files)
if paths:
return [self._make_item(name='GhosttyNautilus::open_in_ghostty', paths=paths)]
else:
return []
def get_background_items(self, *args):
# Nautilus 3.0 API passes args (window, file), 4.0 API just passes file
file = args[0] if len(args) == 1 else args[1]
paths = self._paths_to_open([file])
if paths:
return [self._make_item(name='GhosttyNautilus::open_folder_in_ghostty', paths=paths)]
else:
return []

View File

@@ -21,6 +21,7 @@ from datetime import datetime, timezone
now = datetime.now(timezone.utc)
version = os.environ["GHOSTTY_VERSION"]
version_dash = version.replace('.', '-')
build = os.environ["GHOSTTY_BUILD"]
commit = os.environ["GHOSTTY_COMMIT"]
commit_long = os.environ["GHOSTTY_COMMIT_LONG"]
@@ -82,6 +83,8 @@ elem = ET.SubElement(item, "sparkle:shortVersionString")
elem.text = f"{version}"
elem = ET.SubElement(item, "sparkle:minimumSystemVersion")
elem.text = "13.0.0"
elem = ET.SubElement(item, "sparkle:fullReleaseNotesLink")
elem.text = f"https://ghostty.org/docs/install/release-notes/{version_dash}"
elem = ET.SubElement(item, "description")
elem.text = f"""
<h1>Ghostty v{version}</h1>
@@ -91,8 +94,8 @@ on {now.strftime('%Y-%m-%d')}.
</p>
<p>
We don't currently generate release notes for auto-updates.
You can view the complete changelog and release notes on
the <a href="https://ghostty.org">Ghostty website</a>.
You can view the complete changelog and release notes
at <a href="https://ghostty.org/docs/install/release-notes/{version_dash}">ghostty.org/docs/install/release-notes/{version_dash}</a>.
</p>
"""
elem = ET.SubElement(item, "enclosure")

62
flake.lock generated
View File

@@ -3,11 +3,11 @@
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"lastModified": 1733328505,
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github"
},
"original": {
@@ -21,11 +21,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
@@ -36,11 +36,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1733423277,
"narHash": "sha256-TxabjxEgkNbCGFRHgM/b9yZWlBj60gUOUnRT/wbVQR8=",
"lastModified": 1738255539,
"narHash": "sha256-hP2eOqhIO/OILW+3moNWO4GtdJFYCqAe9yJZgvlCoDQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "e36963a147267afc055f7cf65225958633e536bf",
"rev": "c3511a3b53b482aa7547c9d1626fd7310c1de1c5",
"type": "github"
},
"original": {
@@ -52,11 +52,11 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1733229606,
"narHash": "sha256-FLYY5M0rpa5C2QAE3CKLYAM6TwbKicdRK6qNrSHlNrE=",
"lastModified": 1738136902,
"narHash": "sha256-pUvLijVGARw4u793APze3j6mU1Zwdtz7hGkGGkD87qw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "566e53c2ad750c84f6d31f9ccb9d00f823165550",
"rev": "9a5db3142ce450045840cc8d832b13b8a2018e0c",
"type": "github"
},
"original": {
@@ -69,9 +69,11 @@
"root": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"nixpkgs-stable": "nixpkgs-stable",
"nixpkgs-unstable": "nixpkgs-unstable",
"zig": "zig"
"zig": "zig",
"zig2nix": "zig2nix"
}
},
"systems": {
@@ -92,17 +94,19 @@
"zig": {
"inputs": {
"flake-compat": [],
"flake-utils": "flake-utils",
"flake-utils": [
"flake-utils"
],
"nixpkgs": [
"nixpkgs-stable"
]
},
"locked": {
"lastModified": 1717848532,
"narHash": "sha256-d+xIUvSTreHl8pAmU1fnmkfDTGQYCn2Rb/zOwByxS2M=",
"lastModified": 1738239110,
"narHash": "sha256-Y5i9mQ++dyIQr+zEPNy+KIbc5wjPmfllBrag3cHZgcE=",
"owner": "mitchellh",
"repo": "zig-overlay",
"rev": "02fc5cc555fc14fda40c42d7c3250efa43812b43",
"rev": "1a8fb6f3a04724519436355564b95fce5e272504",
"type": "github"
},
"original": {
@@ -110,6 +114,30 @@
"repo": "zig-overlay",
"type": "github"
}
},
"zig2nix": {
"inputs": {
"flake-utils": [
"flake-utils"
],
"nixpkgs": [
"nixpkgs-stable"
]
},
"locked": {
"lastModified": 1738263917,
"narHash": "sha256-j/3fwe2pEOquHabP/puljOKwAZFjIE9gXZqA91sC48M=",
"owner": "jcollie",
"repo": "zig2nix",
"rev": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a",
"type": "github"
},
"original": {
"owner": "jcollie",
"ref": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a",
"repo": "zig2nix",
"type": "github"
}
}
},
"root": "root",

View File

@@ -8,6 +8,7 @@
# glibc versions used by our dependencies from Nix are compatible with the
# system glibc that the user is building for.
nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.11";
flake-utils.url = "github:numtide/flake-utils";
# Used for shell.nix
flake-compat = {
@@ -19,9 +20,18 @@
url = "github:mitchellh/zig-overlay";
inputs = {
nixpkgs.follows = "nixpkgs-stable";
flake-utils.follows = "flake-utils";
flake-compat.follows = "";
};
};
zig2nix = {
url = "github:jcollie/zig2nix?ref=c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a";
inputs = {
nixpkgs.follows = "nixpkgs-stable";
flake-utils.follows = "flake-utils";
};
};
};
outputs = {
@@ -29,15 +39,19 @@
nixpkgs-unstable,
nixpkgs-stable,
zig,
zig2nix,
...
}:
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (builtins.map (system: let
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (
builtins.map (
system: let
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
in {
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
zig = zig.packages.${system}."0.13.0";
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
zig2nix = zig2nix;
};
packages.${system} = let
@@ -47,6 +61,7 @@
revision = self.shortRev or self.dirtyShortRev or "dirty";
};
in rec {
deps = pkgs-stable.callPackage ./build.zig.zon.nix {};
ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug");
ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
@@ -57,12 +72,53 @@
formatter.${system} = pkgs-stable.alejandra;
# Our supported systems are the same supported systems as the Zig binaries.
}) (builtins.attrNames zig.packages))
// {
overlays.default = final: prev: {
ghostty = self.packages.${prev.system}.default;
apps.${system} = let
runVM = (
module: let
vm = import ./nix/vm/create.nix {
inherit system module;
nixpkgs = nixpkgs-stable;
overlay = self.overlays.debug;
};
program = pkgs-stable.writeShellScript "run-ghostty-vm" ''
SHARED_DIR=$(pwd)
export SHARED_DIR
${pkgs-stable.lib.getExe vm.config.system.build.vm} "$@"
'';
in {
type = "app";
program = "${program}";
}
);
in {
wayland-cinnamon = runVM ./nix/vm/wayland-cinnamon.nix;
wayland-gnome = runVM ./nix/vm/wayland-gnome.nix;
wayland-plasma6 = runVM ./nix/vm/wayland-plasma6.nix;
x11-cinnamon = runVM ./nix/vm/x11-cinnamon.nix;
x11-gnome = runVM ./nix/vm/x11-gnome.nix;
x11-plasma6 = runVM ./nix/vm/x11-plasma6.nix;
x11-xfce = runVM ./nix/vm/x11-xfce.nix;
};
}
# Our supported systems are the same supported systems as the Zig binaries.
) (builtins.attrNames zig.packages)
)
// {
overlays = {
default = self.overlays.releasefast;
releasefast = final: prev: {
ghostty = self.packages.${prev.system}.ghostty-releasefast;
};
debug = final: prev: {
ghostty = self.packages.${prev.system}.ghostty-debug;
};
};
create-vm = import ./nix/vm/create.nix;
create-cinnamon-vm = import ./nix/vm/create-cinnamon.nix;
create-gnome-vm = import ./nix/vm/create-gnome.nix;
create-plasma6-vm = import ./nix/vm/create-plasma6.nix;
create-xfce-vm = import ./nix/vm/create-xfce.nix;
};
nixConfig = {

View File

@@ -159,7 +159,7 @@ typedef enum {
GHOSTTY_KEY_EQUAL,
GHOSTTY_KEY_LEFT_BRACKET, // [
GHOSTTY_KEY_RIGHT_BRACKET, // ]
GHOSTTY_KEY_BACKSLASH, // /
GHOSTTY_KEY_BACKSLASH, // \
// control
GHOSTTY_KEY_UP,
@@ -375,9 +375,9 @@ typedef enum {
typedef enum {
GHOSTTY_GOTO_SPLIT_PREVIOUS,
GHOSTTY_GOTO_SPLIT_NEXT,
GHOSTTY_GOTO_SPLIT_TOP,
GHOSTTY_GOTO_SPLIT_UP,
GHOSTTY_GOTO_SPLIT_LEFT,
GHOSTTY_GOTO_SPLIT_BOTTOM,
GHOSTTY_GOTO_SPLIT_DOWN,
GHOSTTY_GOTO_SPLIT_RIGHT,
} ghostty_action_goto_split_e;
@@ -559,10 +559,13 @@ typedef struct {
// apprt.Action.Key
typedef enum {
GHOSTTY_ACTION_QUIT,
GHOSTTY_ACTION_NEW_WINDOW,
GHOSTTY_ACTION_NEW_TAB,
GHOSTTY_ACTION_CLOSE_TAB,
GHOSTTY_ACTION_NEW_SPLIT,
GHOSTTY_ACTION_CLOSE_ALL_WINDOWS,
GHOSTTY_ACTION_TOGGLE_MAXIMIZE,
GHOSTTY_ACTION_TOGGLE_FULLSCREEN,
GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW,
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
@@ -641,7 +644,7 @@ typedef void (*ghostty_runtime_write_clipboard_cb)(void*,
ghostty_clipboard_e,
bool);
typedef void (*ghostty_runtime_close_surface_cb)(void*, bool);
typedef void (*ghostty_runtime_action_cb)(ghostty_app_t,
typedef bool (*ghostty_runtime_action_cb)(ghostty_app_t,
ghostty_target_s,
ghostty_action_s);
@@ -681,10 +684,11 @@ void ghostty_config_open();
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
ghostty_config_t);
void ghostty_app_free(ghostty_app_t);
bool ghostty_app_tick(ghostty_app_t);
void ghostty_app_tick(ghostty_app_t);
void* ghostty_app_userdata(ghostty_app_t);
void ghostty_app_set_focus(ghostty_app_t, bool);
bool ghostty_app_key(ghostty_app_t, ghostty_input_key_s);
bool ghostty_app_key_is_binding(ghostty_app_t, ghostty_input_key_s);
void ghostty_app_keyboard_changed(ghostty_app_t);
void ghostty_app_open_config(ghostty_app_t);
void ghostty_app_update_config(ghostty_app_t, ghostty_config_t);
@@ -712,7 +716,8 @@ void ghostty_surface_set_color_scheme(ghostty_surface_t,
ghostty_color_scheme_e);
ghostty_input_mods_e ghostty_surface_key_translation_mods(ghostty_surface_t,
ghostty_input_mods_e);
void ghostty_surface_key(ghostty_surface_t, ghostty_input_key_s);
bool ghostty_surface_key(ghostty_surface_t, ghostty_input_key_s);
bool ghostty_surface_key_is_binding(ghostty_surface_t, ghostty_input_key_s);
void ghostty_surface_text(ghostty_surface_t, const char*, uintptr_t);
bool ghostty_surface_mouse_captured(ghostty_surface_t);
bool ghostty_surface_mouse_button(ghostty_surface_t,

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "macOS-AppIcon-1024px.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 KiB

View File

@@ -10,8 +10,8 @@
29C15B1D2CDC3B2900520DD4 /* bat in Resources */ = {isa = PBXBuildFile; fileRef = 29C15B1C2CDC3B2000520DD4 /* bat */; };
55154BE02B33911F001622DC /* ghostty in Resources */ = {isa = PBXBuildFile; fileRef = 55154BDF2B33911F001622DC /* ghostty */; };
552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; };
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
A514C8D72B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
A514C8D82B54DC6800493A16 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
@@ -69,8 +69,13 @@
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3C92D4445E20033CF96 /* Dock.swift */; };
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */; };
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */; };
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */; };
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378D2D31D6C100931030 /* Weak.swift */; };
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
A5CBD0592C9F37B10017A1AE /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
@@ -87,6 +92,8 @@
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.swift */; };
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */; };
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
A5CF66D42D289CEE00139794 /* NSEvent+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CF66D32D289CEA00139794 /* NSEvent+Extension.swift */; };
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */; };
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */; };
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3C2B37804400D21823 /* CodableBridge.swift */; };
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */; };
@@ -99,6 +106,7 @@
C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EA62B738B9900404083 /* NSView+Extension.swift */; };
C1F26EE92B76CBFC00404083 /* VibrantLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EE82B76CBFC00404083 /* VibrantLayer.m */; };
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */; };
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */ = {isa = PBXBuildFile; fileRef = FC5218F92D10FFC7004C93E0 /* zsh */; };
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */ = {isa = PBXBuildFile; fileRef = FC9ABA9B2D0F538D0020D4C8 /* bash-completion */; };
/* End PBXBuildFile section */
@@ -108,8 +116,8 @@
3B39CAA42B33949B00DABEB8 /* GhosttyReleaseLocal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GhosttyReleaseLocal.entitlements; sourceTree = "<group>"; };
55154BDF2B33911F001622DC /* ghostty */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ghostty; path = "../zig-out/share/ghostty"; sourceTree = "<group>"; };
552964E52B34A9B400030505 /* vim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/vim"; sourceTree = "<group>"; };
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Config.swift; sourceTree = "<group>"; };
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; };
A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; };
@@ -158,10 +166,15 @@
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
A5A2A3C92D4445E20033CF96 /* Dock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dock.swift; sourceTree = "<group>"; };
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+Extension.swift"; sourceTree = "<group>"; };
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastWindowPosition.swift; sourceTree = "<group>"; };
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayout.swift; sourceTree = "<group>"; };
A5CA378D2D31D6C100931030 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = "<group>"; };
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableWindowView.swift; sourceTree = "<group>"; };
A5CBD0572C9F30860017A1AE /* Cursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cursor.swift; sourceTree = "<group>"; };
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuickTerminal.xib; sourceTree = "<group>"; };
@@ -177,6 +190,8 @@
A5CEAFDB29B8009000646FDA /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = "<group>"; };
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Divider.swift; sourceTree = "<group>"; };
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
A5CF66D32D289CEA00139794 /* NSEvent+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSEvent+Extension.swift"; sourceTree = "<group>"; };
A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Event.swift; sourceTree = "<group>"; };
A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalRestorable.swift; sourceTree = "<group>"; };
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableBridge.swift; sourceTree = "<group>"; };
A5D4499D2B53AE7B000F5B83 /* Ghostty-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ghostty-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -192,6 +207,7 @@
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VibrantLayer.h; sourceTree = "<group>"; };
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VibrantLayer.m; sourceTree = "<group>"; };
C1F26EEA2B76CC2400404083 /* ghostty-bridging-header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ghostty-bridging-header.h"; sourceTree = "<group>"; };
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalSpaceBehavior.swift; sourceTree = "<group>"; };
FC5218F92D10FFC7004C93E0 /* zsh */ = {isa = PBXFileReference; lastKnownFileType = folder; name = zsh; path = "../zig-out/share/zsh"; sourceTree = "<group>"; };
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "bash-completion"; path = "../zig-out/share/bash-completion"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -256,23 +272,28 @@
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
isa = PBXGroup;
children = (
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */,
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
A5CEAFFE29C2410700646FDA /* Backport.swift */,
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
A5A2A3C92D4445E20033CF96 /* Dock.swift */,
A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */,
A59630962AEE163600D64628 /* HostingWindow.swift */,
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */,
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */,
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */,
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
A5CA378D2D31D6C100931030 /* Weak.swift */,
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
A5CEAFDA29B8005900646FDA /* SplitView */,
@@ -351,12 +372,14 @@
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */,
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */,
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */,
A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */,
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
A59630A12AF0415000D64628 /* Ghostty.TerminalSplit.swift */,
A55685DF29A03A9F004303CE /* AppError.swift */,
A52FFF5A2CAA54A8000C6A5B /* FullscreenMode+Extension.swift */,
A5CF66D32D289CEA00139794 /* NSEvent+Extension.swift */,
);
path = Ghostty;
sourceTree = "<group>";
@@ -399,13 +422,13 @@
children = (
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */,
29C15B1C2CDC3B2000520DD4 /* bat */,
55154BDF2B33911F001622DC /* ghostty */,
552964E52B34A9B400030505 /* vim */,
A586167B2B7703CC009BDB1D /* fish */,
55154BDF2B33911F001622DC /* ghostty */,
A5985CE52C33060F00C57AD3 /* man */,
A5A1F8842A489D6800D1E8BC /* terminfo */,
FC5218F92D10FFC7004C93E0 /* zsh */,
9351BE8E2D22937F003B3499 /* nvim */,
A5A1F8842A489D6800D1E8BC /* terminfo */,
552964E52B34A9B400030505 /* vim */,
FC5218F92D10FFC7004C93E0 /* zsh */,
);
name = Resources;
sourceTree = "<group>";
@@ -439,6 +462,7 @@
children = (
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */,
A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */,
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */,
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
@@ -602,21 +626,26 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */,
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */,
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
A5CF66D42D289CEE00139794 /* NSEvent+Extension.swift in Sources */,
A5CBD0642CA122E70017A1AE /* QuickTerminalPosition.swift in Sources */,
A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */,
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */,
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */,
@@ -632,12 +661,14 @@
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */,
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */,
A59630A22AF0415000D64628 /* Ghostty.TerminalSplit.swift in Sources */,
A5FEB3002ABB69450068369E /* main.swift in Sources */,
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
@@ -647,6 +678,7 @@
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */,
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */,
A54B0CEF2D0D2E2800CBEFF8 /* ColorizedGhosttyIconImage.swift in Sources */,
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
@@ -765,21 +797,22 @@
INFOPLIST_FILE = "Ghostty-Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
INFOPLIST_KEY_NSCameraUsageDescription = "A program running within Ghostty would like to use the camera.";
INFOPLIST_KEY_NSContactsUsageDescription = "A program running within Ghostty would like to access your Contacts.";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
INFOPLIST_KEY_NSMainNibFile = MainMenu;
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
@@ -934,21 +967,22 @@
INFOPLIST_FILE = "Ghostty-Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
INFOPLIST_KEY_NSCameraUsageDescription = "A program running within Ghostty would like to use the camera.";
INFOPLIST_KEY_NSContactsUsageDescription = "A program running within Ghostty would like to access your Contacts.";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
INFOPLIST_KEY_NSMainNibFile = MainMenu;
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
@@ -987,21 +1021,22 @@
INFOPLIST_FILE = "Ghostty-Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
INFOPLIST_KEY_NSCameraUsageDescription = "A program running within Ghostty would like to use the camera.";
INFOPLIST_KEY_NSContactsUsageDescription = "A program running within Ghostty would like to access your Contacts.";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
INFOPLIST_KEY_NSMainNibFile = MainMenu;
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",

View File

@@ -6,8 +6,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/sparkle-project/Sparkle",
"state" : {
"revision" : "b456fd404954a9e13f55aa0c88cd5a40b8399638",
"version" : "2.6.3"
"revision" : "0ef1ee0220239b3776f433314515fd849025673f",
"version" : "2.6.4"
}
}
],

View File

@@ -30,11 +30,13 @@ class AppDelegate: NSObject,
@IBOutlet private var menuSplitRight: NSMenuItem?
@IBOutlet private var menuSplitDown: NSMenuItem?
@IBOutlet private var menuClose: NSMenuItem?
@IBOutlet private var menuCloseTab: NSMenuItem?
@IBOutlet private var menuCloseWindow: NSMenuItem?
@IBOutlet private var menuCloseAllWindows: NSMenuItem?
@IBOutlet private var menuCopy: NSMenuItem?
@IBOutlet private var menuPaste: NSMenuItem?
@IBOutlet private var menuPasteSelection: NSMenuItem?
@IBOutlet private var menuSelectAll: NSMenuItem?
@IBOutlet private var menuToggleVisibility: NSMenuItem?
@@ -90,10 +92,8 @@ class AppDelegate: NSObject,
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
}
/// Tracks whether the application is currently visible. This can be gamed, i.e. if a user manually
/// brings each window one by one to the front. But at worst its off by one set of toggles and this
/// makes our logic very easy.
private var isVisible: Bool = true
/// Tracks the windows that we hid for toggleVisibility.
private var hiddenState: ToggleVisibilityState? = nil
/// The observer for the app appearance.
private var appearanceObserver: NSKeyValueObservation? = nil
@@ -217,7 +217,11 @@ class AppDelegate: NSObject,
}
func applicationDidBecomeActive(_ notification: Notification) {
guard !applicationHasBecomeActive else { return }
// If we're back manually then clear the hidden state because macOS handles it.
self.hiddenState = nil
// First launch stuff
if (!applicationHasBecomeActive) {
applicationHasBecomeActive = true
// Let's launch our first window. We only do this if we have no other windows. It
@@ -228,6 +232,7 @@ class AppDelegate: NSObject,
terminalManager.newWindow()
}
}
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return derivedConfig.shouldQuitAfterLastWindowClosed
@@ -240,7 +245,13 @@ class AppDelegate: NSObject,
// This probably isn't fully safe. The isEmpty check above is aspirational, it doesn't
// quite work with SwiftUI because windows are retained on close. So instead we check
// if there are any that are visible. I'm guessing this breaks under certain scenarios.
if (windows.allSatisfy { !$0.isVisible }) { return .terminateNow }
//
// NOTE(mitchellh): I don't think we need this check at all anymore. I'm keeping it
// here because I don't want to remove it in a patch release cycle but we should
// target removing it soon.
if (self.quickController == nil && windows.allSatisfy { !$0.isVisible }) {
return .terminateNow
}
// If the user is shutting down, restarting, or logging out, we don't confirm quit.
why: if let event = NSAppleEventManager.shared().currentAppleEvent {
@@ -346,6 +357,7 @@ class AppDelegate: NSObject,
syncMenuShortcut(config, action: "new_window", menuItem: self.menuNewWindow)
syncMenuShortcut(config, action: "new_tab", menuItem: self.menuNewTab)
syncMenuShortcut(config, action: "close_surface", menuItem: self.menuClose)
syncMenuShortcut(config, action: "close_tab", menuItem: self.menuCloseTab)
syncMenuShortcut(config, action: "close_window", menuItem: self.menuCloseWindow)
syncMenuShortcut(config, action: "close_all_windows", menuItem: self.menuCloseAllWindows)
syncMenuShortcut(config, action: "new_split:right", menuItem: self.menuSplitRight)
@@ -353,13 +365,14 @@ class AppDelegate: NSObject,
syncMenuShortcut(config, action: "copy_to_clipboard", menuItem: self.menuCopy)
syncMenuShortcut(config, action: "paste_from_clipboard", menuItem: self.menuPaste)
syncMenuShortcut(config, action: "paste_from_selection", menuItem: self.menuPasteSelection)
syncMenuShortcut(config, action: "select_all", menuItem: self.menuSelectAll)
syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit)
syncMenuShortcut(config, action: "goto_split:previous", menuItem: self.menuPreviousSplit)
syncMenuShortcut(config, action: "goto_split:next", menuItem: self.menuNextSplit)
syncMenuShortcut(config, action: "goto_split:top", menuItem: self.menuSelectSplitAbove)
syncMenuShortcut(config, action: "goto_split:bottom", menuItem: self.menuSelectSplitBelow)
syncMenuShortcut(config, action: "goto_split:up", menuItem: self.menuSelectSplitAbove)
syncMenuShortcut(config, action: "goto_split:down", menuItem: self.menuSelectSplitBelow)
syncMenuShortcut(config, action: "goto_split:left", menuItem: self.menuSelectSplitLeft)
syncMenuShortcut(config, action: "goto_split:right", menuItem: self.menuSelectSplitRight)
syncMenuShortcut(config, action: "resize_split:up,10", menuItem: self.menuMoveSplitDividerUp)
@@ -425,6 +438,22 @@ class AppDelegate: NSObject,
// because we let it capture and propagate.
guard NSApp.mainWindow == nil else { return event }
// If this event as-is would result in a key binding then we send it.
if let app = ghostty.app,
ghostty_app_key_is_binding(
app,
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) {
// If the key was handled by Ghostty we stop the event chain. If
// the key wasn't handled then we let it fall through and continue
// processing. This is important because some bindings may have no
// affect at this scope.
if (ghostty_app_key(
app,
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) {
return nil
}
}
// If this event would be handled by our menu then we do nothing.
if let mainMenu = NSApp.mainMenu,
mainMenu.performKeyEquivalent(with: event) {
@@ -438,13 +467,7 @@ class AppDelegate: NSObject,
guard let ghostty = self.ghostty.app else { return event }
// Build our event input and call ghostty
var key_ev = ghostty_input_key_s()
key_ev.action = GHOSTTY_ACTION_PRESS
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
key_ev.keycode = UInt32(event.keyCode)
key_ev.text = nil
key_ev.composing = false
if (ghostty_app_key(ghostty, key_ev)) {
if (ghostty_app_key(ghostty, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) {
// The key was used so we want to stop it from going to our Mac app
Ghostty.logger.debug("local key event handled event=\(event)")
return nil
@@ -486,15 +509,16 @@ class AppDelegate: NSObject,
// Sync our auto-update settings. If SUEnableAutomaticChecks (in our Info.plist) is
// explicitly false (NO), auto-updates are disabled. Otherwise, we use the behavior
// defined by our "auto-update" configuration.
if Bundle.main.infoDictionary?["SUEnableAutomaticChecks"] as? Bool != false {
updaterController.updater.automaticallyChecksForUpdates =
config.autoUpdate == .check || config.autoUpdate == .download
updaterController.updater.automaticallyDownloadsUpdates =
config.autoUpdate == .download
} else {
// defined by our "auto-update" configuration (if set) or fall back to Sparkle
// user-based defaults.
if Bundle.main.infoDictionary?["SUEnableAutomaticChecks"] as? Bool == false {
updaterController.updater.automaticallyChecksForUpdates = false
updaterController.updater.automaticallyDownloadsUpdates = false
} else if let autoUpdate = config.autoUpdate {
updaterController.updater.automaticallyChecksForUpdates =
autoUpdate == .check || autoUpdate == .download
updaterController.updater.automaticallyDownloadsUpdates =
autoUpdate == .download
}
// Config could change keybindings, so update everything that depends on that
@@ -539,6 +563,30 @@ class AppDelegate: NSObject,
self.appIcon = nil
break
case .blueprint:
self.appIcon = NSImage(named: "BlueprintImage")!
case .chalkboard:
self.appIcon = NSImage(named: "ChalkboardImage")!
case .glass:
self.appIcon = NSImage(named: "GlassImage")!
case .holographic:
self.appIcon = NSImage(named: "HolographicImage")!
case .microchip:
self.appIcon = NSImage(named: "MicrochipImage")!
case .paper:
self.appIcon = NSImage(named: "PaperImage")!
case .retro:
self.appIcon = NSImage(named: "RetroImage")!
case .xray:
self.appIcon = NSImage(named: "XrayImage")!
case .customStyle:
guard let ghostColor = config.macosIconGhostColor else { break }
guard let screenColors = config.macosIconScreenColor else { break }
@@ -691,21 +739,27 @@ class AppDelegate: NSObject,
/// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application
@IBAction func toggleVisibility(_ sender: Any) {
// We only care about terminal windows.
for window in NSApp.windows.filter({ $0.windowController is BaseTerminalController }) {
if isVisible {
window.orderOut(nil)
} else {
window.makeKeyAndOrderFront(nil)
}
// If we have focus, then we hide all windows.
if NSApp.isActive {
// Toggle visibility doesn't do anything if the focused window is native
// fullscreen. This is only relevant if Ghostty is active.
guard let keyWindow = NSApp.keyWindow,
!keyWindow.styleMask.contains(.fullScreen) else { return }
// Keep track of our hidden state to restore properly
self.hiddenState = .init()
NSApp.hide(nil)
return
}
// After bringing them all to front we make sure our app is active too.
if !isVisible {
// If we're not active, we want to become active
NSApp.activate(ignoringOtherApps: true)
}
isVisible.toggle()
// Bring all windows to the front. Note: we don't use NSApp.unhide because
// that will unhide ALL hidden windows. We want to only bring forward the
// ones that we hid.
hiddenState?.restore()
hiddenState = nil
}
private struct DerivedConfig {
@@ -725,4 +779,33 @@ class AppDelegate: NSObject,
self.quickTerminalPosition = config.quickTerminalPosition
}
}
private struct ToggleVisibilityState {
let hiddenWindows: [Weak<NSWindow>]
let keyWindow: Weak<NSWindow>?
init() {
// We need to know the key window so that we can bring focus back to the
// right window if it was hidden.
self.keyWindow = if let keyWindow = NSApp.keyWindow {
.init(keyWindow)
} else {
nil
}
// We need to keep track of the windows that were visible because we only
// want to bring back these windows if we remove the toggle.
//
// We also ignore fullscreen windows because they don't hide anyways.
self.hiddenWindows = NSApp.windows.filter {
$0.isVisible &&
!$0.styleMask.contains(.fullScreen)
}.map { Weak($0) }
}
func restore() {
hiddenWindows.forEach { $0.value?.orderFrontRegardless() }
keyWindow?.value?.makeKey()
}
}
}

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -17,6 +17,7 @@
<outlet property="menuCheckForUpdates" destination="GEA-5y-yzH" id="0nV-Tf-nJQ"/>
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
<outlet property="menuCloseTab" destination="Obb-Mk-j8J" id="Gda-L0-gdz"/>
<outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/>
<outlet property="menuCopy" destination="Jqf-pv-Zcu" id="bKd-1C-oy9"/>
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
@@ -31,6 +32,7 @@
<outlet property="menuNextSplit" destination="bD7-ei-wKU" id="LeT-xw-eh4"/>
<outlet property="menuOpenConfig" destination="BOF-NM-1cW" id="Nze-Go-glw"/>
<outlet property="menuPaste" destination="i27-pK-umN" id="ICc-X2-gV3"/>
<outlet property="menuPasteSelection" destination="akq-ov-Jjh" id="GS8-aQ-hVw"/>
<outlet property="menuPreviousSplit" destination="Lic-px-1wg" id="Rto-CG-yRe"/>
<outlet property="menuQuickTerminal" destination="1pv-LF-NBJ" id="glN-5B-IGi"/>
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
@@ -154,6 +156,12 @@
<action selector="close:" target="-1" id="tTZ-2b-Mbm"/>
</connections>
</menuItem>
<menuItem title="Close Tab" id="Obb-Mk-j8J">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="closeTab:" target="-1" id="UBb-Bd-nkj"/>
</connections>
</menuItem>
<menuItem title="Close Window" id="W5w-UZ-crk">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
@@ -185,6 +193,12 @@
<action selector="paste:" target="-1" id="ZKe-2B-mel"/>
</connections>
</menuItem>
<menuItem title="Paste Selection" id="akq-ov-Jjh">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="pasteSelection:" target="-1" id="vo3-Rf-Udb"/>
</connections>
</menuItem>
<menuItem title="Select All" id="q2h-lq-e4r">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>

View File

@@ -3,6 +3,12 @@ import Cocoa
import SwiftUI
import GhosttyKit
// This is a Apple's private function that we need to call to get the active space.
@_silgen_name("CGSGetActiveSpace")
func CGSGetActiveSpace(_ cid: Int) -> size_t
@_silgen_name("CGSMainConnectionID")
func CGSMainConnectionID() -> Int
/// Controller for the "quick" terminal.
class QuickTerminalController: BaseTerminalController {
override var windowNibName: NSNib.Name? { "QuickTerminal" }
@@ -18,6 +24,12 @@ class QuickTerminalController: BaseTerminalController {
/// application to the front.
private var previousApp: NSRunningApplication? = nil
// The active space when the quick terminal was last shown.
private var previousActiveSpace: size_t = 0
/// Non-nil if we have hidden dock state.
private var hiddenDock: HiddenDock? = nil
/// The configuration derived from the Ghostty config so we don't need to rely on references.
private var derivedConfig: DerivedConfig
@@ -32,6 +44,11 @@ class QuickTerminalController: BaseTerminalController {
// Setup our notifications for behaviors
let center = NotificationCenter.default
center.addObserver(
self,
selector: #selector(applicationWillTerminate(_:)),
name: NSApplication.willTerminateNotification,
object: nil)
center.addObserver(
self,
selector: #selector(onToggleFullscreen),
@@ -52,6 +69,9 @@ class QuickTerminalController: BaseTerminalController {
// Remove all of our notificationcenter subscriptions
let center = NotificationCenter.default
center.removeObserver(self)
// Make sure we restore our hidden dock
hiddenDock = nil
}
// MARK: NSWindowController
@@ -87,6 +107,17 @@ class QuickTerminalController: BaseTerminalController {
// MARK: NSWindowDelegate
override func windowDidBecomeKey(_ notification: Notification) {
super.windowDidBecomeKey(notification)
// If we're not visible we don't care to run the logic below. It only
// applies if we can be seen.
guard visible else { return }
// Re-hide the dock if we were hiding it before.
hiddenDock?.hide()
}
override func windowDidResignKey(_ notification: Notification) {
super.windowDidResignKey(notification)
@@ -107,8 +138,32 @@ class QuickTerminalController: BaseTerminalController {
self.previousApp = nil
}
if (derivedConfig.quickTerminalAutoHide) {
// Regardless of autohide, we always want to bring the dock back
// when we lose focus.
hiddenDock?.restore()
if derivedConfig.quickTerminalAutoHide {
switch derivedConfig.quickTerminalSpaceBehavior {
case .remain:
// If we lose focus on the active space, then we can animate out
animateOut()
case .move:
let currentActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
if previousActiveSpace == currentActiveSpace {
// We haven't moved spaces. We lost focus to another app on the
// current space. Animate out.
animateOut()
} else {
// We've moved to a different space. Bring the quick terminal back
// into view.
DispatchQueue.main.async {
self.window?.makeKeyAndOrderFront(nil)
}
self.previousActiveSpace = currentActiveSpace
}
}
}
}
@@ -163,6 +218,9 @@ class QuickTerminalController: BaseTerminalController {
}
}
// Set previous active space
self.previousActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
// Animate the window in
animateWindowIn(window: window, from: position)
@@ -198,8 +256,29 @@ class QuickTerminalController: BaseTerminalController {
// Move our window off screen to the top
position.setInitial(in: window, on: screen)
// We need to set our window level to a high value. In testing, only
// popUpMenu and above do what we want. This gets it above the menu bar
// and lets us render off screen.
window.level = .popUpMenu
// Move it to the visible position since animation requires this
DispatchQueue.main.async {
window.makeKeyAndOrderFront(nil)
}
// If our dock position would conflict with our target location then
// we autohide the dock.
if position.conflictsWithDock(on: screen) {
if (hiddenDock == nil) {
hiddenDock = .init()
}
hiddenDock?.hide()
} else {
// Ensure we don't have any hidden dock if we don't conflict.
// The deinit will restore.
hiddenDock = nil
}
// Run the animation that moves our window into the proper place and makes
// it visible.
@@ -211,8 +290,16 @@ class QuickTerminalController: BaseTerminalController {
// There is a very minor delay here so waiting at least an event loop tick
// keeps us safe from the view not being on the window.
DispatchQueue.main.async {
// If we canceled our animation in we do nothing
guard self.visible else { return }
// If we canceled our animation clean up some state.
guard self.visible else {
self.hiddenDock = nil
return
}
// After animating in, we reset the window level to a value that
// is above other windows but not as high as popUpMenu. This allows
// things like IME dropdowns to appear properly.
window.level = .floating
// Now that the window is visible, sync our appearance. This function
// requires the window is visible.
@@ -276,6 +363,17 @@ class QuickTerminalController: BaseTerminalController {
}
private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
// If we hid the dock then we unhide it.
hiddenDock = nil
// If the window isn't on our active space then we don't animate, we just
// hide it.
if !window.isOnActiveSpace {
self.previousApp = nil
window.orderOut(self)
return
}
// We always animate out to whatever screen the window is actually on.
guard let screen = window.screen ?? NSScreen.main else { return }
@@ -297,6 +395,11 @@ class QuickTerminalController: BaseTerminalController {
}
}
// We need to set our window level to a high value. In testing, only
// popUpMenu and above do what we want. This gets it above the menu bar
// and lets us render off screen.
window.level = .popUpMenu
NSAnimationContext.runAnimationGroup({ context in
context.duration = derivedConfig.quickTerminalAnimationDuration
context.timingFunction = .init(name: .easeIn)
@@ -311,23 +414,13 @@ class QuickTerminalController: BaseTerminalController {
private func syncAppearance() {
guard let window else { return }
// Change the collection behavior of the window depending on the configuration.
window.collectionBehavior = derivedConfig.quickTerminalSpaceBehavior.collectionBehavior
// If our window is not visible, then no need to sync the appearance yet.
// Some APIs such as window blur have no effect unless the window is visible.
guard window.isVisible else { return }
// Terminals typically operate in sRGB color space and macOS defaults
// to "native" which is typically P3. There is a lot more resources
// covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376
// Ghostty defaults to sRGB but this can be overridden.
switch (self.derivedConfig.windowColorspace) {
case "display-p3":
window.colorSpace = .displayP3
case "srgb":
fallthrough
default:
window.colorSpace = .sRGB
}
// If we have window transparency then set it transparent. Otherwise set it opaque.
if (self.derivedConfig.backgroundOpacity < 1) {
window.isOpaque = false
@@ -368,6 +461,13 @@ class QuickTerminalController: BaseTerminalController {
// MARK: Notifications
@objc private func applicationWillTerminate(_ notification: Notification) {
// If the application is going to terminate we want to make sure we
// restore any global dock state. I think deinit should be called which
// would call this anyways but I can't be sure so I will do this too.
hiddenDock = nil
}
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
guard let target = notification.object as? Ghostty.SurfaceView else { return }
guard target == self.focusedSurface else { return }
@@ -396,14 +496,14 @@ class QuickTerminalController: BaseTerminalController {
let quickTerminalScreen: QuickTerminalScreen
let quickTerminalAnimationDuration: Double
let quickTerminalAutoHide: Bool
let windowColorspace: String
let quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior
let backgroundOpacity: Double
init() {
self.quickTerminalScreen = .main
self.quickTerminalAnimationDuration = 0.2
self.quickTerminalAutoHide = true
self.windowColorspace = ""
self.quickTerminalSpaceBehavior = .move
self.backgroundOpacity = 1.0
}
@@ -411,10 +511,39 @@ class QuickTerminalController: BaseTerminalController {
self.quickTerminalScreen = config.quickTerminalScreen
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
self.quickTerminalAutoHide = config.quickTerminalAutoHide
self.windowColorspace = config.windowColorspace
self.quickTerminalSpaceBehavior = config.quickTerminalSpaceBehavior
self.backgroundOpacity = config.backgroundOpacity
}
}
/// Hides the dock globally (not just NSApp). This is only used if the quick terminal is
/// in a conflicting position with the dock.
private class HiddenDock {
let previousAutoHide: Bool
private var hidden: Bool = false
init() {
previousAutoHide = Dock.autoHideEnabled
}
deinit {
restore()
}
func hide() {
guard !hidden else { return }
NSApp.acquirePresentationOption(.autoHideDock)
Dock.autoHideEnabled = true
hidden = true
}
func restore() {
guard hidden else { return }
NSApp.releasePresentationOption(.autoHideDock)
Dock.autoHideEnabled = previousAutoHide
hidden = false
}
}
}
extension Notification.Name {

View File

@@ -69,7 +69,7 @@ enum QuickTerminalPosition : String {
finalSize.width = screen.frame.width
case .left, .right:
finalSize.height = screen.frame.height
finalSize.height = screen.visibleFrame.height
case .center:
finalSize.width = screen.frame.width / 2
@@ -89,13 +89,13 @@ enum QuickTerminalPosition : String {
return .init(x: screen.frame.minX, y: -window.frame.height)
case .left:
return .init(x: -window.frame.width, y: 0)
return .init(x: screen.frame.minX-window.frame.width, y: 0)
case .right:
return .init(x: screen.frame.maxX, y: 0)
case .center:
return .init(x: (screen.visibleFrame.maxX - window.frame.width) / 2, y: screen.visibleFrame.maxY - window.frame.width)
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.height - window.frame.width)
}
}
@@ -115,7 +115,25 @@ enum QuickTerminalPosition : String {
return .init(x: screen.visibleFrame.maxX - window.frame.width, y: window.frame.origin.y)
case .center:
return .init(x: (screen.visibleFrame.maxX - window.frame.width) / 2, y: (screen.visibleFrame.maxY - window.frame.height) / 2)
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
}
}
func conflictsWithDock(on screen: NSScreen) -> Bool {
// Screen must have a dock for it to conflict
guard screen.hasDock else { return false }
// Get the dock orientation for this screen
guard let orientation = Dock.orientation else { return false }
// Depending on the orientation of the dock, we conflict if our quick terminal
// would potentially "hit" the dock. In the future we should probably consider
// the frame of the quick terminal.
return switch (orientation) {
case .top: self == .top || self == .left || self == .right
case .bottom: self == .bottom || self == .left || self == .right
case .left: self == .top || self == .bottom
case .right: self == .top || self == .bottom
}
}
}

View File

@@ -0,0 +1,36 @@
import Foundation
import Cocoa
enum QuickTerminalSpaceBehavior {
case remain
case move
init?(fromGhosttyConfig string: String) {
switch (string) {
case "move":
self = .move
case "remain":
self = .remain
default:
return nil
}
}
var collectionBehavior: NSWindow.CollectionBehavior {
let commonBehavior: [NSWindow.CollectionBehavior] = [
.ignoresCycle,
.fullScreenAuxiliary
]
switch (self) {
case .move:
// We want this to move the window to the active space.
return NSWindow.CollectionBehavior([.canJoinAllSpaces] + commonBehavior)
case .remain:
// We want this to remain the window in the current space.
return NSWindow.CollectionBehavior([.moveToActiveSpace] + commonBehavior)
}
}
}

View File

@@ -1,6 +1,6 @@
import Cocoa
class QuickTerminalWindow: NSWindow {
class QuickTerminalWindow: NSPanel {
// Both of these must be true for windows without decorations to be able to
// still become key/main and receive events.
override var canBecomeKey: Bool { return true }
@@ -26,22 +26,7 @@ class QuickTerminalWindow: NSWindow {
// window remains resizable.
self.styleMask.remove(.titled)
// We need to set our window level to a high value. In testing, only
// popUpMenu and above do what we want. This gets it above the menu bar
// and lets us render off screen.
self.level = .popUpMenu
// This plus the level above was what was needed for the animation to work,
// because it gets the window off screen properly. Plus we add some fields
// we just want the behavior of.
self.collectionBehavior = [
// We want this to be part of every space because it is a singleton.
.canJoinAllSpaces,
// We don't want to be part of command-tilde
.ignoresCycle,
// We never support fullscreen
.fullScreenNone]
// We don't want to activate the owning app when quick terminal is triggered.
self.styleMask.insert(.nonactivatingPanel)
}
}

View File

@@ -389,9 +389,9 @@ class BaseTerminalController: NSWindowController,
}
switch (request) {
case .osc_52_write:
case let .osc_52_write(pasteboard):
guard case .confirm = action else { break }
let pb = NSPasteboard.general
let pb = pasteboard ?? NSPasteboard.general
pb.declareTypes([.string], owner: nil)
pb.setString(cc.contents, forType: .string)
case .osc_52_read, .paste:
@@ -452,6 +452,7 @@ class BaseTerminalController: NSWindowController,
self.alert = nil
switch (response) {
case .alertFirstButtonReturn:
alert.window.orderOut(nil)
window.close()
default:
@@ -540,11 +541,11 @@ class BaseTerminalController: NSWindowController,
}
@IBAction func splitMoveFocusAbove(_ sender: Any) {
splitMoveFocus(direction: .top)
splitMoveFocus(direction: .up)
}
@IBAction func splitMoveFocusBelow(_ sender: Any) {
splitMoveFocus(direction: .bottom)
splitMoveFocus(direction: .down)
}
@IBAction func splitMoveFocusLeft(_ sender: Any) {

View File

@@ -22,7 +22,7 @@ class TerminalController: BaseTerminalController {
private var restorable: Bool = true
/// The configuration derived from the Ghostty config so we don't need to rely on references.
private var derivedConfig: DerivedConfig
private(set) var derivedConfig: DerivedConfig
/// The notification cancellable for focused surface property changes.
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
@@ -60,6 +60,11 @@ class TerminalController: BaseTerminalController {
selector: #selector(onGotoTab),
name: Ghostty.Notification.ghosttyGotoTab,
object: nil)
center.addObserver(
self,
selector: #selector(onCloseTab),
name: .ghosttyCloseTab,
object: nil)
center.addObserver(
self,
selector: #selector(ghosttyConfigDidChange(_:)),
@@ -101,6 +106,12 @@ class TerminalController: BaseTerminalController {
// When our fullscreen state changes, we resync our appearance because some
// properties change when fullscreen or not.
guard let focusedSurface else { return }
if (!(fullscreenStyle?.isFullscreen ?? false) &&
ghostty.config.macosTitlebarStyle == "hidden")
{
applyHiddenTitlebarStyle()
}
syncAppearance(focusedSurface.derivedConfig)
}
@@ -244,7 +255,9 @@ class TerminalController: BaseTerminalController {
let backgroundColor: OSColor
if let surfaceTree {
if let focusedSurface, surfaceTree.doesBorderTop(view: focusedSurface) {
backgroundColor = OSColor(focusedSurface.backgroundColor ?? surfaceConfig.backgroundColor).withAlphaComponent(0.0)
// Similar to above, an alpha component of "0" causes compositor issues, so
// we use 0.001. See: https://github.com/ghostty-org/ghostty/pull/4308
backgroundColor = OSColor(focusedSurface.backgroundColor ?? surfaceConfig.backgroundColor).withAlphaComponent(0.001)
} else {
// We don't have a focused surface or our surface doesn't border the
// top. We choose to match the color of the top-left most surface.
@@ -267,6 +280,31 @@ class TerminalController: BaseTerminalController {
}
}
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
guard let window else { return }
// If we don't have an X/Y then we try to use the previously saved window pos.
guard let x, let y else {
if (!LastWindowPosition.shared.restore(window)) {
window.center()
}
return
}
// Prefer the screen our window is being placed on otherwise our primary screen.
guard let screen = window.screen ?? NSScreen.screens.first else {
window.center()
return
}
// Orient based on the top left of the primary monitor
let frame = screen.visibleFrame
window.setFrameOrigin(.init(
x: frame.minX + CGFloat(x),
y: frame.maxY - (CGFloat(y) + window.frame.height)))
}
//MARK: - NSWindowController
override func windowWillLoad() {
@@ -274,6 +312,43 @@ class TerminalController: BaseTerminalController {
shouldCascadeWindows = false
}
fileprivate func applyHiddenTitlebarStyle() {
guard let window else { return }
window.styleMask = [
// We need `titled` in the mask to get the normal window frame
.titled,
// Full size content view so we can extend
// content in to the hidden titlebar's area
.fullSizeContentView,
.resizable,
.closable,
.miniaturizable,
]
// Hide the title
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
// Hide the traffic lights (window control buttons)
window.standardWindowButton(.closeButton)?.isHidden = true
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
window.standardWindowButton(.zoomButton)?.isHidden = true
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
window.tabbingMode = .disallowed
// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
// some operations that appear to bring back the titlebar visibility so this ensures
// it is gone forever.
if let themeFrame = window.contentView?.superview,
let titleBarContainer = themeFrame.firstDescendant(withClassName: "NSTitlebarContainerView") {
titleBarContainer.isHidden = true
}
}
override func windowDidLoad() {
super.windowDidLoad()
guard let window = window as? TerminalWindow else { return }
@@ -294,40 +369,41 @@ class TerminalController: BaseTerminalController {
// If window decorations are disabled, remove our title
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
// Terminals typically operate in sRGB color space and macOS defaults
// to "native" which is typically P3. There is a lot more resources
// covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376
// Ghostty defaults to sRGB but this can be overridden.
switch (config.windowColorspace) {
case "display-p3":
window.colorSpace = .displayP3
case "srgb":
fallthrough
default:
window.colorSpace = .sRGB
}
// If we have only a single surface (no splits) and that surface requested
// an initial size then we set it here now.
if case let .leaf(leaf) = surfaceTree {
if let initialSize = leaf.surface.initialSize,
let screen = window.screen ?? NSScreen.main {
// Setup our frame. We need to first subtract the views frame so that we can
// just get the chrome frame so that we only affect the surface view size.
// Get the current frame of the window
var frame = window.frame
frame.size.width -= leaf.surface.frame.size.width
frame.size.height -= leaf.surface.frame.size.height
frame.size.width += min(initialSize.width, screen.frame.width)
frame.size.height += min(initialSize.height, screen.frame.height)
// We have no tabs and we are not a split, so set the initial size of the window.
// Calculate the chrome size (window size minus view size)
let chromeWidth = frame.size.width - leaf.surface.frame.size.width
let chromeHeight = frame.size.height - leaf.surface.frame.size.height
// Calculate the new width and height, clamping to the screen's size
let newWidth = min(initialSize.width + chromeWidth, screen.visibleFrame.width)
let newHeight = min(initialSize.height + chromeHeight, screen.visibleFrame.height)
// Update the frame size while keeping the window's position intact
frame.size.width = newWidth
frame.size.height = newHeight
// Ensure the window doesn't go outside the screen boundaries
frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth))
frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight))
// Set the updated frame to the window
window.setFrame(frame, display: true)
}
}
// Center the window to start, we'll move the window frame automatically
// when cascading.
window.center()
// Set our window positioning to coordinates if config value exists, otherwise
// fallback to original centering behavior
setInitialWindowPosition(
x: config.windowPositionX,
y: config.windowPositionY,
windowDecorations: config.windowDecorations)
// Make sure our theme is set on the window so styling is correct.
if let windowTheme = config.windowTheme {
@@ -365,38 +441,7 @@ class TerminalController: BaseTerminalController {
// If our titlebar style is "hidden" we adjust the style appropriately
if (config.macosTitlebarStyle == "hidden") {
window.styleMask = [
// We need `titled` in the mask to get the normal window frame
.titled,
// Full size content view so we can extend
// content in to the hidden titlebar's area
.fullSizeContentView,
.resizable,
.closable,
.miniaturizable,
]
// Hide the title
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
// Hide the traffic lights (window control buttons)
window.standardWindowButton(.closeButton)?.isHidden = true
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
window.standardWindowButton(.zoomButton)?.isHidden = true
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
window.tabbingMode = .disallowed
// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
// some operations that appear to bring back the titlebar visibility so this ensures
// it is gone forever.
if let themeFrame = window.contentView?.superview,
let titleBarContainer = themeFrame.firstDescendant(withClassName: "NSTitlebarContainerView") {
titleBarContainer.isHidden = true
}
applyHiddenTitlebarStyle()
}
// In various situations, macOS automatically tabs new windows. Ghostty handles
@@ -448,6 +493,20 @@ class TerminalController: BaseTerminalController {
override func windowDidMove(_ notification: Notification) {
super.windowDidMove(notification)
self.fixTabBar()
// Whenever we move save our last position for the next start.
if let window {
LastWindowPosition.shared.save(window)
}
}
func windowDidBecomeMain(_ notification: Notification) {
// Whenever we get focused, use that as our last window position for
// restart. This differs from Terminal.app but matches iTerm2 behavior
// and I think its sensible.
if let window {
LastWindowPosition.shared.save(window)
}
}
// Called when the window will be encoded. We handle the data encoding here in the
@@ -469,7 +528,50 @@ class TerminalController: BaseTerminalController {
ghostty.newTab(surface: surface)
}
@IBAction override func closeWindow(_ sender: Any) {
private func confirmClose(
window: NSWindow,
messageText: String,
informativeText: String,
completion: @escaping () -> Void
) {
// If we need confirmation by any, show one confirmation for all windows
// in the tab group.
let alert = NSAlert()
alert.messageText = messageText
alert.informativeText = informativeText
alert.addButton(withTitle: "Close")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
alert.beginSheetModal(for: window) { response in
if response == .alertFirstButtonReturn {
completion()
}
}
}
@IBAction func closeTab(_ sender: Any?) {
guard let window = window else { return }
guard window.tabGroup != nil else {
// No tabs, no tab group, just perform a normal close.
window.performClose(sender)
return
}
if surfaceTree?.needsConfirmQuit() ?? false {
confirmClose(
window: window,
messageText: "Close Tab?",
informativeText: "The terminal still has a running process. If you close the tab the process will be killed."
) {
window.close()
}
return
}
window.close()
}
@IBAction override func closeWindow(_ sender: Any?) {
guard let window = window else { return }
guard let tabGroup = window.tabGroup else {
// No tabs, no tab group, just perform a normal close.
@@ -484,47 +586,34 @@ class TerminalController: BaseTerminalController {
}
// Check if any windows require close confirmation.
var needsConfirm: Bool = false
for tabWindow in tabGroup.windows {
guard let c = tabWindow.windowController as? TerminalController else { continue }
if (c.surfaceTree?.needsConfirmQuit() ?? false) {
needsConfirm = true
break
let needsConfirm = tabGroup.windows.contains { tabWindow in
guard let controller = tabWindow.windowController as? TerminalController else {
return false
}
return controller.surfaceTree?.needsConfirmQuit() ?? false
}
// If none need confirmation then we can just close all the windows.
if (!needsConfirm) {
for tabWindow in tabGroup.windows {
tabWindow.close()
}
if !needsConfirm {
tabGroup.windows.forEach { $0.close() }
return
}
// If we need confirmation by any, show one confirmation for all windows
// in the tab group.
let alert = NSAlert()
alert.messageText = "Close Window?"
alert.informativeText = "All terminal sessions in this window will be terminated."
alert.addButton(withTitle: "Close Window")
alert.addButton(withTitle: "Cancel")
alert.alertStyle = .warning
alert.beginSheetModal(for: window, completionHandler: { response in
if (response == .alertFirstButtonReturn) {
for tabWindow in tabGroup.windows {
tabWindow.close()
confirmClose(
window: window,
messageText: "Close Window?",
informativeText: "All terminal sessions in this window will be terminated."
) {
tabGroup.windows.forEach { $0.close() }
}
}
})
}
@IBAction func toggleGhosttyFullScreen(_ sender: Any) {
@IBAction func toggleGhosttyFullScreen(_ sender: Any?) {
guard let surface = focusedSurface?.surface else { return }
ghostty.toggleFullscreen(surface: surface)
}
@IBAction func toggleTerminalInspector(_ sender: Any) {
@IBAction func toggleTerminalInspector(_ sender: Any?) {
guard let surface = focusedSurface?.surface else { return }
ghostty.toggleTerminalInspector(surface: surface)
}
@@ -620,13 +709,21 @@ class TerminalController: BaseTerminalController {
// If our index is the same we do nothing
guard finalIndex != selectedIndex else { return }
// Get our parent
let parent = tabbedWindows[finalIndex]
// Get our target window
let targetWindow = tabbedWindows[finalIndex]
// Move our current selected window to the proper index
// Begin a group of window operations to minimize visual updates
NSAnimationContext.beginGrouping()
NSAnimationContext.current.duration = 0
// Remove and re-add the window in the correct position
tabGroup.removeWindow(selectedWindow)
parent.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
selectedWindow.makeKeyAndOrderFront(nil)
targetWindow.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
// Ensure our window remains selected
selectedWindow.makeKey()
NSAnimationContext.endGrouping()
}
@objc private func onGotoTab(notification: SwiftUI.Notification) {
@@ -681,6 +778,12 @@ class TerminalController: BaseTerminalController {
targetWindow.makeKeyAndOrderFront(nil)
}
@objc private func onCloseTab(notification: SwiftUI.Notification) {
guard let target = notification.object as? Ghostty.SurfaceView else { return }
guard surfaceTree?.contains(view: target) ?? false else { return }
closeTab(self)
}
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
guard let target = notification.object as? Ghostty.SurfaceView else { return }
guard target == self.focusedSurface else { return }
@@ -698,7 +801,7 @@ class TerminalController: BaseTerminalController {
toggleFullscreen(mode: fullscreenMode)
}
private struct DerivedConfig {
struct DerivedConfig {
let backgroundColor: Color
let macosTitlebarStyle: String

View File

@@ -56,15 +56,10 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
// The title for our window
private var title: String {
var title = "👻"
if let surfaceTitle = surfaceTitle {
if (surfaceTitle.count > 0) {
title = surfaceTitle
if let surfaceTitle, !surfaceTitle.isEmpty {
return surfaceTitle
}
}
return title
return "👻"
}
// The pwd of the focused surface as a URL

View File

@@ -115,6 +115,21 @@ class TerminalWindow: NSWindow {
}
}
// We override this so that with the hidden titlebar style the titlebar
// area is not draggable.
override var contentLayoutRect: CGRect {
var rect = super.contentLayoutRect
// If we are using a hidden titlebar style, the content layout is the
// full frame making it so that it is not draggable.
if let controller = windowController as? TerminalController,
controller.derivedConfig.macosTitlebarStyle == "hidden" {
rect.origin.y = 0
rect.size.height = self.frame.height
}
return rect
}
// The window theme configuration from Ghostty. This is used to control some
// behaviors that don't look quite right in certain situations.
var windowTheme: TerminalWindowTheme?
@@ -667,12 +682,16 @@ fileprivate class WindowDragView: NSView {
// A view that matches the color of selected and unselected tabs in the adjacent tab bar.
fileprivate class WindowButtonsBackdropView: NSView {
private let terminalWindow: TerminalWindow
// This must be weak because the window has this view. Otherwise
// a retain cycle occurs.
private weak var terminalWindow: TerminalWindow?
private let isLightTheme: Bool
private let overlayLayer = VibrantLayer()
var isHighlighted: Bool = true {
didSet {
guard let terminalWindow else { return }
if isLightTheme {
overlayLayer.isHidden = isHighlighted
layer?.backgroundColor = .clear

View File

@@ -62,7 +62,7 @@ extension Ghostty {
// uses to interface with the application runtime environment.
var runtime_cfg = ghostty_runtime_config_s(
userdata: Unmanaged.passUnretained(self).toOpaque(),
supports_selection_clipboard: false,
supports_selection_clipboard: true,
wakeup_cb: { userdata in App.wakeup(userdata) },
action_cb: { app, target, action in App.action(app!, target: target, action: action) },
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
@@ -117,23 +117,7 @@ extension Ghostty {
func appTick() {
guard let app = self.app else { return }
// Tick our app, which lets us know if we want to quit
let exit = ghostty_app_tick(app)
if (!exit) { return }
// On iOS, applications do not terminate programmatically like they do
// on macOS. On iOS, applications are only terminated when a user physically
// closes the application (i.e. going to the home screen). If we request
// exit on iOS we ignore it.
#if os(iOS)
logger.info("quit request received, ignoring on iOS")
#endif
#if os(macOS)
// We want to quit, start that process
NSApplication.shared.terminate(nil)
#endif
ghostty_app_tick(app)
}
func openConfig() {
@@ -273,7 +257,7 @@ extension Ghostty {
// MARK: Ghostty Callbacks (iOS)
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {}
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool { return false }
static func readClipboard(
_ userdata: UnsafeMutableRawPointer?,
location: ghostty_clipboard_e,
@@ -336,13 +320,13 @@ extension Ghostty {
let surfaceView = self.surfaceUserdata(from: userdata)
guard let surface = surfaceView.surface else { return }
// We only support the standard clipboard
if (location != GHOSTTY_CLIPBOARD_STANDARD) {
// Get our pasteboard
guard let pasteboard = NSPasteboard.ghostty(location) else {
return completeClipboardRequest(surface, data: "", state: state)
}
// Get our string
let str = NSPasteboard.general.getOpinionatedStringContents() ?? ""
let str = pasteboard.getOpinionatedStringContents() ?? ""
completeClipboardRequest(surface, data: str, state: state)
}
@@ -380,14 +364,12 @@ extension Ghostty {
static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer<CChar>?, location: ghostty_clipboard_e, confirm: Bool) {
let surface = self.surfaceUserdata(from: userdata)
// We only support the standard clipboard
if (location != GHOSTTY_CLIPBOARD_STANDARD) { return }
guard let pasteboard = NSPasteboard.ghostty(location) else { return }
guard let valueStr = String(cString: string!, encoding: .utf8) else { return }
if !confirm {
let pb = NSPasteboard.general
pb.declareTypes([.string], owner: nil)
pb.setString(valueStr, forType: .string)
pasteboard.declareTypes([.string], owner: nil)
pasteboard.setString(valueStr, forType: .string)
return
}
@@ -396,7 +378,7 @@ extension Ghostty {
object: surface,
userInfo: [
Notification.ConfirmClipboardStrKey: valueStr,
Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write,
Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write(pasteboard),
]
)
}
@@ -441,7 +423,7 @@ extension Ghostty {
// MARK: Actions (macOS)
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool {
// Make sure it a target we understand so all our action handlers can assert
switch (target.tag) {
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
@@ -449,11 +431,14 @@ extension Ghostty {
default:
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
return
return false
}
// Action dispatch
switch (action.tag) {
case GHOSTTY_ACTION_QUIT:
quit(app)
case GHOSTTY_ACTION_NEW_WINDOW:
newWindow(app, target: target)
@@ -463,17 +448,20 @@ extension Ghostty {
case GHOSTTY_ACTION_NEW_SPLIT:
newSplit(app, target: target, direction: action.action.new_split)
case GHOSTTY_ACTION_CLOSE_TAB:
closeTab(app, target: target)
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
case GHOSTTY_ACTION_MOVE_TAB:
moveTab(app, target: target, move: action.action.move_tab)
return moveTab(app, target: target, move: action.action.move_tab)
case GHOSTTY_ACTION_GOTO_TAB:
gotoTab(app, target: target, tab: action.action.goto_tab)
return gotoTab(app, target: target, tab: action.action.goto_tab)
case GHOSTTY_ACTION_GOTO_SPLIT:
gotoSplit(app, target: target, direction: action.action.goto_split)
return gotoSplit(app, target: target, direction: action.action.goto_split)
case GHOSTTY_ACTION_RESIZE_SPLIT:
resizeSplit(app, target: target, resize: action.action.resize_split)
@@ -553,10 +541,30 @@ extension Ghostty {
fallthrough
case GHOSTTY_ACTION_QUIT_TIMER:
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
return false
default:
Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)")
return false
}
// If we reached here then we assume performed since all unknown actions
// are captured in the switch and return false.
return true
}
private static func quit(_ app: ghostty_app_t) {
// On iOS, applications do not terminate programmatically like they do
// on macOS. On iOS, applications are only terminated when a user physically
// closes the application (i.e. going to the home screen). If we request
// exit on iOS we ignore it.
#if os(iOS)
logger.info("quit request received, ignoring on iOS")
#endif
#if os(macOS)
// We want to quit, start that process
NSApplication.shared.terminate(nil)
#endif
}
private static func newWindow(_ app: ghostty_app_t, target: ghostty_target_s) {
@@ -651,6 +659,27 @@ extension Ghostty {
}
}
private static func closeTab(_ app: ghostty_app_t, target: ghostty_target_s) {
switch (target.tag) {
case GHOSTTY_TARGET_APP:
Ghostty.logger.warning("close tab does nothing with an app target")
return
case GHOSTTY_TARGET_SURFACE:
guard let surface = target.target.surface else { return }
guard let surfaceView = self.surfaceView(from: surface) else { return }
NotificationCenter.default.post(
name: .ghosttyCloseTab,
object: surfaceView
)
default:
assertionFailure()
}
}
private static func toggleFullscreen(
_ app: ghostty_app_t,
target: ghostty_target_s,
@@ -692,15 +721,19 @@ extension Ghostty {
private static func moveTab(
_ app: ghostty_app_t,
target: ghostty_target_s,
move: ghostty_action_move_tab_s) {
move: ghostty_action_move_tab_s) -> Bool {
switch (target.tag) {
case GHOSTTY_TARGET_APP:
Ghostty.logger.warning("move tab does nothing with an app target")
return
return false
case GHOSTTY_TARGET_SURFACE:
guard let surface = target.target.surface else { return }
guard let surfaceView = self.surfaceView(from: surface) else { return }
guard let surface = target.target.surface else { return false }
guard let surfaceView = self.surfaceView(from: surface) else { return false }
// See gotoTab for notes on this check.
guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false }
NotificationCenter.default.post(
name: .ghosttyMoveTab,
object: surfaceView,
@@ -712,20 +745,27 @@ extension Ghostty {
default:
assertionFailure()
}
return true
}
private static func gotoTab(
_ app: ghostty_app_t,
target: ghostty_target_s,
tab: ghostty_action_goto_tab_e) {
tab: ghostty_action_goto_tab_e) -> Bool {
switch (target.tag) {
case GHOSTTY_TARGET_APP:
Ghostty.logger.warning("goto tab does nothing with an app target")
return
return false
case GHOSTTY_TARGET_SURFACE:
guard let surface = target.target.surface else { return }
guard let surfaceView = self.surfaceView(from: surface) else { return }
guard let surface = target.target.surface else { return false }
guard let surfaceView = self.surfaceView(from: surface) else { return false }
// Similar to goto_split (see comment there) about our performability,
// we should make this more accurate later.
guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false }
NotificationCenter.default.post(
name: Notification.ghosttyGotoTab,
object: surfaceView,
@@ -737,20 +777,31 @@ extension Ghostty {
default:
assertionFailure()
}
return true
}
private static func gotoSplit(
_ app: ghostty_app_t,
target: ghostty_target_s,
direction: ghostty_action_goto_split_e) {
direction: ghostty_action_goto_split_e) -> Bool {
switch (target.tag) {
case GHOSTTY_TARGET_APP:
Ghostty.logger.warning("goto split does nothing with an app target")
return
return false
case GHOSTTY_TARGET_SURFACE:
guard let surface = target.target.surface else { return }
guard let surfaceView = self.surfaceView(from: surface) else { return }
guard let surface = target.target.surface else { return false }
guard let surfaceView = self.surfaceView(from: surface) else { return false }
guard let controller = surfaceView.window?.windowController as? BaseTerminalController else { return false }
// For now, we return false if the window has no splits and we return
// true if the window has ANY splits. This isn't strictly correct because
// we should only be returning true if we actually performed the action,
// but this handles the most common case of caring about goto_split performability
// which is the no-split case.
guard controller.surfaceTree?.isSplit ?? false else { return false }
NotificationCenter.default.post(
name: Notification.ghosttyFocusSplit,
object: surfaceView,
@@ -762,6 +813,8 @@ extension Ghostty {
default:
assertionFailure()
}
return true
}
private static func resizeSplit(

View File

@@ -132,15 +132,6 @@ extension Ghostty {
return v
}
var windowColorspace: String {
guard let config = self.config else { return "" }
var v: UnsafePointer<Int8>? = nil
let key = "window-colorspace"
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
guard let ptr = v else { return "" }
return String(cString: ptr)
}
var windowSaveState: String {
guard let config = self.config else { return "" }
var v: UnsafePointer<Int8>? = nil
@@ -150,6 +141,20 @@ extension Ghostty {
return String(cString: ptr)
}
var windowPositionX: Int16? {
guard let config = self.config else { return nil }
var v: Int16 = 0
let key = "window-position-x"
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
}
var windowPositionY: Int16? {
guard let config = self.config else { return nil }
var v: Int16 = 0
let key = "window-position-y"
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
}
var windowNewTabPosition: String {
guard let config = self.config else { return "" }
var v: UnsafePointer<Int8>? = nil
@@ -160,11 +165,14 @@ extension Ghostty {
}
var windowDecorations: Bool {
guard let config = self.config else { return true }
var v = false;
let defaultValue = true
guard let config = self.config else { return defaultValue }
var v: UnsafePointer<Int8>? = nil
let key = "window-decoration"
_ = ghostty_config_get(config, &v, key, UInt(key.count))
return v;
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
guard let ptr = v else { return defaultValue }
let str = String(cString: ptr)
return WindowDecoration(rawValue: str)?.enabled() ?? defaultValue
}
var windowTheme: String? {
@@ -331,7 +339,7 @@ extension Ghostty {
var backgroundBlurRadius: Int {
guard let config = self.config else { return 1 }
var v: Int = 0
let key = "background-blur-radius"
let key = "background-blur"
_ = ghostty_config_get(config, &v, key, UInt(key.count))
return v;
}
@@ -361,15 +369,26 @@ extension Ghostty {
)
}
// This isn't actually a configurable value currently but it could be done day.
// We put it here because it is a color that changes depending on the configuration.
var splitDividerColor: Color {
let backgroundColor = OSColor(backgroundColor)
let isLightBackground = backgroundColor.isLightColor
let newColor = isLightBackground ? backgroundColor.darken(by: 0.08) : backgroundColor.darken(by: 0.4)
guard let config = self.config else { return Color(newColor) }
var color: ghostty_config_color_s = .init();
let key = "split-divider-color"
if (!ghostty_config_get(config, &color, key, UInt(key.count))) {
return Color(newColor)
}
return .init(
red: Double(color.r) / 255,
green: Double(color.g) / 255,
blue: Double(color.b) / 255
)
}
#if canImport(AppKit)
var quickTerminalPosition: QuickTerminalPosition {
guard let config = self.config else { return .top }
@@ -406,6 +425,16 @@ extension Ghostty {
_ = ghostty_config_get(config, &v, key, UInt(key.count))
return v
}
var quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior {
guard let config = self.config else { return .move }
var v: UnsafePointer<Int8>? = nil
let key = "quick-terminal-space-behavior"
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .move }
guard let ptr = v else { return .move }
let str = String(cString: ptr)
return QuickTerminalSpaceBehavior(fromGhosttyConfig: str) ?? .move
}
#endif
var resizeOverlay: ResizeOverlay {
@@ -437,15 +466,14 @@ extension Ghostty {
return v;
}
var autoUpdate: AutoUpdate {
let defaultValue = AutoUpdate.check
guard let config = self.config else { return defaultValue }
var autoUpdate: AutoUpdate? {
guard let config = self.config else { return nil }
var v: UnsafePointer<Int8>? = nil
let key = "auto-update"
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
guard let ptr = v else { return defaultValue }
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return nil }
guard let ptr = v else { return nil }
let str = String(cString: ptr)
return AutoUpdate(rawValue: str) ?? defaultValue
return AutoUpdate(rawValue: str)
}
var autoUpdateChannel: AutoUpdateChannel {
@@ -529,4 +557,18 @@ extension Ghostty.Config {
}
}
}
enum WindowDecoration: String {
case none
case client
case server
case auto
func enabled() -> Bool {
switch self {
case .client, .server, .auto: return true
case .none: return false
}
}
}
}

View File

@@ -0,0 +1,15 @@
import Cocoa
import GhosttyKit
extension Ghostty {
/// A comparable event.
struct ComparableKeyEvent: Equatable {
let keyCode: UInt16
let flags: NSEvent.ModifierFlags
init(event: NSEvent) {
self.keyCode = event.keyCode
self.flags = event.modifierFlags
}
}
}

View File

@@ -38,6 +38,15 @@ extension Ghostty {
}
}
/// Returns true if the tree is split.
var isSplit: Bool {
return if case .leaf = self {
false
} else {
true
}
}
func topLeft() -> SurfaceView {
switch (self) {
case .leaf(let leaf):
@@ -51,7 +60,7 @@ extension Ghostty {
/// Returns the view that would prefer receiving focus in this tree. This is always the
/// top-left-most view. This is used when creating a split or closing a split to find the
/// next view to send focus to.
func preferredFocus(_ direction: SplitFocusDirection = .top) -> SurfaceView {
func preferredFocus(_ direction: SplitFocusDirection = .up) -> SurfaceView {
let container: Container
switch (self) {
case .leaf(let leaf):
@@ -64,10 +73,10 @@ extension Ghostty {
let node: SplitNode
switch (direction) {
case .previous, .top, .left:
case .previous, .up, .left:
node = container.bottomRight
case .next, .bottom, .right:
case .next, .down, .right:
node = container.topLeft
}
@@ -120,14 +129,7 @@ extension Ghostty {
/// Returns true if the split tree contains the given view.
func contains(view: SurfaceView) -> Bool {
switch (self) {
case .leaf(let leaf):
return leaf.surface == view
case .split(let container):
return container.topLeft.contains(view: view) ||
container.bottomRight.contains(view: view)
}
return leaf(for: view) != nil
}
/// Find a surface view by UUID.
@@ -164,6 +166,22 @@ extension Ghostty {
}
}
/// Return the node for the given view if its in the tree.
func leaf(for view: SurfaceView) -> Leaf? {
switch (self) {
case .leaf(let leaf):
if leaf.surface == view {
return leaf
} else {
return nil
}
case .split(let container):
return container.topLeft.leaf(for: view) ??
container.bottomRight.leaf(for: view)
}
}
// MARK: - Sequence
func makeIterator() -> IndexingIterator<[Leaf]> {
@@ -431,12 +449,12 @@ extension Ghostty {
struct Neighbors {
var left: SplitNode?
var right: SplitNode?
var top: SplitNode?
var bottom: SplitNode?
var up: SplitNode?
var down: SplitNode?
/// These are the previous/next nodes. It will certainly be one of the above as well
/// but we keep track of these separately because depending on the split direction
/// of the containing node, previous may be left OR top (same for next).
/// of the containing node, previous may be left OR up (same for next).
var previous: SplitNode?
var next: SplitNode?
@@ -448,8 +466,8 @@ extension Ghostty {
let map: [SplitFocusDirection : KeyPath<Self, SplitNode?>] = [
.previous: \.previous,
.next: \.next,
.top: \.top,
.bottom: \.bottom,
.up: \.up,
.down: \.down,
.left: \.left,
.right: \.right,
]

View File

@@ -205,6 +205,7 @@ extension Ghostty {
alert.beginSheetModal(for: window, completionHandler: { response in
switch (response) {
case .alertFirstButtonReturn:
alert.window.orderOut(nil)
node = nil
default:
@@ -308,7 +309,7 @@ extension Ghostty {
resizeIncrements: .init(width: 1, height: 1),
resizePublisher: container.resizeEvent,
left: {
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.bottom
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.down
TerminalSplitNested(
node: closeableTopLeft(),
@@ -318,7 +319,7 @@ extension Ghostty {
])
)
}, right: {
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.top
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.up
TerminalSplitNested(
node: closeableBottomRight(),

View File

@@ -0,0 +1,15 @@
import Cocoa
import GhosttyKit
extension NSEvent {
/// Create a Ghostty key event for a given keyboard action.
func ghosttyKeyEvent(_ action: ghostty_input_action_e) -> ghostty_input_key_s {
var key_ev = ghostty_input_key_s()
key_ev.action = action
key_ev.mods = Ghostty.ghosttyMods(modifierFlags)
key_ev.keycode = UInt32(keyCode)
key_ev.text = nil
key_ev.composing = false
return key_ev
}
}

View File

@@ -66,7 +66,7 @@ extension Ghostty {
/// An enum that is used for the directions that a split focus event can change.
enum SplitFocusDirection {
case previous, next, top, bottom, left, right
case previous, next, up, down, left, right
/// Initialize from a Ghostty API enum.
static func from(direction: ghostty_action_goto_split_e) -> Self? {
@@ -77,11 +77,11 @@ extension Ghostty {
case GHOSTTY_GOTO_SPLIT_NEXT:
return .next
case GHOSTTY_GOTO_SPLIT_TOP:
return .top
case GHOSTTY_GOTO_SPLIT_UP:
return .up
case GHOSTTY_GOTO_SPLIT_BOTTOM:
return .bottom
case GHOSTTY_GOTO_SPLIT_DOWN:
return .down
case GHOSTTY_GOTO_SPLIT_LEFT:
return .left
@@ -102,11 +102,11 @@ extension Ghostty {
case .next:
return GHOSTTY_GOTO_SPLIT_NEXT
case .top:
return GHOSTTY_GOTO_SPLIT_TOP
case .up:
return GHOSTTY_GOTO_SPLIT_UP
case .bottom:
return GHOSTTY_GOTO_SPLIT_BOTTOM
case .down:
return GHOSTTY_GOTO_SPLIT_DOWN
case .left:
return GHOSTTY_GOTO_SPLIT_LEFT
@@ -159,7 +159,7 @@ extension Ghostty {
case osc_52_read
/// An application is attempting to write to the clipboard using OSC 52
case osc_52_write
case osc_52_write(OSPasteboard?)
/// The text to show in the clipboard confirmation prompt for a given request type
func text() -> String {
@@ -188,7 +188,7 @@ extension Ghostty {
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ:
return .osc_52_read
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE:
return .osc_52_write
return .osc_52_write(nil)
default:
return nil
}
@@ -198,6 +198,14 @@ extension Ghostty {
/// macos-icon
enum MacOSIcon: String {
case official
case blueprint
case chalkboard
case glass
case holographic
case microchip
case paper
case retro
case xray
case customStyle = "custom-style"
}
@@ -236,6 +244,9 @@ extension Notification.Name {
/// Goto tab. Has tab index in the userinfo.
static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab")
static let GhosttyMoveTabKey = ghosttyMoveTab.rawValue
/// Close tab
static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab")
}
// NOTE: I am moving all of these to Notification.Name extensions over time. This

View File

@@ -92,22 +92,6 @@ extension Ghostty {
windowFocus = false
}
}
.onDrop(of: [.fileURL], isTargeted: nil) { providers in
providers.forEach { provider in
_ = provider.loadObject(ofClass: URL.self) { url, _ in
guard let url = url else { return }
let path = Shell.escape(url.path)
DispatchQueue.main.async {
surfaceView.insertText(
path,
replacementRange: NSMakeRange(0, 0)
)
}
}
}
return true
}
#endif
// If our geo size changed then we show the resize overlay as configured.

View File

@@ -1,3 +1,4 @@
import AppKit
import SwiftUI
import CoreText
import UserNotifications
@@ -12,7 +13,14 @@ extension Ghostty {
// The current title of the surface as defined by the pty. This can be
// changed with escape codes. This is public because the callbacks go
// to the app level and it is set from there.
@Published private(set) var title: String = "👻"
@Published private(set) var title: String = "" {
didSet {
if !title.isEmpty {
titleFallbackTimer?.invalidate()
titleFallbackTimer = nil
}
}
}
// The current pwd of the surface as defined by the pty. This can be
// changed with escape codes.
@@ -113,6 +121,12 @@ extension Ghostty {
// A small delay that is introduced before a title change to avoid flickers
private var titleChangeTimer: Timer?
// A timer to fallback to ghost emoji if no title is set within the grace period
private var titleFallbackTimer: Timer?
/// Event monitor (see individual events for why)
private var eventMonitor: Any? = nil
// We need to support being a first responder so that we can get input events
override var acceptsFirstResponder: Bool { return true }
@@ -136,6 +150,13 @@ extension Ghostty {
// can do SOMETHING.
super.init(frame: NSMakeRect(0, 0, 800, 600))
// Set a timer to show the ghost emoji after 500ms if no title is set
titleFallbackTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in
if let self = self, self.title.isEmpty {
self.title = "👻"
}
}
// Before we initialize the surface we want to register our notifications
// so there is no window where we can't receive them.
let center = NotificationCenter.default
@@ -170,6 +191,15 @@ extension Ghostty {
name: NSWindow.didChangeScreenNotification,
object: nil)
// Listen for local events that we need to know of outside of
// single surface handlers.
self.eventMonitor = NSEvent.addLocalMonitorForEvents(
matching: [
// We need keyUp because command+key events don't trigger keyUp.
.keyUp
]
) { [weak self] event in self?.localEventHandler(event) }
// Setup our surface. This will also initialize all the terminal IO.
let surface_cfg = baseConfig ?? SurfaceConfiguration()
var surface_cfg_c = surface_cfg.ghosttyConfig(view: self)
@@ -201,6 +231,9 @@ extension Ghostty {
ghostty_surface_set_color_scheme(surface, scheme)
}
// The UTTypes that can be dragged onto this view.
registerForDraggedTypes(Array(Self.dropTypes))
}
required init?(coder: NSCoder) {
@@ -212,6 +245,11 @@ extension Ghostty {
let center = NotificationCenter.default
center.removeObserver(self)
// Remove our event monitor
if let eventMonitor {
NSEvent.removeMonitor(eventMonitor)
}
// Whenever the surface is removed, we need to note that our restorable
// state is invalid to prevent the surface from being restored.
invalidateRestorableState()
@@ -356,6 +394,30 @@ extension Ghostty {
}
}
// MARK: Local Events
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
return switch event.type {
case .keyUp:
localEventKeyUp(event)
default:
event
}
}
private func localEventKeyUp(_ event: NSEvent) -> NSEvent? {
// We only care about events with "command" because all others will
// trigger the normal responder chain.
if (!event.modifierFlags.contains(.command)) { return event }
// Command keyUp events are never sent to the normal responder chain
// so we send them here.
guard focused else { return event }
self.keyUp(with: event)
return nil
}
// MARK: - Notifications
@objc private func onUpdateRendererHealth(notification: SwiftUI.Notification) {
@@ -764,8 +826,23 @@ extension Ghostty {
// know if these events cleared it.
let markedTextBefore = markedText.length > 0
// We need to know the keyboard layout before below because some keyboard
// input events will change our keyboard layout and we don't want those
// going to the terminal.
let keyboardIdBefore: String? = if (!markedTextBefore) {
KeyboardLayout.id
} else {
nil
}
self.interpretKeyEvents([translationEvent])
// If our keyboard changed from this we just assume an input method
// grabbed it and do nothing.
if (!markedTextBefore && keyboardIdBefore != KeyboardLayout.id) {
return
}
// If we have text, then we've composed a character, send that down. We do this
// first because if we completed a preedit, the text will be available here
// AND we'll have a preedit.
@@ -773,7 +850,7 @@ extension Ghostty {
if let list = keyTextAccumulator, list.count > 0 {
handled = true
for text in list {
keyAction(action, event: event, text: text)
_ = keyAction(action, event: event, text: text)
}
}
@@ -783,38 +860,49 @@ extension Ghostty {
// the preedit.
if (markedText.length > 0 || markedTextBefore) {
handled = true
keyAction(action, event: event, preedit: markedText.string)
_ = keyAction(action, event: event, preedit: markedText.string)
}
if (!handled) {
// No text or anything, we want to handle this manually.
keyAction(action, event: event)
_ = keyAction(action, event: event)
}
}
override func keyUp(with event: NSEvent) {
keyAction(GHOSTTY_ACTION_RELEASE, event: event)
_ = keyAction(GHOSTTY_ACTION_RELEASE, event: event)
}
/// Special case handling for some control keys
override func performKeyEquivalent(with event: NSEvent) -> Bool {
// Only process key down events
if (event.type != .keyDown) {
switch (event.type) {
case .keyDown:
// Continue, we care about key down events
break
default:
// Any other key event we don't care about. I don't think its even
// possible to receive any other event type.
return false
}
// Only process events if we're focused. Some key events like C-/ macOS
// appears to send to the first view in the hierarchy rather than the
// the first responder (I don't know why). This prevents us from handling it.
// Besides C-/, its important we don't process key equivalents if unfocused
// because there are other event listeners for that (i.e. AppDelegate's
// local event handler).
if (!focused) {
return false
}
// Only process keys when Control is active. All known issues we're
// resolving happen only in this scenario. This probably isn't fully robust
// but we can broaden the scope as we find more cases.
if (!event.modifierFlags.contains(.control)) {
return false
// If this event as-is would result in a key binding then we send it.
if let surface,
ghostty_surface_key_is_binding(
surface,
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) {
self.keyDown(with: event)
return true
}
let equivalent: String
@@ -832,14 +920,25 @@ extension Ghostty {
case "\r":
// Pass C-<return> through verbatim
// (prevent the default context menu equivalent)
if (!event.modifierFlags.contains(.control)) {
return false
}
equivalent = "\r"
case ".":
if (!event.modifierFlags.contains(.command)) {
return false
}
equivalent = "."
default:
// Ignore other events
return false
}
let newEvent = NSEvent.keyEvent(
let finalEvent = NSEvent.keyEvent(
with: .keyDown,
location: event.locationInWindow,
modifierFlags: event.modifierFlags,
@@ -852,7 +951,7 @@ extension Ghostty {
keyCode: event.keyCode
)
self.keyDown(with: newEvent!)
self.keyDown(with: finalEvent!)
return true
}
@@ -867,6 +966,9 @@ extension Ghostty {
default: return
}
// If we're in the middle of a preedit, don't do anything with mods.
if hasMarkedText() { return }
// The keyAction function will do this AGAIN below which sucks to repeat
// but this is super cheap and flagsChanged isn't that common.
let mods = Ghostty.ghosttyMods(event.modifierFlags)
@@ -897,45 +999,38 @@ extension Ghostty {
}
}
keyAction(action, event: event)
_ = keyAction(action, event: event)
}
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent) {
guard let surface = self.surface else { return }
var key_ev = ghostty_input_key_s()
key_ev.action = action
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
key_ev.keycode = UInt32(event.keyCode)
key_ev.text = nil
key_ev.composing = false
ghostty_surface_key(surface, key_ev)
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent) -> Bool {
guard let surface = self.surface else { return false }
return ghostty_surface_key(surface, event.ghosttyKeyEvent(action))
}
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent, preedit: String) {
guard let surface = self.surface else { return }
private func keyAction(
_ action: ghostty_input_action_e,
event: NSEvent, preedit: String
) -> Bool {
guard let surface = self.surface else { return false }
preedit.withCString { ptr in
var key_ev = ghostty_input_key_s()
key_ev.action = action
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
key_ev.keycode = UInt32(event.keyCode)
return preedit.withCString { ptr in
var key_ev = event.ghosttyKeyEvent(action)
key_ev.text = ptr
key_ev.composing = true
ghostty_surface_key(surface, key_ev)
return ghostty_surface_key(surface, key_ev)
}
}
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent, text: String) {
guard let surface = self.surface else { return }
private func keyAction(
_ action: ghostty_input_action_e,
event: NSEvent, text: String
) -> Bool {
guard let surface = self.surface else { return false }
text.withCString { ptr in
var key_ev = ghostty_input_key_s()
key_ev.action = action
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
key_ev.keycode = UInt32(event.keyCode)
return text.withCString { ptr in
var key_ev = event.ghosttyKeyEvent(action)
key_ev.text = ptr
ghostty_surface_key(surface, key_ev)
return ghostty_surface_key(surface, key_ev)
}
}
@@ -1053,6 +1148,14 @@ extension Ghostty {
}
}
@IBAction func pasteSelection(_ sender: Any?) {
guard let surface = self.surface else { return }
let action = "paste_from_selection"
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
AppDelegate.logger.warning("action failed action=\(action)")
}
}
@IBAction override func selectAll(_ sender: Any?) {
guard let surface = self.surface else { return }
let action = "select_all"
@@ -1374,3 +1477,78 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor {
return true
}
}
// MARK: NSMenuItemValidation
extension Ghostty.SurfaceView: NSMenuItemValidation {
func validateMenuItem(_ item: NSMenuItem) -> Bool {
switch item.action {
case #selector(pasteSelection):
let pb = NSPasteboard.ghosttySelection
guard let str = pb.getOpinionatedStringContents() else { return false }
return !str.isEmpty
default:
return true
}
}
}
// MARK: NSDraggingDestination
extension Ghostty.SurfaceView {
static let dropTypes: Set<NSPasteboard.PasteboardType> = [
.string,
.fileURL,
.URL
]
override func draggingEntered(_ sender: any NSDraggingInfo) -> NSDragOperation {
guard let types = sender.draggingPasteboard.types else { return [] }
// If the dragging object contains none of our types then we return none.
// This shouldn't happen because AppKit should guarantee that we only
// receive types we registered for but its good to check.
if Set(types).isDisjoint(with: Self.dropTypes) {
return []
}
// We use copy to get the proper icon
return .copy
}
override func performDragOperation(_ sender: any NSDraggingInfo) -> Bool {
let pb = sender.draggingPasteboard
let content: String?
if let url = pb.string(forType: .URL) {
// URLs first, they get escaped as-is.
content = Ghostty.Shell.escape(url)
} else if let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL],
urls.count > 0 {
// File URLs next. They get escaped individually and then joined by a
// space if there are multiple.
content = urls
.map { Ghostty.Shell.escape($0.path) }
.joined(separator: " ")
} else if let str = pb.string(forType: .string) {
// Strings are not escaped because they may be copy/pasting a
// command they want to execute.
content = str
} else {
content = nil
}
if let content {
DispatchQueue.main.async {
self.insertText(
content,
replacementRange: NSMakeRange(0, 0)
)
}
return true
}
return false
}
}

View File

@@ -10,6 +10,7 @@ import AppKit
typealias OSView = NSView
typealias OSColor = NSColor
typealias OSSize = NSSize
typealias OSPasteboard = NSPasteboard
protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType {
associatedtype OSViewType: NSView
@@ -34,6 +35,7 @@ import UIKit
typealias OSView = UIView
typealias OSColor = UIColor
typealias OSSize = CGSize
typealias OSPasteboard = UIPasteboard
protocol OSViewRepresentable: UIViewRepresentable {
associatedtype OSViewType: UIView

View File

@@ -0,0 +1,38 @@
import Cocoa
// Private API to get Dock location
@_silgen_name("CoreDockGetOrientationAndPinning")
func CoreDockGetOrientationAndPinning(
_ outOrientation: UnsafeMutablePointer<Int32>,
_ outPinning: UnsafeMutablePointer<Int32>)
// Private API to get the current Dock auto-hide state
@_silgen_name("CoreDockGetAutoHideEnabled")
func CoreDockGetAutoHideEnabled() -> Bool
// Toggles the Dock's auto-hide state
@_silgen_name("CoreDockSetAutoHideEnabled")
func CoreDockSetAutoHideEnabled(_ flag: Bool)
enum DockOrientation: Int {
case top = 1
case bottom = 2
case left = 3
case right = 4
}
class Dock {
/// Returns the orientation of the dock or nil if it can't be determined.
static var orientation: DockOrientation? {
var orientation: Int32 = 0
var pinning: Int32 = 0
CoreDockGetOrientationAndPinning(&orientation, &pinning)
return .init(rawValue: Int(orientation)) ?? nil
}
/// Set the dock autohide.
static var autoHideEnabled: Bool {
get { return CoreDockGetAutoHideEnabled() }
set { CoreDockSetAutoHideEnabled(newValue) }
}
}

View File

@@ -307,21 +307,21 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
// MARK: Dock
private func hideDock() {
NSApp.presentationOptions.insert(.autoHideDock)
NSApp.acquirePresentationOption(.autoHideDock)
}
private func unhideDock() {
NSApp.presentationOptions.remove(.autoHideDock)
NSApp.releasePresentationOption(.autoHideDock)
}
// MARK: Menu
func hideMenu() {
NSApp.presentationOptions.insert(.autoHideMenuBar)
NSApp.acquirePresentationOption(.autoHideMenuBar)
}
func unhideMenu() {
NSApp.presentationOptions.remove(.autoHideMenuBar)
NSApp.releasePresentationOption(.autoHideMenuBar)
}
/// The state that must be saved for non-native fullscreen to exit fullscreen.

View File

@@ -0,0 +1,14 @@
import Carbon
class KeyboardLayout {
/// Return a string ID of the current keyboard input source.
static var id: String? {
if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(),
let sourceIdPointer = TISGetInputSourceProperty(source, kTISPropertyInputSourceID) {
let sourceId = unsafeBitCast(sourceIdPointer, to: CFString.self)
return sourceId as String
}
return nil
}
}

View File

@@ -0,0 +1,34 @@
import Cocoa
/// Manages the persistence and restoration of window positions across app launches.
class LastWindowPosition {
static let shared = LastWindowPosition()
private let positionKey = "NSWindowLastPosition"
func save(_ window: NSWindow) {
let origin = window.frame.origin
let point = [origin.x, origin.y]
UserDefaults.standard.set(point, forKey: positionKey)
}
func restore(_ window: NSWindow) -> Bool {
guard let points = UserDefaults.standard.array(forKey: positionKey) as? [Double],
points.count == 2 else { return false }
let lastPosition = CGPoint(x: points[0], y: points[1])
guard let screen = window.screen ?? NSScreen.main else { return false }
let visibleFrame = screen.visibleFrame
var newFrame = window.frame
newFrame.origin = lastPosition
if !visibleFrame.contains(newFrame.origin) {
newFrame.origin.x = max(visibleFrame.minX, min(visibleFrame.maxX - newFrame.width, newFrame.origin.x))
newFrame.origin.y = max(visibleFrame.minY, min(visibleFrame.maxY - newFrame.height, newFrame.origin.y))
}
window.setFrame(newFrame, display: true)
return true
}
}

View File

@@ -0,0 +1,31 @@
import Cocoa
extension NSApplication {
private static var presentationOptionCounts: [NSApplication.PresentationOptions.Element: UInt] = [:]
/// Add a presentation option to the application and main a reference count so that and equal
/// number of pops is required to disable it. This is useful so that multiple classes can affect global
/// app state without overriding others.
func acquirePresentationOption(_ option: NSApplication.PresentationOptions.Element) {
Self.presentationOptionCounts[option, default: 0] += 1
presentationOptions.insert(option)
}
/// See acquirePresentationOption
func releasePresentationOption(_ option: NSApplication.PresentationOptions.Element) {
guard let value = Self.presentationOptionCounts[option] else { return }
guard value > 0 else { return }
if (value == 1) {
presentationOptions.remove(option)
Self.presentationOptionCounts.removeValue(forKey: option)
} else {
Self.presentationOptionCounts[option] = value - 1
}
}
}
extension NSApplication.PresentationOptions.Element: @retroactive Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(rawValue)
}
}

View File

@@ -1,17 +1,39 @@
import AppKit
import GhosttyKit
extension NSPasteboard {
/// The pasteboard to used for Ghostty selection.
static var ghosttySelection: NSPasteboard = {
NSPasteboard(name: .init("com.mitchellh.ghostty.selection"))
}()
/// Gets the contents of the pasteboard as a string following a specific set of semantics.
/// Does these things in order:
/// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one.
/// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one and ensures the file path is properly escaped.
/// - Tries to get any string from the pasteboard.
/// If all of the above fail, returns None.
func getOpinionatedStringContents() -> String? {
if let file = self.string(forType: .fileURL) {
if let path = NSURL(string: file)?.path {
return path
}
if let urls = readObjects(forClasses: [NSURL.self]) as? [URL],
urls.count > 0 {
return urls
.map { $0.isFileURL ? Ghostty.Shell.escape($0.path) : $0.absoluteString }
.joined(separator: " ")
}
return self.string(forType: .string)
}
/// The pasteboard for the Ghostty enum type.
static func ghostty(_ clipboard: ghostty_clipboard_e) -> NSPasteboard? {
switch (clipboard) {
case GHOSTTY_CLIPBOARD_STANDARD:
return Self.general
case GHOSTTY_CLIPBOARD_SELECTION:
return Self.ghosttySelection
default:
return nil
}
}
}

View File

@@ -0,0 +1,9 @@
/// A wrapper that holds a weak reference to an object. This lets us create native containers
/// of weak references.
class Weak<T: AnyObject> {
weak var value: T?
init(_ value: T) {
self.value = value
}
}

View File

@@ -1,63 +0,0 @@
#!/usr/bin/env bash
# Nothing in this script should fail.
set -e
CACHE_HASH_FILE="$(realpath "$(dirname "$0")/../zigCacheHash.nix")"
help() {
echo ""
echo "To fix, please (manually) re-run the script from the repository root,"
echo "commit, and push the update:"
echo ""
echo " ./nix/build-support/check-zig-cache-hash.sh --update"
echo " git add nix/zigCacheHash.nix"
echo " git commit -m \"nix: update Zig cache hash\""
echo " git push"
echo ""
}
if [ -f "${CACHE_HASH_FILE}" ]; then
OLD_CACHE_HASH="$(nix eval --raw --file "${CACHE_HASH_FILE}")"
elif [ "$1" != "--update" ]; then
echo -e "\nERROR: Zig cache hash file missing."
help
exit 1
fi
ZIG_GLOBAL_CACHE_DIR="$(mktemp --directory --suffix nix-zig-cache)"
export ZIG_GLOBAL_CACHE_DIR
# This is not 100% necessary in CI but is helpful when running locally to keep
# a local workstation clean.
trap 'rm -rf "${ZIG_GLOBAL_CACHE_DIR}"' EXIT
# Run Zig and download the cache to the temporary directory.
sh ./nix/build-support/fetch-zig-cache.sh
# Now, calculate the hash.
ZIG_CACHE_HASH="sha256-$(nix-hash --type sha256 --to-base64 "$(nix-hash --type sha256 "${ZIG_GLOBAL_CACHE_DIR}")")"
if [ "${OLD_CACHE_HASH}" == "${ZIG_CACHE_HASH}" ]; then
echo -e "\nOK: Zig cache store hash unchanged."
exit 0
elif [ "$1" != "--update" ]; then
echo -e "\nERROR: The Zig cache store hash has updated."
echo ""
echo " * Old hash: ${OLD_CACHE_HASH}"
echo " * New hash: ${ZIG_CACHE_HASH}"
help
exit 1
else
echo -e "\nNew Zig cache store hash: ${ZIG_CACHE_HASH}"
fi
# Write out the cache file
cat > "${CACHE_HASH_FILE}" <<EOS
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
# more details.
"${ZIG_CACHE_HASH}"
EOS
echo -e "\nOK: Wrote new hash to file: ${CACHE_HASH_FILE}"

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env bash
# Nothing in this script should fail.
set -e
WORK_DIR=$(mktemp -d)
if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then
echo "could not create temp dir"
exit 1
fi
function cleanup {
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
help() {
echo ""
echo "To fix, please (manually) re-run the script from the repository root,"
echo "commit, and submit a PR with the update:"
echo ""
echo " ./nix/build-support/check-zig-cache-hash.sh --update"
echo " git add build.zig.zon.nix"
echo " git commit -m \"nix: update build.zig.zon.nix\""
echo ""
}
BUILD_ZIG_ZON="$(realpath "$(dirname "$0")/../../build.zig.zon")"
BUILD_ZIG_ZON_LOCK="$(realpath "$(dirname "$0")/../../build.zig.zon2json-lock")"
BUILD_ZIG_ZON_NIX="$(realpath "$(dirname "$0")/../../build.zig.zon.nix")"
if [ -f "${BUILD_ZIG_ZON_NIX}" ]; then
OLD_HASH=$(sha512sum "${BUILD_ZIG_ZON_NIX}" | awk '{print $1}')
elif [ "$1" != "--update" ]; then
echo -e "\nERROR: build.zig.zon.nix missing."
help
exit 1
fi
rm -f "$BUILD_ZIG_ZON_LOCK"
zon2nix "$BUILD_ZIG_ZON" > "$WORK_DIR/build.zig.zon.nix"
alejandra --quiet "$WORK_DIR/build.zig.zon.nix"
rm -f "$BUILD_ZIG_ZON_LOCK"
NEW_HASH=$(sha512sum "$WORK_DIR/build.zig.zon.nix" | awk '{print $1}')
if [ "${OLD_HASH}" == "${NEW_HASH}" ]; then
echo -e "\nOK: build.zig.zon.nix unchanged."
exit 0
elif [ "$1" != "--update" ]; then
echo -e "\nERROR: build.zig.zon.nix needs to be updated."
echo ""
echo " * Old hash: ${OLD_HASH}"
echo " * New hash: ${NEW_HASH}"
help
exit 1
else
mv "$WORK_DIR/build.zig.zon.nix" "$BUILD_ZIG_ZON_NIX"
echo -e "\nOK: build.zig.zon.nix updated."
exit 0
fi

View File

@@ -1,39 +0,0 @@
#!/bin/sh
set -e
# Because Zig does not fetch recursive dependencies when you run `zig build
# --fetch` (see https://github.com/ziglang/zig/issues/20976) we need to do some
# extra work to fetch everything that we actually need to build without Internet
# access (such as when building a Nix package).
#
# An example of this happening:
#
# error: builder for '/nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv' failed with exit code 1;
# la/build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:7:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure
# > .url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e",
# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# > /build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:16:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure
# > .url = "git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b",
# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# >
# For full logs, run 'nix log /nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv'.
#
# To update this script, add any failing URLs with a line like this:
#
# zig fetch <url>
#
# Periodically old URLs may need to be cleaned out.
#
# Hopefully when the Zig issue is fixed this script can be eliminated in favor
# of a plain `zig build --fetch`.
if [ -z ${ZIG_GLOBAL_CACHE_DIR+x} ]
then
echo "must set ZIG_GLOBAL_CACHE_DIR!"
exit 1
fi
zig build --fetch
zig fetch git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e
zig fetch git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b

View File

@@ -30,7 +30,9 @@
glib,
glslang,
gtk4,
gobject-introspection,
libadwaita,
blueprint-compiler,
adwaita-icon-theme,
hicolor-icon-theme,
harfbuzz,
@@ -51,6 +53,11 @@
pandoc,
hyperfine,
typos,
wayland,
wayland-scanner,
wayland-protocols,
zig2nix,
system,
}: let
# See package.nix. Keep in sync.
rpathLibs =
@@ -80,6 +87,8 @@
libadwaita
gtk4
glib
gobject-introspection
wayland
];
in
mkShell {
@@ -96,6 +105,7 @@ in
scdoc
zig
zip
zig2nix.packages.${system}.zon2nix
# For web and wasm stuff
nodejs
@@ -150,9 +160,14 @@ in
libXrandr
# Only needed for GTK builds
blueprint-compiler
libadwaita
gtk4
glib
gobject-introspection
wayland
wayland-scanner
wayland-protocols
];
# This should be set onto the rpath of the ghostty binary if you want

View File

@@ -2,6 +2,7 @@
lib,
stdenv,
bzip2,
callPackage,
expat,
fontconfig,
freetype,
@@ -10,13 +11,11 @@
oniguruma,
zlib,
libGL,
libX11,
libXcursor,
libXi,
libXrandr,
glib,
gtk4,
gobject-introspection,
libadwaita,
blueprint-compiler,
wrapGAppsHook4,
gsettings-desktop-schemas,
git,
@@ -26,7 +25,15 @@
pandoc,
revision ? "dirty",
optimize ? "Debug",
x11 ? true,
enableX11 ? true,
libX11,
libXcursor,
libXi,
libXrandr,
enableWayland ? true,
wayland,
wayland-protocols,
wayland-scanner,
}: let
# The Zig hook has no way to select the release type without actual
# overriding of the default flags.
@@ -36,8 +43,12 @@
# ultimately acted on and has made its way to a nixpkgs implementation, this
# can probably be removed in favor of that.
zig_hook = zig_0_13.hook.overrideAttrs {
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize}";
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize} --color off";
};
in
stdenv.mkDerivation (finalAttrs: {
pname = "ghostty";
version = "1.1.2";
# We limit source like this to try and reduce the amount of rebuilds as possible
# thus we only provide the source that is needed for the build
@@ -49,7 +60,6 @@
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
lib.fileset.unions [
../dist/linux
../conformance
../images
../include
../pkg
@@ -57,70 +67,27 @@
../vendor
../build.zig
../build.zig.zon
./build-support/fetch-zig-cache.sh
../build.zig.zon.nix
]
);
};
# This hash is the computation of the zigCache fixed-output derivation. This
# allows us to use remote package dependencies without breaking the sandbox.
#
# This will need updating whenever dependencies get updated (e.g. changes are
# made to zig.build.zon). If you see that the main build is trying to reach
# out to the internet and failing, this is likely the cause. Change this
# value back to lib.fakeHash, and re-run. The build failure should emit the
# updated hash, which of course, should be validated before updating here.
#
# (It's also possible that you might see a hash mismatch - without the
# network errors - if you don't have a previous instance of the cache
# derivation in your store already. If so, just update the value as above.)
zigCacheHash = import ./zigCacheHash.nix;
deps = callPackage ../build.zig.zon.nix {name = "ghostty-cache-${finalAttrs.version}";};
zigCache = stdenv.mkDerivation {
inherit src;
name = "ghostty-cache";
nativeBuildInputs = [
git
zig_hook
];
dontConfigure = true;
dontUseZigBuild = true;
dontUseZigInstall = true;
dontFixup = true;
buildPhase = ''
runHook preBuild
sh ./nix/build-support/fetch-zig-cache.sh
runHook postBuild
'';
installPhase = ''
runHook preInstall
cp -r --reflink=auto $ZIG_GLOBAL_CACHE_DIR $out
runHook postInstall
'';
outputHashMode = "recursive";
outputHash = zigCacheHash;
};
in
stdenv.mkDerivation (finalAttrs: {
pname = "ghostty";
version = "1.0.1";
inherit src;
nativeBuildInputs = [
nativeBuildInputs =
[
git
ncurses
pandoc
pkg-config
zig_hook
gobject-introspection
wrapGAppsHook4
blueprint-compiler
]
++ lib.optionals enableWayland [
wayland-scanner
wayland-protocols
];
buildInputs =
@@ -142,22 +109,25 @@ in
glib
gsettings-desktop-schemas
]
++ lib.optionals x11 [
++ lib.optionals enableX11 [
libX11
libXcursor
libXi
libXrandr
]
++ lib.optionals enableWayland [
wayland
];
dontConfigure = true;
zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix -Dgtk-x11=${lib.boolToString x11}";
preBuild = ''
rm -rf $ZIG_GLOBAL_CACHE_DIR
cp -r --reflink=auto ${zigCache} $ZIG_GLOBAL_CACHE_DIR
chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR
'';
zigBuildFlags = [
"--system"
"${finalAttrs.deps}"
"-Dversion-string=${finalAttrs.version}-${revision}-nix"
"-Dgtk-x11=${lib.boolToString enableX11}"
"-Dgtk-wayland=${lib.boolToString enableWayland}"
];
outputs = [
"out"
@@ -191,7 +161,7 @@ in
'';
meta = {
homepage = "https://github.com/ghostty-org/ghostty";
homepage = "https://ghostty.org";
license = lib.licenses.mit;
platforms = [
"x86_64-linux"

View File

@@ -0,0 +1,18 @@
{...}: {
imports = [
./common.nix
];
services.xserver = {
displayManager = {
lightdm = {
enable = true;
};
};
desktopManager = {
cinnamon = {
enable = true;
};
};
};
}

136
nix/vm/common-gnome.nix Normal file
View File

@@ -0,0 +1,136 @@
{
config,
lib,
pkgs,
...
}: {
imports = [
./common.nix
];
services.xserver = {
displayManager = {
gdm = {
enable = true;
autoSuspend = false;
};
};
desktopManager = {
gnome = {
enable = true;
};
};
};
environment.systemPackages = [
pkgs.gnomeExtensions.no-overview
];
environment.gnome.excludePackages = with pkgs; [
atomix
baobab
cheese
epiphany
evince
file-roller
geary
gnome-backgrounds
gnome-calculator
gnome-calendar
gnome-clocks
gnome-connections
gnome-contacts
gnome-disk-utility
gnome-extension-manager
gnome-logs
gnome-maps
gnome-music
gnome-photos
gnome-software
gnome-system-monitor
gnome-text-editor
gnome-themes-extra
gnome-tour
gnome-user-docs
gnome-weather
hitori
iagno
loupe
nautilus
orca
seahorse
simple-scan
snapshot
sushi
tali
totem
yelp
];
programs.dconf = {
enable = true;
profiles.user.databases = [
{
settings = with lib.gvariant; {
"org/gnome/desktop/background" = {
picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
picture-uri-dark = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
picture-options = "centered";
primary-color = "#000000000000";
secondary-color = "#000000000000";
};
"org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
};
"org/gnome/desktop/notifications" = {
show-in-lock-screen = false;
};
"org/gnome/desktop/screensaver" = {
lock-enabled = false;
picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
picture-options = "centered";
primary-color = "#000000000000";
secondary-color = "#000000000000";
};
"org/gnome/desktop/session" = {
idle-delay = mkUint32 0;
};
"org/gnome/shell" = {
disable-user-extensions = false;
enabled-extensions = builtins.map (x: x.extensionUuid) (
lib.filter (p: p ? extensionUuid) config.environment.systemPackages
);
};
};
}
];
};
programs.geary.enable = false;
services.gnome = {
gnome-browser-connector.enable = false;
gnome-initial-setup.enable = false;
gnome-online-accounts.enable = false;
gnome-remote-desktop.enable = false;
rygel.enable = false;
};
system.activationScripts = {
face = {
text = ''
mkdir -p /var/lib/AccountsService/{icons,users}
cp ${pkgs.ghostty}/share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png /var/lib/AccountsService/icons/ghostty
echo -e "[User]\nIcon=/var/lib/AccountsService/icons/ghostty\n" > /var/lib/AccountsService/users/ghostty
chown root:root /var/lib/AccountsService/users/ghostty
chmod 0600 /var/lib/AccountsService/users/ghostty
chown root:root /var/lib/AccountsService/icons/ghostty
chmod 0444 /var/lib/AccountsService/icons/ghostty
'';
};
};
}

21
nix/vm/common-plasma6.nix Normal file
View File

@@ -0,0 +1,21 @@
{...}: {
imports = [
./common.nix
];
services = {
displayManager = {
sddm = {
enable = true;
wayland = {
enable = true;
};
};
};
desktopManager = {
plasma6 = {
enable = true;
};
};
};
}

18
nix/vm/common-xfce.nix Normal file
View File

@@ -0,0 +1,18 @@
{...}: {
imports = [
./common.nix
];
services.xserver = {
displayManager = {
lightdm = {
enable = true;
};
};
desktopManager = {
xfce = {
enable = true;
};
};
};
}

83
nix/vm/common.nix Normal file
View File

@@ -0,0 +1,83 @@
{pkgs, ...}: {
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
documentation.nixos.enable = false;
networking.hostName = "ghostty";
networking.domain = "mitchellh.com";
virtualisation.vmVariant = {
virtualisation.memorySize = 2048;
};
nix = {
settings = {
trusted-users = [
"root"
"ghostty"
];
};
extraOptions = ''
experimental-features = nix-command flakes
'';
};
users.mutableUsers = false;
users.groups.ghostty = {};
users.users.ghostty = {
description = "Ghostty";
group = "ghostty";
extraGroups = ["wheel"];
isNormalUser = true;
initialPassword = "ghostty";
};
environment.etc = {
"xdg/autostart/com.mitchellh.ghostty.desktop" = {
source = "${pkgs.ghostty}/share/applications/com.mitchellh.ghostty.desktop";
};
};
environment.systemPackages = [
pkgs.kitty
pkgs.fish
pkgs.ghostty
pkgs.helix
pkgs.neovim
pkgs.xterm
pkgs.zsh
];
security.polkit = {
enable = true;
};
services.dbus = {
enable = true;
};
services.displayManager = {
autoLogin = {
user = "ghostty";
};
};
services.libinput = {
enable = true;
};
services.qemuGuest = {
enable = true;
};
services.spice-vdagentd = {
enable = true;
};
services.xserver = {
enable = true;
};
}

View File

@@ -0,0 +1,12 @@
{
system,
nixpkgs,
overlay,
module,
uid ? 1000,
gid ? 1000,
}:
import ./create.nix {
inherit system nixpkgs overlay module uid gid;
common = ./common-cinnamon.nix;
}

12
nix/vm/create-gnome.nix Normal file
View File

@@ -0,0 +1,12 @@
{
system,
nixpkgs,
overlay,
module,
uid ? 1000,
gid ? 1000,
}:
import ./create.nix {
inherit system nixpkgs overlay module uid gid;
common = ./common-gnome.nix;
}

12
nix/vm/create-plasma6.nix Normal file
View File

@@ -0,0 +1,12 @@
{
system,
nixpkgs,
overlay,
module,
uid ? 1000,
gid ? 1000,
}:
import ./create.nix {
inherit system nixpkgs overlay module uid gid;
common = ./common-plasma6.nix;
}

12
nix/vm/create-xfce.nix Normal file
View File

@@ -0,0 +1,12 @@
{
system,
nixpkgs,
overlay,
module,
uid ? 1000,
gid ? 1000,
}:
import ./create.nix {
inherit system nixpkgs overlay module uid gid;
common = ./common-xfce.nix;
}

42
nix/vm/create.nix Normal file
View File

@@ -0,0 +1,42 @@
{
system,
nixpkgs,
overlay,
module,
common ? ./common.nix,
uid ? 1000,
gid ? 1000,
}: let
pkgs = import nixpkgs {
inherit system;
overlays = [
overlay
];
};
in
nixpkgs.lib.nixosSystem {
system = builtins.replaceStrings ["darwin"] ["linux"] system;
modules = [
{
virtualisation.vmVariant = {
virtualisation.host.pkgs = pkgs;
};
nixpkgs.overlays = [
overlay
];
users.groups.ghostty = {
gid = gid;
};
users.users.ghostty = {
uid = uid;
};
system.stateVersion = nixpkgs.lib.trivial.release;
}
common
module
];
}

View File

@@ -0,0 +1,7 @@
{...}: {
imports = [
./common-cinnamon.nix
];
services.displayManager.defaultSession = "cinnamon-wayland";
}

9
nix/vm/wayland-gnome.nix Normal file
View File

@@ -0,0 +1,9 @@
{...}: {
imports = [
./common-gnome.nix
];
services.displayManager = {
defaultSession = "gnome";
};
}

View File

@@ -0,0 +1,6 @@
{...}: {
imports = [
./common-plasma6.nix
];
services.displayManager.defaultSession = "plasma";
}

7
nix/vm/x11-cinnamon.nix Normal file
View File

@@ -0,0 +1,7 @@
{...}: {
imports = [
./common-cinnamon.nix
];
services.displayManager.defaultSession = "cinnamon";
}

9
nix/vm/x11-gnome.nix Normal file
View File

@@ -0,0 +1,9 @@
{...}: {
imports = [
./common-gnome.nix
];
services.displayManager = {
defaultSession = "gnome-xorg";
};
}

6
nix/vm/x11-plasma6.nix Normal file
View File

@@ -0,0 +1,6 @@
{...}: {
imports = [
./common-plasma6.nix
];
services.displayManager.defaultSession = "plasmax11";
}

7
nix/vm/x11-xfce.nix Normal file
View File

@@ -0,0 +1,7 @@
{...}: {
imports = [
./common-xfce.nix
];
services.displayManager.defaultSession = "xfce";
}

View File

@@ -1,3 +0,0 @@
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
# more details.
"sha256-ot5onG1yq7EWQkNUgTNBuqvsnLuaoFs2UDS96IqgJmU="

View File

@@ -1,10 +1,10 @@
.{
.name = "cimgui",
.version = "1.89.9",
.version = "1.90.6", // -docking branch
.paths = .{""},
.dependencies = .{
// This should be kept in sync with the submodule in the cimgui source
// code to be safe that they're compatible.
// code in ./vendor/ to be safe that they're compatible.
.imgui = .{
.url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz",
.hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402",

Some files were not shown because too many files have changed in this diff Show More