summaryrefslogtreecommitdiff
path: root/macos/Sources/Ghostty/SurfaceView_AppKit.swift
AgeCommit message (Collapse)Author
2025-10-03Fix typosAndreas Deininger
2025-09-19macos: correct SurfaceView supported send/receive types for servicesMitchell Hashimoto
Fixes #8785 This is the callback AppKit sends when it wants to know if our application can handle sending and receiving certain types of data. The prior implementaiton was incorrect and would erroneously claim support over combinations that we couldn't handle (at least, at the SurfaceView layer). This corrects the implementation. The services we expect still show up and the error in 8785 goes away.
2025-09-11macos: run change title dialog in a sheet modalMitchell Hashimoto
This fixes a macOS 26 issue where the OK button would not be visible. This MUST be an AppKit bug, but I'm trying to find workarounds.
2025-09-03macOS: SurfaceView should implement IdentifiableMitchell Hashimoto
This has no meaningful functionality yet, it was one of the paths I was looking at for #8505 but didn't pursue further. But I still think that this makes more sense in general for the macOS app and will likely be more useful later.
2025-09-02macOS: firstRect should return full rect width/heightMitchell Hashimoto
Fixes #2473 This commit changes `ghostty_surface_ime_point` to return a full rect with the width/height calculated for the preedit. The `firstRect` function, which calls `ghostty_surface_ime_point` was previously setting the width/height to zero. macOS didn't like this. We then changed it to just hardcode it to width/height of one cell. This worked but made it so the IME cursor didn't follow the preedit.
2025-09-02macOS: Notify macOS of cell width/height for firstRectMitchell Hashimoto
Related to #2473 This fixes an issue where the dictation icon didn't show the language picker.
2025-09-02config: bind both physical digit plus unicode digit for `goto_tab`Mitchell Hashimoto
Fixes #8478 The comments explain this.
2025-08-31macOS: Progress bar for OSC9 progress reportsMitchell Hashimoto
2025-08-19macos: show the copy menu item if we have any text selectedMitchell Hashimoto
2025-07-23Fix tab titles not being preserved with window-save-stateBryan Lee
Add serialization for tab titles in SurfaceView to persist user-set titles across app restarts. Bump TerminalRestorableState version to 4 to handle the new format.
2025-07-14fix: rename tab title popup focusWilliam Walker
2025-06-21The Big Renderer Rework (#7620)Qwerasd
It's here, the long-foretold and long-procrastinated renderer rework! Hopefully this makes it easier to adapt and modify the renderer in the future and ensures feature parity between Metal and OpenGL. Despite having been a lot of work to write initially, with the abstraction layer in place I feel like working on the renderer will be a much more pleasant experience going forward. ## Key points - CPU-side renderer logic is now mostly unified via a generic `Renderer`. - A graphics API abstraction layer over OpenGL and Metal has been introduced. - Minimum OpenGL version bumped to `4.3`, so can no longer be run on macOS; I used the nix VM stuff for my testing during development. (Edit by @mitchellh: Note for readers that Ghostty still works on macOS, but the OpenGL backend doesn't, only the Metal one) - The OpenGL backend now supports linear blending! Woohoo! The default `alpha-blending` has been updated to `linear-corrected` since it's essentially a strict improvement over `native`. The default on macOS is still `native` though to match other mac apps in appearance, since macOS users are more sensitive to text appearance. - Custom shaders can now be hot reloaded. - The background color is once again drawn by us, so custom shaders can interact with it properly. In general, custom shaders should be a little more robust. ## The abstraction layer The general hierarchy of the abstraction layer is as such: ``` [ GraphicsAPI ] - Responsible for configuring the runtime surface | | and providing render `Target`s that draw to it, | | as well as `Frame`s and `Pipeline`s. | V | [ Target ] - Represents an abstract target for rendering, which | could be a surface directly but is also used as an | abstraction for off-screen frame buffers. V [ Frame ] - Represents the context for drawing a given frame, | provides `RenderPass`es for issuing draw commands | to, and reports the frame health when complete. V [ RenderPass ] - Represents a render pass in a frame, consisting of : one or more `Step`s applied to the same target(s), [ Step ] - - - - each describing the input buffers and textures and : the vertex/fragment functions and geometry to use. :_ _ _ _ _ _ _ _ _ _/ v [ Pipeline ] - Describes a vertex and fragment function to be used for a `Step`; the `GraphicsAPI` is responsible for these and they should be constructed and cached ahead of time. [ Buffer ] - An abstraction over a GPU buffer. [ Texture ] - An abstraction over a GPU texture. ``` More specific documentation can be found on the relevant structures. ## Miscellany Renderers (which effectively just means the generic renderer) are now expected to only touch GPU resources in `init`, certain lifecycle functions such as the `displayRealized`/`displayUnrealized` callbacks from GTK-- and `drawFrame`; and are also expected to be thread-safe. This allows the renderer thread to build the CPU-side buffers (`updateFrame`) even if we can only *draw* from the app thread. Because of this change, we can draw synchronously from the main thread on macOS when necessary to always have a frame of the correct size during a resize animation. This was necessary to allow the background to be drawn by our GPU code (instead of setting a background color on the layer) while still avoiding holes during resize. The OpenGL backend now theoretically has access to multi-buffering, but it's disabled (by setting the buffer count to 1) because it synchronously waits for frames to complete anyway which means that the extra buffers were just a waste of memory. ## Validation To validate that there are no significant or obvious problems, I exercised both backends with a variety of configurations, and visually inspected the results. Everything looks to be in order. The images are available in a gist here: https://gist.github.com/qwerasd205/c1bd3e4c694d888e41612e53c0560179 ## Memory Here's a comparison of memory usage for ReleaseFast builds on macOS, between `main` and this branch. Memory figures given are values from Activity Monitor measuring windows of the same size, with two tabs with 3 splits each. ||Before|After| |-:|-|-| |**Memory**|247.9 MB|224.2 MB| |**Real Memory**|174.4 MB|172.5 MB| Happily, the rework has slightly *reduced* the memory footprint- likely due to removing the overhead of `CAMetalLayer`. (The footprint could be reduced much further if we got rid of multi-buffering and satisfied ourselves with blocking for each frame, but that's a discussion for another day.) If someone could do a similar comparison for Linux, that'd be much appreciated! ## Notes / future work - There are a couple structures that *can* be unified using the abstraction layer, but I haven't gotten around to unifying yet. Specifically, in `renderer/(opengl|metal)/`, there's `cell.zig` and `image.zig`, both of which are substantially identical between the two backends. `shaders.zig` may also be a candidate for unification, but that might be *overly* DRY. - ~~I did not double-check the documentation for config options, which may mention whether certain options can be hot-reloaded; if it does then that will need to be updated.~~ Fixed: be5908f - The `fragCoord` for custom shaders originates at the top left for Metal, but *bottom* left for OpenGL; fixing this will be a bit annoying, since the screen texture is likewise vertically flipped between the two. Some shaders rely on the fragcoord for things like falling particles, so this does need to be fixed. - `Target` should be improved to support multiple types of targets right now it only represents a framebuffer or iosurface, but it should also be able to represent a texture; right now a kind of messy tagged union is used so that steps can accept both. - Custom shader cursor uniforms (#6912) and terminal background images (#4226, #5233) should be much more straightforward to implement on top of this rework, and I plan to make follow-up PRs for them once this is merged. - I *do* want to do a rework of the pipelines themselves, since the way we're rendering stuff is a bit messy currently, but this is already a huge enough PR as it is- so for now the renderer still uses the same rendering passes that Metal did before. - We should probably add a system requirements section to the README where we can note the minimum required OpenGL version of `4.3`, any even slightly modern Linux system will support this, but it would be good to document it somewhere user-facing anyway. # TODO BEFORE MERGE - [x] Have multiple people test this on both macOS and linux. - [ ] ~~Have someone with a better dev setup on linux check for memory leaks and other problems.~~ (Skipped, will merge and let tip users figure this out, someone should *specifically* look for memory leaks before the next versioned release though.) - [x] Address any code review feedback.
2025-06-21macos: support env vars for surface config, clean up surface configMitchell Hashimoto
2025-06-21macos: move mousePos and mousScroll to Ghostty.SurfaceMitchell Hashimoto
2025-06-21macos: add mouse button intentMitchell Hashimoto
2025-06-21macos: input intentMitchell Hashimoto
2025-06-21macos: starting to work on new libghostty data modelsMitchell Hashimoto
2025-06-21macos: GetTerminalDetails intentMitchell Hashimoto
2025-06-20renderer: big rework, graphics API abstraction layers, unified logicQwerasd
This commit is very large, representing about a month of work with many interdependent changes that don't separate cleanly in to atomic commits. The main change here is unifying the renderer logic to a single generic renderer, implemented on top of an abstraction layer over OpenGL/Metal. I'll write a more complete summary of the changes in the description of the PR.
2025-06-15macos: complete more ax APIs for terminal accessibilityMitchell Hashimoto
2025-06-15macos: auto-expire cached screen contentsMitchell Hashimoto
2025-06-15macos: simple cache of screen contents for axMitchell Hashimoto
2025-06-15apprt/embedded: improve text reading APIs (selection, random points)Mitchell Hashimoto
2025-06-15macos: bare minimum terminal axMitchell Hashimoto
2025-06-14macos: menu item symbols for TahoeMitchell Hashimoto
This is recommended for macOS Tahoe and all standard menu items now have associated images. This makes our app look more polished and native for macOS Tahoe. For icon choice, I tried to copy other native macOS apps as much as possible, mostly from Xcode. It looks like a lot of apps aren't updated yet. I'm absolutely open to suggestions for better icons but I think these are a good starting point. One menu change is I moved "reset font size" above "increase font size" which better matches other apps (e.g. Terminal.app).
2025-06-07macos: show quick terminal on undo/redoMitchell Hashimoto
2025-06-07macos: address quick terminal basic functionality with new APIMitchell Hashimoto
2025-06-07macos: remove debug logMitchell Hashimoto
2025-06-07macos: implement a custom ExpiringUndoManager, setup undo for new/closeMitchell Hashimoto
2025-06-06macos: dismiss notifications on focus, application exitMitchell Hashimoto
I've only recently been using programs that use user notifications heavily and this commit addresses a number of annoyances I've encountered. 1. Notifications dispatched while the source terminal surface is focused are now only shown for a short time (3 seconds hardcoded) and then automatically dismiss. 2. Notifications are dismissed when the target surface becomes focused from an unfocused state. This dismissal happens immediately (no delay). 3. Notifications are dismissed when the application exits. 4. This fixes a bug where notification callbacks were modifying view state, but the notification center doesn't guarantee that the callback is called on the main thread. We now ensure that the callback is always called on the main thread.
2025-06-05macos: restoration for new split treeMitchell Hashimoto
2025-06-02macos: fix small memory leak in surface tree when closing splitsMitchell Hashimoto
This fixes a small memory leak I found where the `SplitNode.Leaf` was not being deinitialized properly when closing a split. It would get deinitialized the next time a split was made or the window was closed, so the leak wasn't big. The surface view underneath the split was also properly deinitialized because we forced it, so again, the leak was quite small. But conceptually this is a big problem, because when we change the surface tree we expect the deinit chain to propagate properly through the whole thing, _including_ to the SurfaceView. This fixes that by removing the `id(node)` call. I don't find this to be necessary anymore. I don't know when that happened but we've changed quite a lot in our split system since it was introduced. I'm also not 100% sure why the `id(node)` was causing a strong reference to begin with... which bothers me a bit. AI note: While I manually hunted this down, I started up Claude Code and Codex in separate tabs to also hunt for the memory leak. They both failed to find it and offered solutions that didn't work.
2025-05-11macOS: treat C-/ specially again to prevent beepMitchell Hashimoto
Fixes #7310
2025-05-09clean up bindings so that they match macOS menusMitchell Hashimoto
2025-04-28macos: key input that clears preedit without text shouldn't encodeMitchell Hashimoto
Fixes #7225
2025-04-19macOS: use KeyboardShortcut rather than homegrown KeyEquivalentMitchell Hashimoto
This replaces the use of our custom `Ghostty.KeyEquivalent` with the SwiftUI `KeyboardShortcut` type. This is a more standard way to represent keyboard shortcuts and lets us more tightly integrate with SwiftUI/AppKit when necessary over our custom type. Note that not all Ghostty triggers can be represented as KeyboardShortcut values because macOS itself does not support binding keys such as function keys (e.g. F1-F12) to KeyboardShortcuts. This isn't an issue since all input also passes through a lower level libghostty API which can handle all key events, we just can't show these keyboard shortcuts on things like the menu bar. This was already true before this commit.
2025-04-19macOS: Do not send control characters as UTF8 keyboard textMitchell Hashimoto
Fixes a regression where `ctrl+enter` was not encoding properly since our input stack changes.
2025-04-18macOS: translation mods should never have "control"Mitchell Hashimoto
This also lets us get rid of our `C-/` special handling to prevent a system beep.
2025-04-18macos: translationMods should be used for consumed mods calculationMitchell Hashimoto
Fixes #7131 Regression from #7121 Our consumed mods should not include "alt" if `macos-option-as-alt` is set. To do this, we need to calculate our consumed mods based on the actual translation event mods (if available, only available during keyDown).
2025-04-17macOS: handle preedit text changes outside of key eventMitchell Hashimoto
2025-04-17macOS/libghostty: rework keyboard input handlingMitchell Hashimoto
This is a large refactor of the keyboard input handling code in libghostty and macOS. Previously, libghostty did a lot of things that felt out of scope or was repeated work due to lacking context. For example, libghostty would do full key translation from key event to character (including unshifted translation) as well as managing dead key states and setting the proper preedit text. This is all information the apprt can and should have on its own. NSEvent on macOS already provides us with all of this information, there's no need to redo the work. The reason we did in the first place is mostly historical: libghostty powered our initial macOS port years ago when we didn't have an AppKit runtime yet. This cruft has already practically been the source of numerous issues, e.g. #5558, but many other hacks along the way, too. This commit pushes all preedit (e.g. dead key) handling and key translation including unshifted keys up into the caller of libghostty. Besides code cleanup, a practical benefit of this is that key event handling on macOS is now about 10x faster on average. That's because we're avoiding repeated key translations as well as other unnecessary work. This should have a meaningful impact on input latency but I didn't measure the full end-to-end latency. A scarier part of this commit is that key handling is not well tested since its a GUI component. I suspect we'll have some fallout for certain keyboard layouts or input methods, but I did my best to run through everything I could think of.
2025-04-15macOS: Implement basic bell features (no sound)Mitchell Hashimoto
Fixes #7099 This adds basic bell features to macOS to conceptually match the GTK implementation. When a bell is triggered, macOS will do the following: 1. Bounce the dock icon once, if the app isn't already in focus. 2. Add a bell emoji (🔔) to the title of the surface that triggered the bell. This emoji will be removed after the surface is focused or a keyboard event if the surface is already focused. This behavior matches iTerm2. This doesn't add an icon badge because macOS's dockTitle.badgeLabel API wasn't doing anything for me and I wasn't able to fully figure out why...
2025-04-13macOS: only emit a mouse exited position if we're not draggingMitchell Hashimoto
Fixes #7071 When the mouse is being actively dragged, AppKit continues to emit mouseDragged events which will update our position appropriately. The mouseExit event we were sending sends a synthetic (-1, -1) position which was causing a scroll up.
2025-04-04macos: replay control+key events that go to doCommandMitchell Hashimoto
Fixes #7000 Related to #6909, the same mechanism, but it turns out some control+keys are also handled in this same way (namely control+esc leads to "cancel" by default, which is not what we want).
2025-04-04macos: left mouse click while not focused doesn't encode to ptyMitchell Hashimoto
Fixes #2595 This fixes an issue where a left mouse click on a terminal while not focused would subsequently be encoded to the pty as a mouse event. This is atypical for macOS applications in general and wasn't something we wanted to do. We do, however, want to ensure our terminal gains focus when clicked without focus. Specifically, a split. This matches iTerm2 behavior and is rather nice. We had this behavior before but our logic to make this work before caused the issue this commit is fixing. I also tested this with command+click which is a common macOS shortcut to emit a mouse event without raising the focus of the target window. In this case, we will properly focus the split but will not encode the mouse event to the pty. I think we actually do a _better job_ here tha iTerm2 (but, subjective) because we do encode the pty event properly if the split is focused whereas iTerm2 never does.
2025-03-27macos: reset the last command key state when keyDown eventMitchell Hashimoto
Fixes a regression from #6909 See #6887 In certain scenarios, the last command key state would linger around (I could only see this happen with global keybinds for unknown reasons yet). This state is only meant to have an effect within the cycle of a single keybind and only so we can ensure an event reaches keyDown so it should be reset if keyDown is ever sent (since, by definition at that point, keyDown has been reached). I'm still not happy that this is necessary and I suspect there is a better root cause to resolve, but I'd rather get this fix in now and figure out the root cause later.
2025-03-25macos: remove special-case cmd+period handlingMitchell Hashimoto
Fixes #5522 This commit re-dispatches command inputs that are unhandled by our macOS app so they can be encoded to the pty and handled by the core libghostty key callback system. We've had a special case `cmd+period` handling in Ghostty for a very long time (since well into the private beta). `cmd+period` by default binds to "cancel" in macOS, so it doesn't encode to the pty. We don't handle "cancel" in any meaningful way in Ghostty, so we special-cased it to encode properly to the pty. However, as shown in #5522, if the user rebinds `cmd+period` at the system level to some other operation, then this is ignored and we encode it still. This isn't desirable, we just want to work around not caring about "cancel." The callback path that AppKit takes for key events is a bit convoluted. For command keys, it first calls `performKeyEquivalent`. If this returns false (we want to continue standard processing), then it calls EITHER `keyDown` or `doCommand(by:)`. It calls the latter if there is a standard system command that matches the key event. For `cmd+period` by default, this is "cancel." Unfortunately, from `doCommand` we can't say "oops, we don't want to handle this, please continue processing." Its too late. So, this commit stores the last command key event from `performKeyEquivalent` and if we reach `doCommand` for it without having called `keyDown`, we re-dispatch the event and send it to keyDown. I'm honestly pretty sus about this whole logic but it is scoped to only command-keys and I couldn't trigger any adverse behavior in my testing. It also definitely fixed #5522 as far as I could reproduce it before.
2025-02-16Add Split Left and Split Up to menumbrown379
2025-02-14Add tab title rename feature to macosAswin M Prabhu
2025-02-13macos: don't remove ctrl modifier for text inputMitchell Hashimoto
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