mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00

Surprise, @ghostty-org/gtk! Hopefully a happy one. This PR introduces the boilerplate for a new apprt I'm calling `gtk-ng`. The `gtk-ng` apprt is still GTK, but built up from first principles using the GObject type system, Blueprint files, etc. This will ultimately replace and become `gtk` (the `-ng` suffix will be stripped once we fully replace our existing GTK apprt). In this PR, the `gtk-ng` apprt does nothing more but show a "Hello, Ghostty" GTK window. It doesn't run a terminal, yet. 😄 I want to use this PR to introduce the boilerplate and share my motivations. Since `gtk-ng` and `gtk` are separate apprts, I can PR small, reviewable, and risky changes into `gtk-ng` rather than opening some mega-PR that replaces everything all at once. Simultaneously, we can continue to iterate on and maintain our shipping `gtk` apprt without dealing with conflicts. > [!IMPORTANT] > > To reiterate, this PR doesn't change anything about our `gtk` apprt. Builds by default will still use the `gtk` apprt and we can continue to build both `gtk` and `gtk-ng` side by side (actually, a very important property until we can be confident we've reached parity). ## A Refactor, Not a Rewrite The primary goal of this apprt is to _primarily_ be a **refactor, not a rewrite.** As much as possible, I'm going to be bringing over a lot of the same logic from `gtk` as long as it fits and makes sense, but applying it to our new structure and lifecycle. For example in this PR you can see how we handle style manager, cgroups, etc. and how that fits within the new `GhosttyApplication` class. Our GTK apprt from a business logic standpoint is _pretty damn good_ and _pretty damn stable_. There's no need to rock that boat and try to rewrite core logic such as input handling, X11/Wayland stuff, etc. It just has to be massaged into the new structure. ## Why? **Object-oriented, reference-counted systems are good for UI, actually.** Experience iterating on the non-trivial macOS application has really reaffirmed that OOP and memory managed systems are really, really nice for GUI. I'm not a huge OOP fan in general, but it fits GUI patterns extremely well. And memory management of any form (GC, Ref Counts, etc.) is important in GUIs where "objects" are handed off to various owners at different times, the most concrete example being: splits moving across windows or into an undo management system. **Blueprint and UI definitions have been a success.** These were introduced in an incremental way into the `apprt/gtk` (thanks ❤️ ) and have been great. But our existing non-GObject system makes it hard to go _all in_ on them, e.g. bindings. Moving to a full GObject-based system will let us fully adopt this. **`zig-gobject` is good and stable.** This didn't really exist when we started the GTK apprt (see the long history below). Since adopting it, its proven to be an excellent, stable dependency. I'm ready to go all-in on it. **Memory management has been a challenge.** Our mix of GObject and non-GObject lifetimes within the GTK apprt has consistently been a source of memory leaks at best and crashes at worst. For example, `Window`, `Surface`, `Tab`, etc. have weird lifetimes that we try to pair alongside their GTK counterparts and its nasty and I don't think anyone who maintains this will disagree. By representing all of these concepts as GObject or Widget subclasses, we'll align all their lifetimes as expected. **Personally, I've grown a lot, particularly from working on the macOS side.** I think all of us as programmers can agree that _programming in multiple languages makes us better programmers_. Similarly, building the macOS app has shown me patterns and techniques that would make our GTK-based application better. I'd like to bring those to the GTK side. (Likewise, I've improved the macOS side from periods of time working on the GTK side and I suspect that might happen again!) ### Longer Background It's easy to rewrite. And I think our maintainers know that I'm not a fan of rewrites. I think its the wrong decision most of the time. It is easy to look at "legacy" code (especially code you didn't write yourself), be disgusted, and think you can rewrite it all better. But no engineer sets out to create technical debt, and I think its worth respecting how and why some code came to be before embarking on something new. This section does that. #### The Beginning Ghostty started as a pure Zig-based GLFW app, with no concept of "apprt". It was Linux-only, and X11-only. At some point, I refactored out the "apprt" system in order to introduce GTK4 (GTK4 came before any macOS work). For the initial GTK4 work, I decided to just call into the libgtk C APIs directly. There were various contributing factors for this decision: 1. Zig was _rapidly_ changing, and we were on nightly Zig. This was around the Zig 0.11, 0.12 times. Taking on new Zig dependencies was really dangerous because Zig nightly could break all of us at any moment. 2. [`zig-gobject`](https://github.com/ianprime0509/zig-gobject) was brand new and unstable. Given point 1, I discarded it and did straight C APIs. 3. Ghostty itself was very simple. We didn't support tabs, we didn't support splits. We were still primarily concerned with making the terminal stable. We weren't on the "native UI" part of our roadmap quite yet. This was our initial foray in that direction. 4. On a personal level, I hadn't done real native GUI programming in a _long_ time (on any platform). Recall the macOS apprt didn't exist yet, either. Jumping into "plain old Zig" with "plain old C APIs" was a practical, no-nonsense way for me to get going. Given all this, I still believe I (it was only me then) made the right decision for the time. #### Zig, GTK apprt Stabilization Eventually, the factors listed above changed: (1) Zig stabilized more and Ghostty moved to stable Zig for various reasons. (2) `zig-gobject` became a mature, stable library. (3) Ghostty the application has become increasingly complex (in a good way, we support a ton of awesome "platform UI" features). Socially, the @ghostty-org/gtk subsystem team was created and is filled with people who are experienced with GTK and Linux in general. This team introduced more idiomatic GTK concepts into the project such as blueprint files, a `zig-gobject` migration, and more. The @ghostty-org/gtk subsystem maintainers have done an awesome job iterating on this change within the existing `apprt/gtk`. This has been often frustrating, but it was a pragmatic approach to move us towards the future and let us ship new features into GTK4 to pursue our platform UI goals. #### GUI Maturity We're now at the point where the core Ghostty terminal (the core, terminal emulation) is incredibly stable. We don't have exact numbers but we can confidently assume its used by thousands of people everyday for real, professional work. As such, most of the changes within the 1.1 and 1.2 cycle have been at the apprt/GUI layer, introducing significantly more complexity: localization, more X11/Wayland integrations, more text to native elements like the process exit overlay, etc. I recently rewrote the entire terminal, tab, and split data model in the macOS app to give us a better foundation for future functionality, and to improve our memory management story (surface leaks were a common problem before, and they haven't happened since since the lifetime of a surface is so much more obvious). This also let me iterate more quickly on more features such as undo/redo, but will also more easily enable things like split titles, merging splits into tabs/windows, etc. (not done yet). I think its time for this type of change within the GTK apprt as well. We have the collective real world experience and we've put in the work in iteration to understand what needs to be done.