mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge pull request #1116 from clebs/feature/settings-shortcut
macos: Add settings shortcut
This commit is contained in:
@ -354,6 +354,7 @@ typedef struct {
|
|||||||
|
|
||||||
typedef void (*ghostty_runtime_wakeup_cb)(void *);
|
typedef void (*ghostty_runtime_wakeup_cb)(void *);
|
||||||
typedef const ghostty_config_t (*ghostty_runtime_reload_config_cb)(void *);
|
typedef const ghostty_config_t (*ghostty_runtime_reload_config_cb)(void *);
|
||||||
|
typedef void (*ghostty_runtime_open_config_cb)(void *);
|
||||||
typedef void (*ghostty_runtime_set_title_cb)(void *, const char *);
|
typedef void (*ghostty_runtime_set_title_cb)(void *, const char *);
|
||||||
typedef void (*ghostty_runtime_set_mouse_shape_cb)(void *, ghostty_mouse_shape_e);
|
typedef void (*ghostty_runtime_set_mouse_shape_cb)(void *, ghostty_mouse_shape_e);
|
||||||
typedef void (*ghostty_runtime_set_mouse_visibility_cb)(void *, bool);
|
typedef void (*ghostty_runtime_set_mouse_visibility_cb)(void *, bool);
|
||||||
@ -381,6 +382,7 @@ typedef struct {
|
|||||||
bool supports_selection_clipboard;
|
bool supports_selection_clipboard;
|
||||||
ghostty_runtime_wakeup_cb wakeup_cb;
|
ghostty_runtime_wakeup_cb wakeup_cb;
|
||||||
ghostty_runtime_reload_config_cb reload_config_cb;
|
ghostty_runtime_reload_config_cb reload_config_cb;
|
||||||
|
ghostty_runtime_open_config_cb open_config_cb;
|
||||||
ghostty_runtime_set_title_cb set_title_cb;
|
ghostty_runtime_set_title_cb set_title_cb;
|
||||||
ghostty_runtime_set_mouse_shape_cb set_mouse_shape_cb;
|
ghostty_runtime_set_mouse_shape_cb set_mouse_shape_cb;
|
||||||
ghostty_runtime_set_mouse_visibility_cb set_mouse_visibility_cb;
|
ghostty_runtime_set_mouse_visibility_cb set_mouse_visibility_cb;
|
||||||
@ -422,12 +424,14 @@ bool ghostty_config_get(ghostty_config_t, void *, const char *, uintptr_t);
|
|||||||
ghostty_input_trigger_s ghostty_config_trigger(ghostty_config_t, const char *, uintptr_t);
|
ghostty_input_trigger_s ghostty_config_trigger(ghostty_config_t, const char *, uintptr_t);
|
||||||
uint32_t ghostty_config_errors_count(ghostty_config_t);
|
uint32_t ghostty_config_errors_count(ghostty_config_t);
|
||||||
ghostty_error_s ghostty_config_get_error(ghostty_config_t, uint32_t);
|
ghostty_error_s ghostty_config_get_error(ghostty_config_t, uint32_t);
|
||||||
|
void ghostty_config_open();
|
||||||
|
|
||||||
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s *, ghostty_config_t);
|
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s *, ghostty_config_t);
|
||||||
void ghostty_app_free(ghostty_app_t);
|
void ghostty_app_free(ghostty_app_t);
|
||||||
bool ghostty_app_tick(ghostty_app_t);
|
bool ghostty_app_tick(ghostty_app_t);
|
||||||
void *ghostty_app_userdata(ghostty_app_t);
|
void *ghostty_app_userdata(ghostty_app_t);
|
||||||
void ghostty_app_keyboard_changed(ghostty_app_t);
|
void ghostty_app_keyboard_changed(ghostty_app_t);
|
||||||
|
void ghostty_app_open_config(ghostty_app_t);
|
||||||
void ghostty_app_reload_config(ghostty_app_t);
|
void ghostty_app_reload_config(ghostty_app_t);
|
||||||
bool ghostty_app_needs_confirm_quit(ghostty_app_t);
|
bool ghostty_app_needs_confirm_quit(ghostty_app_t);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
|
|||||||
)
|
)
|
||||||
|
|
||||||
/// Various menu items so that we can programmatically sync the keyboard shortcut with the Ghostty config.
|
/// Various menu items so that we can programmatically sync the keyboard shortcut with the Ghostty config.
|
||||||
|
@IBOutlet private var menuOpenConfig: NSMenuItem?
|
||||||
@IBOutlet private var menuReloadConfig: NSMenuItem?
|
@IBOutlet private var menuReloadConfig: NSMenuItem?
|
||||||
@IBOutlet private var menuQuit: NSMenuItem?
|
@IBOutlet private var menuQuit: NSMenuItem?
|
||||||
|
|
||||||
@ -212,6 +213,7 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
|
|||||||
private func syncMenuShortcuts() {
|
private func syncMenuShortcuts() {
|
||||||
guard ghostty.config != nil else { return }
|
guard ghostty.config != nil else { return }
|
||||||
|
|
||||||
|
syncMenuShortcut(action: "open_config", menuItem: self.menuOpenConfig)
|
||||||
syncMenuShortcut(action: "reload_config", menuItem: self.menuReloadConfig)
|
syncMenuShortcut(action: "reload_config", menuItem: self.menuReloadConfig)
|
||||||
syncMenuShortcut(action: "quit", menuItem: self.menuQuit)
|
syncMenuShortcut(action: "quit", menuItem: self.menuQuit)
|
||||||
|
|
||||||
@ -342,6 +344,10 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti
|
|||||||
|
|
||||||
//MARK: - IB Actions
|
//MARK: - IB Actions
|
||||||
|
|
||||||
|
@IBAction func openConfig(_ sender: Any?) {
|
||||||
|
ghostty.openConfig()
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func reloadConfig(_ sender: Any?) {
|
@IBAction func reloadConfig(_ sender: Any?) {
|
||||||
ghostty.reloadConfig()
|
ghostty.reloadConfig()
|
||||||
}
|
}
|
||||||
|
@ -147,6 +147,7 @@ extension Ghostty {
|
|||||||
supports_selection_clipboard: false,
|
supports_selection_clipboard: false,
|
||||||
wakeup_cb: { userdata in AppState.wakeup(userdata) },
|
wakeup_cb: { userdata in AppState.wakeup(userdata) },
|
||||||
reload_config_cb: { userdata in AppState.reloadConfig(userdata) },
|
reload_config_cb: { userdata in AppState.reloadConfig(userdata) },
|
||||||
|
open_config_cb: { userdata in AppState.openConfig(userdata) },
|
||||||
set_title_cb: { userdata, title in AppState.setTitle(userdata, title: title) },
|
set_title_cb: { userdata, title in AppState.setTitle(userdata, title: title) },
|
||||||
set_mouse_shape_cb: { userdata, shape in AppState.setMouseShape(userdata, shape: shape) },
|
set_mouse_shape_cb: { userdata, shape in AppState.setMouseShape(userdata, shape: shape) },
|
||||||
set_mouse_visibility_cb: { userdata, visible in AppState.setMouseVisibility(userdata, visible: visible) },
|
set_mouse_visibility_cb: { userdata, visible in AppState.setMouseVisibility(userdata, visible: visible) },
|
||||||
@ -267,6 +268,11 @@ extension Ghostty {
|
|||||||
NSApplication.shared.terminate(nil)
|
NSApplication.shared.terminate(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openConfig() {
|
||||||
|
guard let app = self.app else { return }
|
||||||
|
ghostty_app_open_config(app)
|
||||||
|
}
|
||||||
|
|
||||||
func reloadConfig() {
|
func reloadConfig() {
|
||||||
guard let app = self.app else { return }
|
guard let app = self.app else { return }
|
||||||
ghostty_app_reload_config(app)
|
ghostty_app_reload_config(app)
|
||||||
@ -489,6 +495,10 @@ extension Ghostty {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func openConfig(_ userdata: UnsafeMutableRawPointer?) {
|
||||||
|
ghostty_config_open();
|
||||||
|
}
|
||||||
|
|
||||||
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? {
|
static func reloadConfig(_ userdata: UnsafeMutableRawPointer?) -> ghostty_config_t? {
|
||||||
guard let newConfig = Self.loadConfig() else {
|
guard let newConfig = Self.loadConfig() else {
|
||||||
AppDelegate.logger.warning("failed to reload configuration")
|
AppDelegate.logger.warning("failed to reload configuration")
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
<outlet property="menuNewTab" destination="uTG-Vz-hJU" id="eMg-R3-SeS"/>
|
<outlet property="menuNewTab" destination="uTG-Vz-hJU" id="eMg-R3-SeS"/>
|
||||||
<outlet property="menuNewWindow" destination="Was-JA-tGl" id="lK7-3I-CPG"/>
|
<outlet property="menuNewWindow" destination="Was-JA-tGl" id="lK7-3I-CPG"/>
|
||||||
<outlet property="menuNextSplit" destination="bD7-ei-wKU" id="LeT-xw-eh4"/>
|
<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="menuPaste" destination="i27-pK-umN" id="ICc-X2-gV3"/>
|
||||||
<outlet property="menuPreviousSplit" destination="Lic-px-1wg" id="Rto-CG-yRe"/>
|
<outlet property="menuPreviousSplit" destination="Lic-px-1wg" id="Rto-CG-yRe"/>
|
||||||
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
||||||
@ -58,7 +59,11 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
|
||||||
|
<connections>
|
||||||
|
<action selector="openConfig:" target="bbz-4X-AYv" id="X65-fg-iWU"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
<menuItem title="Reload Configuration" id="KKH-XX-5py">
|
<menuItem title="Reload Configuration" id="KKH-XX-5py">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
10
src/App.zig
10
src/App.zig
@ -186,6 +186,7 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
|
|||||||
log.debug("mailbox message={s}", .{@tagName(message)});
|
log.debug("mailbox message={s}", .{@tagName(message)});
|
||||||
switch (message) {
|
switch (message) {
|
||||||
.reload_config => try self.reloadConfig(rt_app),
|
.reload_config => try self.reloadConfig(rt_app),
|
||||||
|
.open_config => try self.openConfig(rt_app),
|
||||||
.new_window => |msg| try self.newWindow(rt_app, msg),
|
.new_window => |msg| try self.newWindow(rt_app, msg),
|
||||||
.close => |surface| try self.closeSurface(surface),
|
.close => |surface| try self.closeSurface(surface),
|
||||||
.quit => try self.setQuit(),
|
.quit => try self.setQuit(),
|
||||||
@ -196,6 +197,12 @@ fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn openConfig(self: *App, rt_app: *apprt.App) !void {
|
||||||
|
_ = self;
|
||||||
|
log.debug("opening configuration", .{});
|
||||||
|
try rt_app.openConfig();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reloadConfig(self: *App, rt_app: *apprt.App) !void {
|
pub fn reloadConfig(self: *App, rt_app: *apprt.App) !void {
|
||||||
log.debug("reloading configuration", .{});
|
log.debug("reloading configuration", .{});
|
||||||
if (try rt_app.reloadConfig()) |new| {
|
if (try rt_app.reloadConfig()) |new| {
|
||||||
@ -274,6 +281,9 @@ pub const Message = union(enum) {
|
|||||||
/// all the active surfaces.
|
/// all the active surfaces.
|
||||||
reload_config: void,
|
reload_config: void,
|
||||||
|
|
||||||
|
// Open the configuration file
|
||||||
|
open_config: void,
|
||||||
|
|
||||||
/// Create a new terminal window.
|
/// Create a new terminal window.
|
||||||
new_window: NewWindow,
|
new_window: NewWindow,
|
||||||
|
|
||||||
|
@ -2550,6 +2550,8 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
.unbind => unreachable,
|
.unbind => unreachable,
|
||||||
.ignore => {},
|
.ignore => {},
|
||||||
|
|
||||||
|
.open_config => try self.app.openConfig(self.rt_app),
|
||||||
|
|
||||||
.reload_config => try self.app.reloadConfig(self.rt_app),
|
.reload_config => try self.app.reloadConfig(self.rt_app),
|
||||||
|
|
||||||
.csi, .esc => |data| {
|
.csi, .esc => |data| {
|
||||||
|
@ -47,6 +47,9 @@ pub const App = struct {
|
|||||||
/// called.
|
/// called.
|
||||||
reload_config: *const fn (AppUD) callconv(.C) ?*const Config,
|
reload_config: *const fn (AppUD) callconv(.C) ?*const Config,
|
||||||
|
|
||||||
|
/// Open the configuration file.
|
||||||
|
open_config: *const fn (AppUD) callconv(.C) void,
|
||||||
|
|
||||||
/// Called to set the title of the window.
|
/// Called to set the title of the window.
|
||||||
set_title: *const fn (SurfaceUD, [*]const u8) callconv(.C) void,
|
set_title: *const fn (SurfaceUD, [*]const u8) callconv(.C) void,
|
||||||
|
|
||||||
@ -160,6 +163,10 @@ pub const App = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn openConfig(self: *App) !void {
|
||||||
|
try configpkg.edit.open(self.core_app.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reloadConfig(self: *App) !?*const Config {
|
pub fn reloadConfig(self: *App) !?*const Config {
|
||||||
// Reload
|
// Reload
|
||||||
if (self.opts.reload_config(self.opts.userdata)) |new| {
|
if (self.opts.reload_config(self.opts.userdata)) |new| {
|
||||||
@ -1285,6 +1292,14 @@ pub const CAPI = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Open the configuration.
|
||||||
|
export fn ghostty_app_open_config(v: *App) void {
|
||||||
|
_ = v.core_app.openConfig(v) catch |err| {
|
||||||
|
log.err("error reloading config err={}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Reload the configuration.
|
/// Reload the configuration.
|
||||||
export fn ghostty_app_reload_config(v: *App) void {
|
export fn ghostty_app_reload_config(v: *App) void {
|
||||||
_ = v.core_app.reloadConfig(v) catch |err| {
|
_ = v.core_app.reloadConfig(v) catch |err| {
|
||||||
|
@ -217,6 +217,11 @@ pub fn terminate(self: *App) void {
|
|||||||
self.config.deinit();
|
self.config.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Open the configuration in the system editor.
|
||||||
|
pub fn openConfig(self: *App) !void {
|
||||||
|
try configpkg.edit.open(self.core_app.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
/// Reload the configuration. This should return the new configuration.
|
/// Reload the configuration. This should return the new configuration.
|
||||||
/// The old value can be freed immediately at this point assuming a
|
/// The old value can be freed immediately at this point assuming a
|
||||||
/// successful return.
|
/// successful return.
|
||||||
|
@ -3,6 +3,7 @@ const builtin = @import("builtin");
|
|||||||
pub usingnamespace @import("config/key.zig");
|
pub usingnamespace @import("config/key.zig");
|
||||||
pub const Config = @import("config/Config.zig");
|
pub const Config = @import("config/Config.zig");
|
||||||
pub const string = @import("config/string.zig");
|
pub const string = @import("config/string.zig");
|
||||||
|
pub const edit = @import("config/edit.zig");
|
||||||
pub const url = @import("config/url.zig");
|
pub const url = @import("config/url.zig");
|
||||||
|
|
||||||
// Field types
|
// Field types
|
||||||
|
@ -5,6 +5,7 @@ const global = &@import("../main.zig").state;
|
|||||||
|
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const c_get = @import("c_get.zig");
|
const c_get = @import("c_get.zig");
|
||||||
|
const edit = @import("edit.zig");
|
||||||
const Key = @import("key.zig").Key;
|
const Key = @import("key.zig").Key;
|
||||||
|
|
||||||
const log = std.log.scoped(.config);
|
const log = std.log.scoped(.config);
|
||||||
@ -120,6 +121,12 @@ export fn ghostty_config_get_error(self: *Config, idx: u32) Error {
|
|||||||
return .{ .message = err.message.ptr };
|
return .{ .message = err.message.ptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn ghostty_config_open() void {
|
||||||
|
edit.open(global.alloc) catch |err| {
|
||||||
|
log.err("error opening config in editor err={}", .{err});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Sync with ghostty_error_s
|
/// Sync with ghostty_error_s
|
||||||
const Error = extern struct {
|
const Error = extern struct {
|
||||||
message: [*:0]const u8 = "",
|
message: [*:0]const u8 = "",
|
||||||
|
@ -1121,7 +1121,11 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
|
|||||||
.{ .key = .comma, .mods = .{ .super = true, .shift = true } },
|
.{ .key = .comma, .mods = .{ .super = true, .shift = true } },
|
||||||
.{ .reload_config = {} },
|
.{ .reload_config = {} },
|
||||||
);
|
);
|
||||||
|
try result.keybind.set.put(
|
||||||
|
alloc,
|
||||||
|
.{ .key = .comma, .mods = .{ .super = true } },
|
||||||
|
.{ .open_config = {} },
|
||||||
|
);
|
||||||
try result.keybind.set.put(
|
try result.keybind.set.put(
|
||||||
alloc,
|
alloc,
|
||||||
.{ .key = .k, .mods = .{ .super = true } },
|
.{ .key = .k, .mods = .{ .super = true } },
|
||||||
|
29
src/config/edit.zig
Normal file
29
src/config/edit.zig
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const internal_os = @import("../os/main.zig");
|
||||||
|
const Command = @import("../Command.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.config);
|
||||||
|
|
||||||
|
/// Open the configuration in the OS default editor according to the default
|
||||||
|
/// paths the main config file could be in.
|
||||||
|
pub fn open(alloc_gpa: Allocator) !void {
|
||||||
|
// default location
|
||||||
|
const config_path = try internal_os.xdg.config(alloc_gpa, .{ .subdir = "ghostty/config" });
|
||||||
|
defer alloc_gpa.free(config_path);
|
||||||
|
|
||||||
|
// Try to create file and go on if it already exists
|
||||||
|
_ = std.fs.createFileAbsolute(
|
||||||
|
config_path,
|
||||||
|
.{ .exclusive = true },
|
||||||
|
) catch |err| {
|
||||||
|
switch (err) {
|
||||||
|
error.PathAlreadyExists => {},
|
||||||
|
else => return err,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try internal_os.open(alloc_gpa, config_path);
|
||||||
|
}
|
@ -214,6 +214,11 @@ pub const Action = union(enum) {
|
|||||||
/// focused terminal.
|
/// focused terminal.
|
||||||
inspector: InspectorMode,
|
inspector: InspectorMode,
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
open_config: void,
|
||||||
|
|
||||||
/// Reload the configuration. The exact meaning depends on the app runtime
|
/// Reload the configuration. The exact meaning depends on the app runtime
|
||||||
/// in use but this usually involves re-reading the configuration file
|
/// in use but this usually involves re-reading the configuration file
|
||||||
/// and applying any changes. Note that not all changes can be applied at
|
/// and applying any changes. Note that not all changes can be applied at
|
||||||
|
Reference in New Issue
Block a user