qt: phase 1.4 — retire MainWindow's mirror statics
The s_app / s_config / s_needsPremultiply / s_quitDelayMs mirror
caches that phase 1.0-1.3 kept on MainWindow are gone. Every read
goes through GhosttyApp::instance().{app,config,needsPremultiply,
quitDelayMs}() now.
- 28 sed-rewrites of the form `s_config` → `GhosttyApp::instance().
config()` (and friends) across the remaining MainWindow.cpp call
sites.
- configString and configBool cache the singleton's config() in a
local so the hot path doesn't double-call. closeAllWindows does
the same with app().
- MainWindow::config() and MainWindow::needsPremultiply() are now
out-of-line forwarders to the singleton; they keep the same
signature so external callers (GhosttySurface, InspectorWindow)
don't need to take a dependency on app/GhosttyApp.h.
- Drop the four static declarations from MainWindow.h, the four
definitions from MainWindow.cpp, the priming block in
initialize(), the mirror sync in reloadConfigGlobal, and the
nullify-on-teardown in ~MainWindow.
- GhosttyApp.h: forward-declare GhosttySurface — needed by
surfaceAlive(GhosttySurface *). Caught by the docker build (gcc
was implicitly typing it as `int *`).
Build: docker build --target qt SUCCEEDED. Phases 1.0 through 1.4
compile cleanly on Fedora 42 / Qt 6.10 with no warnings.
Co-Authored-By: claude-flow <ruv@ruv.net>
pull/12846/head
parent
f669aa0d7a
commit
687d4b05b9
|
|
@ -87,16 +87,17 @@ static QIcon bellAttentionIcon() {
|
|||
return cached;
|
||||
}
|
||||
|
||||
// Process-shared libghostty state — see MainWindow.h.
|
||||
//
|
||||
// s_app / s_config / s_needsPremultiply mirror GhosttyApp::instance()
|
||||
// so the rest of this file (and GhosttySurface) keep their existing
|
||||
// access patterns. Phase 1.2+ will retire them as call sites move
|
||||
// over to the singleton directly.
|
||||
ghostty_app_t MainWindow::s_app = nullptr;
|
||||
ghostty_config_t MainWindow::s_config = nullptr;
|
||||
bool MainWindow::s_needsPremultiply = false;
|
||||
int MainWindow::s_quitDelayMs = 0;
|
||||
// All process-shared libghostty state lives on GhosttyApp::instance().
|
||||
// MainWindow's config() and needsPremultiply() forward there so
|
||||
// external consumers (GhosttySurface, InspectorWindow) don't have to
|
||||
// take a dependency on app/GhosttyApp.h.
|
||||
ghostty_config_t MainWindow::config() const {
|
||||
return GhosttyApp::instance().config();
|
||||
}
|
||||
|
||||
bool MainWindow::needsPremultiply() const {
|
||||
return GhosttyApp::instance().needsPremultiply();
|
||||
}
|
||||
|
||||
MainWindow::MainWindow() {
|
||||
setWindowTitle(QStringLiteral("Ghastty"));
|
||||
|
|
@ -183,11 +184,8 @@ MainWindow::~MainWindow() {
|
|||
// GhosttyApp::teardown stops + frees the frame and quit timers,
|
||||
// drains qApp-targeted MetaCalls (so worker callbacks can't touch
|
||||
// a freed app), and ghostty_app_frees + ghostty_config_frees the
|
||||
// live handles. The local s_app / s_config mirrors are still in
|
||||
// use across this file; null them so they match the singleton.
|
||||
// live handles.
|
||||
GhosttyApp::instance().teardown();
|
||||
s_app = nullptr;
|
||||
s_config = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -383,17 +381,8 @@ static void openUrlByKind(const QString &url,
|
|||
}
|
||||
|
||||
bool MainWindow::initialize() {
|
||||
// First-call: build libghostty app + config via the singleton. The
|
||||
// singleton holds m_app / m_config / m_needsPremultiply; the
|
||||
// MainWindow s_app / s_config / s_needsPremultiply statics are now
|
||||
// forwarders that read it.
|
||||
// First-call: build libghostty app + config via the singleton.
|
||||
if (!GhosttyApp::instance().ensureInitialized()) return false;
|
||||
// Mirror the singleton's pointers into the legacy statics so the
|
||||
// existing call sites (the rest of MainWindow.cpp + GhosttySurface)
|
||||
// keep working unchanged. Phases 1.1+ swap them out.
|
||||
s_app = GhosttyApp::instance().app();
|
||||
s_config = GhosttyApp::instance().config();
|
||||
s_needsPremultiply = GhosttyApp::instance().needsPremultiply();
|
||||
|
||||
GhosttyApp::instance().registerWindow(this);
|
||||
|
||||
|
|
@ -416,7 +405,6 @@ bool MainWindow::initialize() {
|
|||
? static_cast<int>(std::min(delayMs, uint64_t(INT_MAX)))
|
||||
: 0;
|
||||
GhosttyApp::instance().setQuitDelayMs(delayMsInt);
|
||||
s_quitDelayMs = delayMsInt; // mirror; phase 1.3 retires the static
|
||||
QApplication::setQuitOnLastWindowClosed(quitAfter && delayMsInt == 0);
|
||||
}
|
||||
|
||||
|
|
@ -479,8 +467,8 @@ MainWindow *MainWindow::newWindow(ghostty_surface_t parent) {
|
|||
// NSWindow.cascadeTopLeft. Wayland compositors typically ignore
|
||||
// window placement requests; this is a hint at most.
|
||||
int16_t posX = 0, posY = 0;
|
||||
const bool haveX = configGet(s_config, &posX, "window-position-x");
|
||||
const bool haveY = configGet(s_config, &posY, "window-position-y");
|
||||
const bool haveX = configGet(GhosttyApp::instance().config(), &posX, "window-position-x");
|
||||
const bool haveY = configGet(GhosttyApp::instance().config(), &posY, "window-position-y");
|
||||
if (haveX && haveY) {
|
||||
w->move(posX, posY);
|
||||
} else {
|
||||
|
|
@ -502,7 +490,7 @@ MainWindow *MainWindow::newWindow(ghostty_surface_t parent) {
|
|||
if (!s_initialWindowConsumed) {
|
||||
s_initialWindowConsumed = true;
|
||||
bool initialWindow = true;
|
||||
configGet(s_config, &initialWindow, "initial-window");
|
||||
configGet(GhosttyApp::instance().config(), &initialWindow, "initial-window");
|
||||
wantsShow = initialWindow;
|
||||
}
|
||||
if (wantsShow) w->show();
|
||||
|
|
@ -543,7 +531,7 @@ void MainWindow::createFirstTab() {
|
|||
}
|
||||
|
||||
GhosttySurface *MainWindow::newTab(ghostty_surface_t parent) {
|
||||
auto *surface = new GhosttySurface(s_app, this, parent);
|
||||
auto *surface = new GhosttySurface(GhosttyApp::instance().app(), this, parent);
|
||||
m_surfaces.append(surface);
|
||||
|
||||
// The tab page hosts the tab's split tree (initially one surface).
|
||||
|
|
@ -576,7 +564,7 @@ GhosttySurface *MainWindow::splitSurface(
|
|||
const bool newAfter = dir == GHOSTTY_SPLIT_DIRECTION_RIGHT ||
|
||||
dir == GHOSTTY_SPLIT_DIRECTION_DOWN;
|
||||
|
||||
auto *surface = new GhosttySurface(s_app, this, target->surface());
|
||||
auto *surface = new GhosttySurface(GhosttyApp::instance().app(), this, target->surface());
|
||||
auto *splitter =
|
||||
new QSplitter(horizontal ? Qt::Horizontal : Qt::Vertical);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
|
|
@ -909,7 +897,8 @@ void MainWindow::closeAllWindows(bool thenQuit) {
|
|||
// + Cancel default — same style as confirmCloseSurfaces. Title /
|
||||
// verb track whether this is a Quit (process ends) or a
|
||||
// Close All Windows (process may stay alive).
|
||||
if (s_app && ghostty_app_needs_confirm_quit(s_app)) {
|
||||
ghostty_app_t app = GhosttyApp::instance().app();
|
||||
if (app && ghostty_app_needs_confirm_quit(app)) {
|
||||
const QString title = thenQuit ? QStringLiteral("Quit")
|
||||
: QStringLiteral("Close All Windows");
|
||||
const QString verb = thenQuit ? QStringLiteral("Quit")
|
||||
|
|
@ -986,7 +975,7 @@ void MainWindow::animateQuickTerminalIn() {
|
|||
show();
|
||||
raise();
|
||||
activateWindow();
|
||||
const int ms = quickTerminalAnimationMs(s_config);
|
||||
const int ms = quickTerminalAnimationMs(GhosttyApp::instance().config());
|
||||
if (ms <= 0) {
|
||||
setWindowOpacity(1.0);
|
||||
return;
|
||||
|
|
@ -1004,7 +993,7 @@ void MainWindow::animateQuickTerminalIn() {
|
|||
}
|
||||
|
||||
void MainWindow::animateQuickTerminalOut() {
|
||||
const int ms = quickTerminalAnimationMs(s_config);
|
||||
const int ms = quickTerminalAnimationMs(GhosttyApp::instance().config());
|
||||
if (ms <= 0) {
|
||||
hide();
|
||||
return;
|
||||
|
|
@ -1086,7 +1075,7 @@ void MainWindow::setupLayerShell() {
|
|||
|
||||
// quick-terminal-size: primary is the edge-perpendicular extent.
|
||||
ghostty_config_quick_terminal_size_s qsz = {};
|
||||
configGet(s_config, &qsz, "quick-terminal-size");
|
||||
configGet(GhosttyApp::instance().config(), &qsz, "quick-terminal-size");
|
||||
const auto toPx = [](const ghostty_quick_terminal_size_s &s, int dim,
|
||||
int fallback) -> int {
|
||||
switch (s.tag) {
|
||||
|
|
@ -1217,7 +1206,7 @@ void MainWindow::gotoSplit(GhosttySurface *from,
|
|||
// `bool`==1) under-sized the buffer and corrupted adjacent stack;
|
||||
// read into c_uint and mask the bits.
|
||||
unsigned int pzBits = 0;
|
||||
configGet(s_config, &pzBits, "split-preserve-zoom");
|
||||
configGet(GhosttyApp::instance().config(), &pzBits, "split-preserve-zoom");
|
||||
const bool preserveZoom = (pzBits & 0x1) != 0 && m_zoomed == from;
|
||||
|
||||
const auto centerOf = [](GhosttySurface *s) {
|
||||
|
|
@ -1381,7 +1370,7 @@ void MainWindow::ringBell(GhosttySurface *surface) {
|
|||
// If config-get succeeds with features=0, the user explicitly opted
|
||||
// out of every bell feature and we honor that.
|
||||
unsigned int features = 0;
|
||||
if (!configGet(s_config, &features, "bell-features")) {
|
||||
if (!configGet(GhosttyApp::instance().config(), &features, "bell-features")) {
|
||||
features = BellAttention;
|
||||
}
|
||||
if (features & BellAttention) QApplication::alert(this);
|
||||
|
|
@ -1438,7 +1427,7 @@ void MainWindow::playBellAudio() {
|
|||
m_bellPlayer->play();
|
||||
}
|
||||
|
||||
// Refresh every window's chrome from the current s_config: tab-bar
|
||||
// Refresh every window's chrome from the current GhosttyApp config: tab-bar
|
||||
// policy, colour scheme, blur — plus window-level state that
|
||||
// previously only applied at startup (window-decoration, fullscreen,
|
||||
// maximize) and the quit-after-last-window-closed delay.
|
||||
|
|
@ -1446,9 +1435,9 @@ void MainWindow::refreshChrome() {
|
|||
// Refresh app-scoped state. quit-after-last-window-closed[-delay]
|
||||
// can change the delay or the quitOnLastWindowClosed strategy at
|
||||
// runtime; mirrors the calculation in initialize().
|
||||
if (s_config) {
|
||||
if (GhosttyApp::instance().config()) {
|
||||
bool quitAfter = true;
|
||||
configGet(s_config, &quitAfter, "quit-after-last-window-closed");
|
||||
configGet(GhosttyApp::instance().config(), &quitAfter, "quit-after-last-window-closed");
|
||||
// Same Duration-decode workaround as initialize().
|
||||
const uint64_t delayNs = parseDurationNs(
|
||||
configValue(QStringLiteral("quit-after-last-window-closed-delay")), 0);
|
||||
|
|
@ -1457,7 +1446,6 @@ void MainWindow::refreshChrome() {
|
|||
? static_cast<int>(std::min(delayMs, uint64_t(INT_MAX)))
|
||||
: 0;
|
||||
GhosttyApp::instance().setQuitDelayMs(delayMsInt);
|
||||
s_quitDelayMs = delayMsInt; // mirror; phase 1.3 retires the static
|
||||
QApplication::setQuitOnLastWindowClosed(quitAfter && delayMsInt == 0);
|
||||
}
|
||||
|
||||
|
|
@ -1510,7 +1498,7 @@ void MainWindow::refreshChrome() {
|
|||
void MainWindow::reloadConfig() { reloadConfigGlobal(); }
|
||||
|
||||
void MainWindow::reloadConfigGlobal() {
|
||||
if (!s_app) return;
|
||||
if (!GhosttyApp::instance().app()) return;
|
||||
// Re-read the config from disk in the same order as initialize().
|
||||
ghostty_config_t config = ghostty_config_new();
|
||||
ghostty_config_load_default_files(config);
|
||||
|
|
@ -1521,14 +1509,12 @@ void MainWindow::reloadConfigGlobal() {
|
|||
// Push to libghostty. App.updateConfig propagates the config to every
|
||||
// surface and fires CONFIG_CHANGE back at us — which only refreshes
|
||||
// chrome, never re-pushes, so this does not loop.
|
||||
ghostty_app_update_config(s_app, config);
|
||||
ghostty_app_update_config(GhosttyApp::instance().app(), config);
|
||||
|
||||
// Hand the new config to the singleton, which frees the previous one
|
||||
// (in the right order — libghostty borrows the previous until update
|
||||
// completes) and recomputes needsPremultiply.
|
||||
GhosttyApp::instance().replaceConfig(config);
|
||||
s_config = GhosttyApp::instance().config();
|
||||
s_needsPremultiply = GhosttyApp::instance().needsPremultiply();
|
||||
|
||||
refreshChrome();
|
||||
|
||||
|
|
@ -1542,7 +1528,7 @@ void MainWindow::reloadConfigGlobal() {
|
|||
// forward compatibility — Qt doesn't currently post a copy
|
||||
// toast, but a future one will pick up the same gate.
|
||||
unsigned int notifBits = 0;
|
||||
const bool notifOk = configGet(s_config, ¬ifBits, "app-notifications");
|
||||
const bool notifOk = configGet(GhosttyApp::instance().config(), ¬ifBits, "app-notifications");
|
||||
// configGet failure → defaults (both bits set) so the feature
|
||||
// still works as documented.
|
||||
if (!notifOk) notifBits = 0x3;
|
||||
|
|
@ -1553,16 +1539,17 @@ void MainWindow::reloadConfigGlobal() {
|
|||
}
|
||||
|
||||
QString MainWindow::configString(const char *key) const {
|
||||
ghostty_config_t cfg = GhosttyApp::instance().config();
|
||||
const char *value = nullptr;
|
||||
if (!s_config ||
|
||||
!ghostty_config_get(s_config, &value, key, qstrlen(key)) || !value)
|
||||
if (!cfg || !ghostty_config_get(cfg, &value, key, qstrlen(key)) || !value)
|
||||
return {};
|
||||
return QString::fromUtf8(value);
|
||||
}
|
||||
|
||||
bool MainWindow::configBool(const char *key, bool fallback) const {
|
||||
bool value = fallback; // ghostty_config_get leaves it untouched if absent
|
||||
if (s_config) ghostty_config_get(s_config, &value, key, qstrlen(key));
|
||||
if (ghostty_config_t cfg = GhosttyApp::instance().config())
|
||||
ghostty_config_get(cfg, &value, key, qstrlen(key));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
@ -1887,7 +1874,7 @@ void MainWindow::applyWindowConfig() {
|
|||
scheme = Qt::ColorScheme::Light;
|
||||
} else if (theme == QLatin1String("ghostty")) {
|
||||
ghostty_config_color_s bg{};
|
||||
if (configGet(s_config, &bg, "background")) {
|
||||
if (configGet(GhosttyApp::instance().config(), &bg, "background")) {
|
||||
const double luma = 0.299 * bg.r + 0.587 * bg.g + 0.114 * bg.b;
|
||||
scheme = luma < 128.0 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
|
||||
}
|
||||
|
|
@ -1905,7 +1892,7 @@ void MainWindow::applyBlur() {
|
|||
// macOS-only negatives) means off, a positive radius means on. KWin
|
||||
// uses its own configured radius, so only on/off matters here.
|
||||
short blur = 0;
|
||||
configGet(s_config, &blur, "background-blur");
|
||||
configGet(GhosttyApp::instance().config(), &blur, "background-blur");
|
||||
applyWindowBlur(this, blur > 0);
|
||||
}
|
||||
|
||||
|
|
@ -2238,7 +2225,7 @@ bool MainWindow::onAction(ghostty_app_t, ghostty_target_s target,
|
|||
// abnormal threshold (default 250ms). Banner = "the process
|
||||
// died unexpectedly," not "the process exited."
|
||||
uint32_t threshold = 250;
|
||||
configGet(s_config, &threshold, "abnormal-command-exit-runtime");
|
||||
configGet(GhosttyApp::instance().config(), &threshold, "abnormal-command-exit-runtime");
|
||||
if (ce.runtime_ms < threshold) return true;
|
||||
const int code = static_cast<int>(ce.exit_code);
|
||||
post(src, [srcp, code]() {
|
||||
|
|
@ -2397,7 +2384,7 @@ bool MainWindow::onAction(ghostty_app_t, ghostty_target_s target,
|
|||
// "configGet failed; nothing to do" semantics.
|
||||
unsigned int actBits = 0;
|
||||
const bool actOk =
|
||||
configGet(s_config, &actBits, "notify-on-command-finish-action");
|
||||
configGet(GhosttyApp::instance().config(), &actBits, "notify-on-command-finish-action");
|
||||
// configGet failure → fall back to the documented defaults
|
||||
// (bell=true, notify=false) so the feature still works.
|
||||
if (!actOk) actBits = 0x1;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <QList>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
|
|
@ -71,8 +69,11 @@ public:
|
|||
// Update the tab label and window title for `surface`.
|
||||
void setSurfaceTitle(GhosttySurface *surface, const QString &title);
|
||||
|
||||
// The live libghostty config (for keybind lookups, etc.).
|
||||
ghostty_config_t config() const { return s_config; }
|
||||
// The live libghostty config (for keybind lookups, etc.). Forwards
|
||||
// to GhosttyApp::instance().config(); kept on MainWindow as a thin
|
||||
// shim so external callers (GhosttySurface, InspectorWindow) don't
|
||||
// need to take a dependency on app/GhosttyApp.h.
|
||||
ghostty_config_t config() const;
|
||||
|
||||
// UNDO / REDO close-tab/window. The libghostty actions carry no
|
||||
// payload — the apprt is responsible for tracking what was closed
|
||||
|
|
@ -105,7 +106,8 @@ public:
|
|||
// Whether a custom shader is configured. With one, libghostty's final
|
||||
// framebuffer is non-premultiplied and surfaces must premultiply it
|
||||
// before Qt composites (see GhosttySurface::premultiplyFramebuffer).
|
||||
bool needsPremultiply() const { return s_needsPremultiply; }
|
||||
// Forwards to GhosttyApp::instance().needsPremultiply().
|
||||
bool needsPremultiply() const;
|
||||
|
||||
// Whether `focus-follows-mouse` is enabled — a GhosttySurface grabs
|
||||
// focus when the pointer enters it.
|
||||
|
|
@ -274,18 +276,9 @@ private:
|
|||
// of `background-opacity`).
|
||||
bool m_opacityForcedOpaque = false;
|
||||
|
||||
// Process-shared libghostty state: one app and config drive every
|
||||
// window. Created by the first initialize(), freed with the last
|
||||
// window. The live window list lives on GhosttyApp; the s_app /
|
||||
// s_config / s_needsPremultiply statics here are mirror caches kept
|
||||
// in sync with GhosttyApp::instance() to limit phase-1 callsite
|
||||
// churn — they retire as call sites move to the singleton.
|
||||
static ghostty_app_t s_app;
|
||||
static ghostty_config_t s_config;
|
||||
static bool s_needsPremultiply; // a custom shader is configured
|
||||
// Mirror of GhosttyApp::quitDelayMs; phase 1.3 retires it when the
|
||||
// remaining call site (closeAllWindows) moves to the singleton.
|
||||
static int s_quitDelayMs; // 0 = no delay configured
|
||||
// The libghostty app + config + derived state all live on
|
||||
// GhosttyApp::instance(). MainWindow's config() / needsPremultiply()
|
||||
// accessors forward to it.
|
||||
|
||||
// Snapshot of a closed tab or window for undo/redo. `pageTitles`
|
||||
// holds each tab's last-known title (window snapshots have N tabs;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "ghostty.h"
|
||||
|
||||
class GhosttySurface;
|
||||
class MainWindow;
|
||||
class QTimer;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue