@@ -7,6 +7,10 @@ const Window = @This();
7
7
8
8
const std = @import ("std" );
9
9
const builtin = @import ("builtin" );
10
+
11
+ const gtk = @import ("gtk" );
12
+ const gobject = @import ("gobject" );
13
+
10
14
const build_config = @import ("../../build_config.zig" );
11
15
const Allocator = std .mem .Allocator ;
12
16
const assert = std .debug .assert ;
@@ -73,11 +77,12 @@ pub const DerivedConfig = struct {
73
77
gtk_wide_tabs : bool ,
74
78
gtk_toolbar_style : configpkg.Config.GtkToolbarStyle ,
75
79
80
+ title : ? [:0 ]const u8 ,
76
81
maximize : bool ,
77
82
fullscreen : bool ,
78
83
window_decoration : configpkg.Config.WindowDecoration ,
79
84
80
- pub fn init (config : * const configpkg.Config ) DerivedConfig {
85
+ pub fn init (config : * const configpkg.Config , alloc : Allocator ) ! DerivedConfig {
81
86
return .{
82
87
.background_opacity = config .@"background-opacity" ,
83
88
.background_blur = config .@"background-blur" ,
@@ -88,11 +93,16 @@ pub const DerivedConfig = struct {
88
93
.gtk_wide_tabs = config .@"gtk-wide-tabs" ,
89
94
.gtk_toolbar_style = config .@"gtk-toolbar-style" ,
90
95
96
+ .title = if (config .title ) | t | alloc .dupe (u8 , t ) else null ,
91
97
.maximize = config .maximize ,
92
98
.fullscreen = config .fullscreen ,
93
99
.window_decoration = config .@"window-decoration" ,
94
100
};
95
101
}
102
+
103
+ pub fn deinit (self : * DerivedConfig , alloc : Allocator ) void {
104
+ if (self .title ) | t | alloc .free (t );
105
+ }
96
106
};
97
107
98
108
pub fn create (alloc : Allocator , app : * App ) ! * Window {
@@ -114,7 +124,7 @@ pub fn init(self: *Window, app: *App) !void {
114
124
self .* = .{
115
125
.app = app ,
116
126
.last_config = @intFromPtr (& app .config ),
117
- .config = DerivedConfig .init (& app .config ),
127
+ .config = DerivedConfig .init (& app .config , app . core_app . alloc ),
118
128
.window = undefined ,
119
129
.headerbar = undefined ,
120
130
.tab_overview = null ,
@@ -130,17 +140,10 @@ pub fn init(self: *Window, app: *App) !void {
130
140
131
141
self .window = @ptrCast (@alignCast (gtk_widget ));
132
142
133
- c .gtk_window_set_title (self .window , "Ghostty" );
134
- c .gtk_window_set_default_size (self .window , 1000 , 600 );
135
- c .gtk_widget_add_css_class (gtk_widget , "window" );
136
- c .gtk_widget_add_css_class (gtk_widget , "terminal-window" );
137
-
138
143
// GTK4 grabs F10 input by default to focus the menubar icon. We want
139
144
// to disable this so that terminal programs can capture F10 (such as htop)
140
145
c .gtk_window_set_handle_menubar_accel (self .window , 0 );
141
146
142
- c .gtk_window_set_icon_name (self .window , build_config .bundle_id );
143
-
144
147
// Create our box which will hold our widgets in the main content area.
145
148
const box = c .gtk_box_new (c .GTK_ORIENTATION_VERTICAL , 0 );
146
149
@@ -353,11 +356,7 @@ pub fn init(self: *Window, app: *App) !void {
353
356
if (! self .config .gtk_wide_tabs ) c .adw_tab_bar_set_expand_tabs (tab_bar , 0 );
354
357
}
355
358
356
- // If we want the window to be maximized, we do that here.
357
- if (self .config .maximize ) c .gtk_window_maximize (self .window );
358
-
359
- // If we are in fullscreen mode, new windows start fullscreen.
360
- if (self .config .fullscreen ) c .gtk_window_fullscreen (self .window );
359
+ try self .initAppearance ();
361
360
362
361
// Show the window
363
362
c .gtk_widget_show (gtk_widget );
@@ -367,36 +366,64 @@ pub fn updateConfig(
367
366
self : * Window ,
368
367
config : * const configpkg.Config ,
369
368
) ! void {
370
- // avoid multiple reconfigs when we have many surfaces contained in this
371
- // window using the integer value of config as a simple marker to know if
372
- // we've "seen" this particular config before
369
+ // HACK:
370
+ // Avoid multiple reconfigs when we have many surfaces contained in this
371
+ // window using the integer value of the config pointer as a simple marker
372
+ // to know if we've "seen" this particular config before.
373
+ //
374
+ // For the long run we should try to make the flow of config updates saner
375
+ // and avoid manual deduplication. See #5947 for more information.
373
376
const this_config = @intFromPtr (config );
374
377
if (self .last_config == this_config ) return ;
375
378
self .last_config = this_config ;
376
379
377
- self .config = DerivedConfig .init (config );
380
+ self .config = DerivedConfig .init (config , self . app . core_app . alloc );
378
381
379
382
// We always resync our appearance whenever the config changes.
380
383
try self .syncAppearance ();
381
384
}
382
385
386
+ /// Initializes the appearance of the window.
387
+ ///
388
+ /// Not all appearance changes *should* be applied when a config option is
389
+ /// reloaded (for instance, the maximize and fullscreen settings should only
390
+ /// apply to new windows), so we apply them here.
391
+ pub fn initAppearance (self : * Window ) ! void {
392
+ // FIXME: Remove once self.window has been migrated to use zig-gobject
393
+ const window : * gtk.Window = @ptrCast (self .window );
394
+
395
+ window .setTitle (self .config .title orelse "Ghostty" );
396
+ window .setDefaultSize (1000 , 600 );
397
+ window .as (gtk .Widget ).addCssClass ("window" );
398
+ window .as (gtk .Widget ).addCssClass ("terminal-window" );
399
+ window .setIconName (build_config .bundle_id );
400
+
401
+ if (self .config .maximize ) window .maximize ();
402
+ if (self .config .fullscreen ) window .fullscreen ();
403
+
404
+ try self .syncAppearance ();
405
+ }
406
+
383
407
/// Updates appearance based on config settings. Will be called once upon window
384
408
/// realization, every time the config is reloaded, and every time a window state
385
409
/// is toggled (un-/maximized, un-/fullscreened, window decorations toggled, etc.)
386
410
///
387
411
/// TODO: Many of the initial style settings in `create` could possibly be made
388
412
/// reactive by moving them here.
389
413
pub fn syncAppearance (self : * Window ) ! void {
414
+ // FIXME: Remove once self.window has been migrated to use zig-gobject
415
+ const window : * gtk.Window = @ptrCast (self .window );
416
+
390
417
const csd_enabled = self .winproto .clientSideDecorationEnabled ();
391
- c . gtk_window_set_decorated ( self . window , @intFromBool (csd_enabled ));
418
+ window . setDecorated ( @intFromBool (csd_enabled ));
392
419
393
420
// Fix any artifacting that may occur in window corners. The .ssd CSS
394
421
// class is defined in the GtkWindow documentation:
395
422
// https://docs.gtk.org/gtk4/class.Window.html#css-nodes. A definition
396
423
// for .ssd is provided by GTK and Adwaita.
397
- toggleCssClass (@ptrCast ( self . window ) , "csd" , csd_enabled );
398
- toggleCssClass (@ptrCast ( self . window ) , "ssd" , ! csd_enabled );
399
- toggleCssClass (@ptrCast ( self . window ) , "no-border-radius" , ! csd_enabled );
424
+ toggleCssClass (window , "csd" , csd_enabled );
425
+ toggleCssClass (window , "ssd" , ! csd_enabled );
426
+ toggleCssClass (window , "no-border-radius" , ! csd_enabled );
400
427
401
428
self .headerbar .setVisible (visible : {
402
429
// Never display the header bar when CSDs are disabled.
@@ -414,7 +441,7 @@ pub fn syncAppearance(self: *Window) !void {
414
441
});
415
442
416
443
toggleCssClass (
417
- @ptrCast ( self . window ) ,
444
+ window ,
418
445
"background" ,
419
446
self .config .background_opacity >= 1 ,
420
447
);
@@ -423,7 +450,7 @@ pub fn syncAppearance(self: *Window) !void {
423
450
// GTK version is before 4.16. The conditional is because above 4.16
424
451
// we use GTK CSS color variables.
425
452
toggleCssClass (
426
- @ptrCast ( self . window ) ,
453
+ window ,
427
454
"window-theme-ghostty" ,
428
455
! version .atLeast (4 , 16 , 0 ) and self .config .window_theme == .ghostty ,
429
456
);
@@ -451,23 +478,17 @@ pub fn syncAppearance(self: *Window) !void {
451
478
self .winproto .syncAppearance () catch | err | {
452
479
log .warn ("failed to sync winproto appearance error={}" , .{err });
453
480
};
454
-
455
- toggleCssClass (
456
- @ptrCast (self .window ),
457
- "background" ,
458
- self .config .background_opacity >= 1 ,
459
- );
460
481
}
461
482
462
483
fn toggleCssClass (
463
- widget : * c.GtkWidget ,
484
+ widget : anytype ,
464
485
class : [:0 ]const u8 ,
465
486
v : bool ,
466
487
) void {
467
488
if (v ) {
468
- c . gtk_widget_add_css_class ( widget , class );
489
+ widget . as ( gtk . Widget ). addCssClass ( class );
469
490
} else {
470
- c . gtk_widget_remove_css_class ( widget , class );
491
+ widget . as ( gtk . Widget ). removeCssClass ( class );
471
492
}
472
493
}
473
494
@@ -510,6 +531,7 @@ fn initActions(self: *Window) void {
510
531
511
532
pub fn deinit (self : * Window ) void {
512
533
self .winproto .deinit (self .app .core_app .alloc );
534
+ self .config .deinit (self .app .core_app .alloc );
513
535
514
536
if (self .adw_tab_overview_focus_timer ) | timer | {
515
537
_ = c .g_source_remove (timer );
0 commit comments