mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge branch 'ghostty-org:main' into invertCursor
This commit is contained in:
55
.github/DISCUSSION_TEMPLATE/issue-triage.yml
vendored
55
.github/DISCUSSION_TEMPLATE/issue-triage.yml
vendored
@ -1,4 +1,4 @@
|
||||
labels: ["needs confirmation"]
|
||||
labels: ["needs-confirmation"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@ -14,22 +14,37 @@ body:
|
||||
description: |
|
||||
Provide a detailed description of the issue. Include relevant information, such as:
|
||||
- The feature or configuration option you encounter the issue with.
|
||||
- The expected behavior.
|
||||
- The actual behavior (and how it deviates from the expected behavior, if it is not immediately obvious).
|
||||
- Relevant Ghostty logs or other stacktraces.
|
||||
- Relevant screenshots, screen recordings, or other supporting media (as needed).
|
||||
- Screenshots, screen recordings, or other supporting media (as needed).
|
||||
- If this is a regression of an existing issue that was closed or resolved, please include the previous item reference (Discussion, Issue, PR, commit) in your description.
|
||||
|
||||
>[!TIP]
|
||||
> [!TIP]
|
||||
> **Not sure what information to include?**
|
||||
> Here are some recommendations:
|
||||
> - **Input issues:** include your keyboard layout, a screenshot of the terminal inspector's logged keystrokes (Linux: <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>i</kbd>; MacOS: <kbd>cmd</kbd>+<kbd>alt</kbd>+<kbd>i</kbd>), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version.
|
||||
> - **Input issues:** include your keyboard layout, a screenshot of logged keystrokes from the terminal inspector's "Keyboard" tab (Linux: <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>i</kbd>; MacOS: <kbd>cmd</kbd>+<kbd>alt</kbd>+<kbd>i</kbd>), input method, Linux input method engine (IBus, Fcitx 5, or none) and its version.
|
||||
> - **Font issues:** include the problematic character(s), the output of `ghostty +show-face` for these character(s), and if they work in other applications.
|
||||
> - **VT issues (including image rendering issues):** attach an [asciinema](https://docs.asciinema.org/getting-started/) cast file, shell script, or text file for reproduction.
|
||||
> - **Terminal emulation issues (including image rendering issues):** attach an [asciinema](https://docs.asciinema.org/getting-started/) cast file, shell script, or text file for reproduction.
|
||||
> - **Renderer issues:** (Linux) include your OpenGL version, graphics card, driver version.
|
||||
|
||||
> - **Crashes:** (macOS) include the [Sentry UUID](https://github.com/ghostty-org/ghostty?tab=readme-ov-file#crash-reports); (Linux) try to reproduce using a debug build and provide the stack trace.
|
||||
placeholder: |
|
||||
Example: When using SSH to connect to my remote Linux machine from my local macOS device in Ghostty, I try to run `clear`, and the screen does not clear. Instead, I see the following error message printed to the terminal: `Error opening terminal: xterm-ghostty.`
|
||||
When using SSH to connect to my remote Linux machine from my local macOS device in Ghostty, I try to run `clear`, and the screen does not clear. Instead, I see the following error message printed to the terminal: `Error opening terminal: xterm-ghostty.`
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: |
|
||||
Describe how you expect Ghostty to behave in this situation. Include any relevant documentation links.
|
||||
placeholder: |
|
||||
The screen is cleared and the prompt is redrawn at the top of the window.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual Behavior
|
||||
description: |
|
||||
Describe how Ghostty actually behaves in this situation. If it is not immediately obvious how the actual behavior differs from the expected behavior described above, please be sure to mention the deviation specifically.
|
||||
placeholder: |
|
||||
The screen is not cleared, and an error is printed: `Error opening terminal: xterm-ghostty`.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@ -44,6 +59,12 @@ body:
|
||||
4. Observe `xterm-ghostty` error message above.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ghostty Logs
|
||||
description: |
|
||||
Provide any captured Ghostty logs or stacktraces during your issue reproduction in this field. On Linux, logs can be found by running `ghostty` from the command-line; on macOS, logs can be viewed with `sudo log stream --level debug --predicate 'subsystem=="com.mitchellh.ghostty"'` from another terminal emulator.
|
||||
render: text
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ghostty Version
|
||||
@ -93,9 +114,9 @@ body:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ghostty Configuration
|
||||
label: Minimal Ghostty Configuration
|
||||
description: |
|
||||
Please provide the minimum configuration needed to reproduce this issue. If you cannot determine this, paste the output of `ghostty +show-config` here.
|
||||
Please provide the **minimum** configuration needed to reproduce this issue. If you can still reproduce the issue with one of the lines removed, do not include that line. If and **only** if you are not able to determine this, paste the contents of your Ghostty configuration file here.
|
||||
placeholder: |
|
||||
font-family = CommitMono Nerd Font
|
||||
font-family-bold = CommitMono Nerd Font
|
||||
@ -112,15 +133,15 @@ body:
|
||||
attributes:
|
||||
label: Additional Relevant Configuration
|
||||
description: |
|
||||
If your issue involves other programs, tools, or applications in addition to Ghostty (e.g. Neovim, tmux, Zellij, etc.), please provide the minimum configuration needed for all relevant programs to reproduce the issue here. If you use custom CSS or shaders for Ghostty, also include them here, if applicable to your issue.
|
||||
If your issue involves other programs, tools, or applications in addition to Ghostty (e.g. Neovim, tmux, Zellij, etc.), please provide the minimum configuration and versions needed for all relevant programs to reproduce the issue here. If you use custom CSS or shaders for Ghostty, also include them here, if applicable to your issue.
|
||||
placeholder: |
|
||||
`tmux.conf`
|
||||
---
|
||||
#### `tmux.conf` (tmux 3.5a)
|
||||
```
|
||||
set -g default-terminal "tmux-256color"
|
||||
set-option -sa terminal-overrides ",xterm*:Tc"
|
||||
set -g base-index 1
|
||||
setw -g pane-base-index 1
|
||||
render: text
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
@ -137,3 +158,5 @@ body:
|
||||
required: true
|
||||
- label: I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion.
|
||||
required: true
|
||||
- label: I have checked the "Preview" tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (` ``` `) on separate lines.
|
||||
required: true
|
||||
|
1
.github/workflows/update-colorschemes.yml
vendored
1
.github/workflows/update-colorschemes.yml
vendored
@ -71,6 +71,7 @@ jobs:
|
||||
build.zig.zon.nix
|
||||
build.zig.zon.txt
|
||||
build.zig.zon.json
|
||||
flatpak/zig-packages.json
|
||||
body: |
|
||||
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
|
||||
labels: dependencies
|
||||
|
@ -103,8 +103,8 @@
|
||||
// Other
|
||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||
.iterm2_themes = .{
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz",
|
||||
.hash = "N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym",
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz",
|
||||
.hash = "N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
|
6
build.zig.zon.json
generated
6
build.zig.zon.json
generated
@ -54,10 +54,10 @@
|
||||
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||
},
|
||||
"N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym": {
|
||||
"N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A": {
|
||||
"name": "iterm2_themes",
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz",
|
||||
"hash": "sha256-Vy5muiJ3hJXcOvmFHLhqc+Dvdh74GG6+u/L+EsavDb0="
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz",
|
||||
"hash": "sha256-xpDitXpZrdU/EcgLyG4G0cEiT4r42viy+DJALmy2sQE="
|
||||
},
|
||||
"N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD": {
|
||||
"name": "libpng",
|
||||
|
6
build.zig.zon.nix
generated
6
build.zig.zon.nix
generated
@ -170,11 +170,11 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym";
|
||||
name = "N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A";
|
||||
path = fetchZigArtifact {
|
||||
name = "iterm2_themes";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz";
|
||||
hash = "sha256-Vy5muiJ3hJXcOvmFHLhqc+Dvdh74GG6+u/L+EsavDb0=";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz";
|
||||
hash = "sha256-xpDitXpZrdU/EcgLyG4G0cEiT4r42viy+DJALmy2sQE=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
2
build.zig.zon.txt
generated
2
build.zig.zon.txt
generated
@ -27,7 +27,7 @@ https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.
|
||||
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||
https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz
|
||||
https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz
|
||||
https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz
|
||||
https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz
|
||||
https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz
|
||||
|
@ -67,9 +67,9 @@
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/5233095e442645995e8af1fcb7b011478ee86f32.tar.gz",
|
||||
"dest": "vendor/p/N-V-__8AAA38OASk6VOHVXwuyGVAeYu0nghqa1RSIliXV5ym",
|
||||
"sha256": "572e66ba22778495dc3af9851cb86a73e0ef761ef8186ebebbf2fe12c6af0dbd"
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/1e4957e65005908993250f8f07be3f70e805195e.tar.gz",
|
||||
"dest": "vendor/p/N-V-__8AAHOASAQuLADcCSHLHEJiKFVLZiCD9Aq2rh5GT01A",
|
||||
"sha256": "c690e2b57a59add53f11c80bc86e06d1c1224f8af8daf8b2f832402e6cb6b101"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
|
@ -240,6 +240,9 @@ typedef enum {
|
||||
GHOSTTY_KEY_KP_DELETE,
|
||||
GHOSTTY_KEY_KP_BEGIN,
|
||||
|
||||
// special keys
|
||||
GHOSTTY_KEY_CONTEXT_MENU,
|
||||
|
||||
// modifiers
|
||||
GHOSTTY_KEY_LEFT_SHIFT,
|
||||
GHOSTTY_KEY_LEFT_CONTROL,
|
||||
|
@ -29,7 +29,6 @@ struct TerminalCommandPaletteView: View {
|
||||
let key = String(cString: c.action_key)
|
||||
switch (key) {
|
||||
case "toggle_tab_overview",
|
||||
"toggle_maximize",
|
||||
"toggle_window_decorations":
|
||||
return false
|
||||
default:
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Cocoa
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import GhosttyKit
|
||||
|
||||
/// A base class for windows that can contain Ghostty windows. This base class implements
|
||||
@ -71,6 +72,9 @@ class BaseTerminalController: NSWindowController,
|
||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||
private var derivedConfig: DerivedConfig
|
||||
|
||||
/// The cancellables related to our focused surface.
|
||||
private var focusedSurfaceCancellables: Set<AnyCancellable> = []
|
||||
|
||||
struct SavedFrame {
|
||||
let window: NSRect
|
||||
let screen: NSRect
|
||||
@ -115,6 +119,11 @@ class BaseTerminalController: NSWindowController,
|
||||
selector: #selector(ghosttyCommandPaletteDidToggle(_:)),
|
||||
name: .ghosttyCommandPaletteDidToggle,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(ghosttyMaximizeDidToggle(_:)),
|
||||
name: .ghosttyMaximizeDidToggle,
|
||||
object: nil)
|
||||
|
||||
// Listen for local events that we need to know of outside of
|
||||
// single surface handlers.
|
||||
@ -234,6 +243,13 @@ class BaseTerminalController: NSWindowController,
|
||||
toggleCommandPalette(nil)
|
||||
}
|
||||
|
||||
@objc private func ghosttyMaximizeDidToggle(_ notification: Notification) {
|
||||
guard let window else { return }
|
||||
guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard surfaceTree?.contains(view: surfaceView) ?? false else { return }
|
||||
window.zoom(nil)
|
||||
}
|
||||
|
||||
// MARK: Local Events
|
||||
|
||||
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
|
||||
@ -274,7 +290,26 @@ class BaseTerminalController: NSWindowController,
|
||||
func surfaceTreeDidChange() {}
|
||||
|
||||
func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) {
|
||||
let lastFocusedSurface = focusedSurface
|
||||
focusedSurface = to
|
||||
|
||||
// Important to cancel any prior subscriptions
|
||||
focusedSurfaceCancellables = []
|
||||
|
||||
// Setup our title listener. If we have a focused surface we always use that.
|
||||
// Otherwise, we try to use our last focused surface. In either case, we only
|
||||
// want to care if the surface is in the tree so we don't listen to titles of
|
||||
// closed surfaces.
|
||||
if let titleSurface = focusedSurface ?? lastFocusedSurface,
|
||||
surfaceTree?.contains(view: titleSurface) ?? false {
|
||||
// If we have a surface, we want to listen for title changes.
|
||||
titleSurface.$title
|
||||
.sink { [weak self] in self?.titleDidChange(to: $0) }
|
||||
.store(in: &focusedSurfaceCancellables)
|
||||
} else {
|
||||
// There is no surface to listen to titles for.
|
||||
titleDidChange(to: "👻")
|
||||
}
|
||||
}
|
||||
|
||||
func titleDidChange(to: String) {
|
||||
@ -361,14 +396,6 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
}
|
||||
|
||||
func fullscreenDidChange() {
|
||||
// For some reason focus can get lost when we change fullscreen. Regardless of
|
||||
// mode above we just move it back.
|
||||
if let focusedSurface {
|
||||
Ghostty.moveFocus(to: focusedSurface)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Clipboard Confirmation
|
||||
|
||||
@objc private func onConfirmClipboardRequest(notification: SwiftUI.Notification) {
|
||||
|
@ -121,9 +121,7 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
|
||||
override func fullscreenDidChange() {
|
||||
super.fullscreenDidChange()
|
||||
|
||||
func fullscreenDidChange() {
|
||||
// When our fullscreen state changes, we resync our appearance because some
|
||||
// properties change when fullscreen or not.
|
||||
guard let focusedSurface else { return }
|
||||
|
@ -8,9 +8,6 @@ protocol TerminalViewDelegate: AnyObject {
|
||||
/// Called when the currently focused surface changed. This can be nil.
|
||||
func focusedSurfaceDidChange(to: Ghostty.SurfaceView?)
|
||||
|
||||
/// The title of the terminal should change.
|
||||
func titleDidChange(to: String)
|
||||
|
||||
/// The URL of the pwd should change.
|
||||
func pwdDidChange(to: URL?)
|
||||
|
||||
@ -59,19 +56,10 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
|
||||
// Various state values sent back up from the currently focused terminals.
|
||||
@FocusedValue(\.ghosttySurfaceView) private var focusedSurface
|
||||
@FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle
|
||||
@FocusedValue(\.ghosttySurfacePwd) private var surfacePwd
|
||||
@FocusedValue(\.ghosttySurfaceZoomed) private var zoomedSplit
|
||||
@FocusedValue(\.ghosttySurfaceCellSize) private var cellSize
|
||||
|
||||
// The title for our window
|
||||
private var title: String {
|
||||
if let surfaceTitle, !surfaceTitle.isEmpty {
|
||||
return surfaceTitle
|
||||
}
|
||||
return "👻"
|
||||
}
|
||||
|
||||
// The pwd of the focused surface as a URL
|
||||
private var pwdURL: URL? {
|
||||
guard let surfacePwd, surfacePwd != "" else { return nil }
|
||||
@ -105,9 +93,6 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
self.delegate?.focusedSurfaceDidChange(to: newValue)
|
||||
}
|
||||
}
|
||||
.onChange(of: title) { newValue in
|
||||
self.delegate?.titleDidChange(to: newValue)
|
||||
}
|
||||
.onChange(of: pwdURL) { newValue in
|
||||
self.delegate?.pwdDidChange(to: newValue)
|
||||
}
|
||||
|
@ -526,6 +526,9 @@ extension Ghostty {
|
||||
case GHOSTTY_ACTION_TOGGLE_COMMAND_PALETTE:
|
||||
toggleCommandPalette(app, target: target)
|
||||
|
||||
case GHOSTTY_ACTION_TOGGLE_MAXIMIZE:
|
||||
toggleMaximize(app, target: target)
|
||||
|
||||
case GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL:
|
||||
toggleQuickTerminal(app, target: target)
|
||||
|
||||
@ -770,6 +773,29 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
private static func toggleMaximize(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s
|
||||
) {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("toggle maximize does nothing with an app target")
|
||||
return
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyMaximizeDidToggle,
|
||||
object: surfaceView
|
||||
)
|
||||
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private static func toggleVisibility(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s
|
||||
|
@ -45,8 +45,6 @@ extension Ghostty {
|
||||
/// this one.
|
||||
@Binding var zoomedSurface: SurfaceView?
|
||||
|
||||
@FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String?
|
||||
|
||||
var body: some View {
|
||||
let center = NotificationCenter.default
|
||||
let pubZoom = center.publisher(for: Notification.didToggleSplitZoom)
|
||||
@ -77,7 +75,6 @@ extension Ghostty {
|
||||
.onReceive(pubZoom) { onZoom(notification: $0) }
|
||||
}
|
||||
}
|
||||
.navigationTitle(surfaceTitle ?? "Ghostty")
|
||||
.id(node) // Needed for change detection on node
|
||||
} else {
|
||||
// On these events we want to reset the split state and call it.
|
||||
|
@ -31,7 +31,6 @@ extension Ghostty {
|
||||
}, right: {
|
||||
InspectorViewRepresentable(surfaceView: surfaceView)
|
||||
.focused($inspectorFocus)
|
||||
.focusedValue(\.ghosttySurfaceTitle, surfaceView.title)
|
||||
.focusedValue(\.ghosttySurfaceView, surfaceView)
|
||||
})
|
||||
}
|
||||
|
@ -279,6 +279,9 @@ extension Notification.Name {
|
||||
/// Ring the bell
|
||||
static let ghosttyBellDidRing = Notification.Name("com.mitchellh.ghostty.ghosttyBellDidRing")
|
||||
static let ghosttyCommandPaletteDidToggle = Notification.Name("com.mitchellh.ghostty.commandPaletteDidToggle")
|
||||
|
||||
/// Toggle maximize of current window
|
||||
static let ghosttyMaximizeDidToggle = Notification.Name("com.mitchellh.ghostty.maximizeDidToggle")
|
||||
}
|
||||
|
||||
// NOTE: I am moving all of these to Notification.Name extensions over time. This
|
||||
|
@ -6,14 +6,12 @@ extension Ghostty {
|
||||
/// Render a terminal for the active app in the environment.
|
||||
struct Terminal: View {
|
||||
@EnvironmentObject private var ghostty: Ghostty.App
|
||||
@FocusedValue(\.ghosttySurfaceTitle) private var surfaceTitle: String?
|
||||
|
||||
var body: some View {
|
||||
if let app = self.ghostty.app {
|
||||
SurfaceForApp(app) { surfaceView in
|
||||
SurfaceWrapper(surfaceView: surfaceView)
|
||||
}
|
||||
.navigationTitle(surfaceTitle ?? "Ghostty")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,7 +81,6 @@ extension Ghostty {
|
||||
|
||||
Surface(view: surfaceView, size: geo.size)
|
||||
.focused($surfaceFocus)
|
||||
.focusedValue(\.ghosttySurfaceTitle, title)
|
||||
.focusedValue(\.ghosttySurfacePwd, surfaceView.pwd)
|
||||
.focusedValue(\.ghosttySurfaceView, surfaceView)
|
||||
.focusedValue(\.ghosttySurfaceCellSize, surfaceView.cellSize)
|
||||
@ -496,15 +493,6 @@ extension FocusedValues {
|
||||
typealias Value = Ghostty.SurfaceView
|
||||
}
|
||||
|
||||
var ghosttySurfaceTitle: String? {
|
||||
get { self[FocusedGhosttySurfaceTitle.self] }
|
||||
set { self[FocusedGhosttySurfaceTitle.self] = newValue }
|
||||
}
|
||||
|
||||
struct FocusedGhosttySurfaceTitle: FocusedValueKey {
|
||||
typealias Value = String
|
||||
}
|
||||
|
||||
var ghosttySurfacePwd: String? {
|
||||
get { self[FocusedGhosttySurfacePwd.self] }
|
||||
set { self[FocusedGhosttySurfacePwd.self] = newValue }
|
||||
|
@ -171,6 +171,13 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
guard let savedState = SavedState(window) else { return }
|
||||
self.savedState = savedState
|
||||
|
||||
// Get our current first responder on this window. For non-native fullscreen
|
||||
// we have to restore this because for some reason the operations below
|
||||
// lose it (see: https://github.com/ghostty-org/ghostty/issues/6999).
|
||||
// I don't know the root cause here so if we can figure that out there may
|
||||
// be a nicer way than this.
|
||||
let firstResponder = window.firstResponder
|
||||
|
||||
// We hide the dock if the window is on a screen with the dock.
|
||||
// We must hide the dock FIRST then hide the menu:
|
||||
// If you specify autoHideMenuBar, it must be accompanied by either hideDock or autoHideDock.
|
||||
@ -207,6 +214,10 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
// https://github.com/ghostty-org/ghostty/issues/1996
|
||||
DispatchQueue.main.async {
|
||||
self.window.setFrame(self.fullscreenFrame(screen), display: true)
|
||||
if let firstResponder {
|
||||
self.window.makeFirstResponder(firstResponder)
|
||||
}
|
||||
|
||||
self.delegate?.fullscreenDidChange()
|
||||
}
|
||||
}
|
||||
@ -220,6 +231,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
let center = NotificationCenter.default
|
||||
center.removeObserver(self, name: NSWindow.didChangeScreenNotification, object: window)
|
||||
|
||||
// See enter where we do the same thing to understand why.
|
||||
let firstResponder = window.firstResponder
|
||||
|
||||
// Unhide our elements
|
||||
if savedState.dock {
|
||||
unhideDock()
|
||||
@ -258,6 +272,10 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
}
|
||||
}
|
||||
|
||||
if let firstResponder {
|
||||
window.makeFirstResponder(firstResponder)
|
||||
}
|
||||
|
||||
// Unset our saved state, we're restored!
|
||||
self.savedState = nil
|
||||
|
||||
@ -355,16 +373,23 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
self.styleMask = window.styleMask
|
||||
self.dock = window.screen?.hasDock ?? false
|
||||
|
||||
// We hide the menu only if this window is not on any fullscreen
|
||||
// spaces. We do this because fullscreen spaces already hide the
|
||||
// menu and if we insert/remove this presentation option we get
|
||||
// issues (see #7075)
|
||||
let activeSpace = CGSSpace.active()
|
||||
let spaces = CGSSpace.list(for: window.cgWindowId)
|
||||
if spaces.contains(activeSpace) {
|
||||
self.menu = activeSpace.type != .fullscreen
|
||||
if let cgWindowId = window.cgWindowId {
|
||||
// We hide the menu only if this window is not on any fullscreen
|
||||
// spaces. We do this because fullscreen spaces already hide the
|
||||
// menu and if we insert/remove this presentation option we get
|
||||
// issues (see #7075)
|
||||
let activeSpace = CGSSpace.active()
|
||||
let spaces = CGSSpace.list(for: cgWindowId)
|
||||
if spaces.contains(activeSpace) {
|
||||
self.menu = activeSpace.type != .fullscreen
|
||||
} else {
|
||||
self.menu = spaces.allSatisfy { $0.type != .fullscreen }
|
||||
}
|
||||
} else {
|
||||
self.menu = spaces.allSatisfy { $0.type != .fullscreen }
|
||||
// Window doesn't have a window device, its not visible or something.
|
||||
// In this case, we assume we can hide the menu. We may want to do
|
||||
// something more sophisticated but this works for now.
|
||||
self.menu = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,11 @@ import AppKit
|
||||
|
||||
extension NSWindow {
|
||||
/// Get the CGWindowID type for the window (used for low level CoreGraphics APIs).
|
||||
var cgWindowId: CGWindowID {
|
||||
CGWindowID(windowNumber)
|
||||
var cgWindowId: CGWindowID? {
|
||||
// "If the window doesn’t have a window device, the value of this
|
||||
// property is equal to or less than 0." - Docs. In practice I've
|
||||
// found this is true if a window is not visible.
|
||||
guard windowNumber > 0 else { return nil }
|
||||
return CGWindowID(windowNumber)
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
pub const c = @import("c.zig").c;
|
||||
|
||||
// OpenGL
|
||||
pub extern fn ImGui_ImplOpenGL3_Init(?[*:0]const u8) callconv(.C) bool;
|
||||
pub extern fn ImGui_ImplOpenGL3_Shutdown() callconv(.C) void;
|
||||
pub extern fn ImGui_ImplOpenGL3_NewFrame() callconv(.C) void;
|
||||
pub extern fn ImGui_ImplOpenGL3_RenderDrawData(*c.ImDrawData) callconv(.C) void;
|
||||
pub extern fn ImGui_ImplOpenGL3_Init(?[*:0]const u8) callconv(.c) bool;
|
||||
pub extern fn ImGui_ImplOpenGL3_Shutdown() callconv(.c) void;
|
||||
pub extern fn ImGui_ImplOpenGL3_NewFrame() callconv(.c) void;
|
||||
pub extern fn ImGui_ImplOpenGL3_RenderDrawData(*c.ImDrawData) callconv(.c) void;
|
||||
|
||||
// Metal
|
||||
pub extern fn ImGui_ImplMetal_Init(*anyopaque) callconv(.C) bool;
|
||||
pub extern fn ImGui_ImplMetal_Shutdown() callconv(.C) void;
|
||||
pub extern fn ImGui_ImplMetal_NewFrame(*anyopaque) callconv(.C) void;
|
||||
pub extern fn ImGui_ImplMetal_RenderDrawData(*c.ImDrawData, *anyopaque, *anyopaque) callconv(.C) void;
|
||||
pub extern fn ImGui_ImplMetal_Init(*anyopaque) callconv(.c) bool;
|
||||
pub extern fn ImGui_ImplMetal_Shutdown() callconv(.c) void;
|
||||
pub extern fn ImGui_ImplMetal_NewFrame(*anyopaque) callconv(.c) void;
|
||||
pub extern fn ImGui_ImplMetal_RenderDrawData(*c.ImDrawData, *anyopaque, *anyopaque) callconv(.c) void;
|
||||
|
||||
// OSX
|
||||
pub extern fn ImGui_ImplOSX_Init(*anyopaque) callconv(.C) bool;
|
||||
pub extern fn ImGui_ImplOSX_Shutdown() callconv(.C) void;
|
||||
pub extern fn ImGui_ImplOSX_NewFrame(*anyopaque) callconv(.C) void;
|
||||
pub extern fn ImGui_ImplOSX_Init(*anyopaque) callconv(.c) bool;
|
||||
pub extern fn ImGui_ImplOSX_Shutdown() callconv(.c) void;
|
||||
pub extern fn ImGui_ImplOSX_NewFrame(*anyopaque) callconv(.c) void;
|
||||
|
||||
test {}
|
||||
|
@ -333,7 +333,7 @@ pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Eve
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn joystickCallbackWrapper(jid: c_int, event: c_int) callconv(.C) void {
|
||||
pub fn joystickCallbackWrapper(jid: c_int, event: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
Joystick{ .jid = @as(Joystick.Id, @enumFromInt(jid)) },
|
||||
@as(Event, @enumFromInt(event)),
|
||||
|
@ -389,7 +389,7 @@ pub inline fn setCallback(comptime callback: ?fn (monitor: Monitor, event: Event
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn monitorCallbackWrapper(monitor: ?*c.GLFWmonitor, event: c_int) callconv(.C) void {
|
||||
pub fn monitorCallbackWrapper(monitor: ?*c.GLFWmonitor, event: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
Monitor{ .handle = monitor.? },
|
||||
@as(Event, @enumFromInt(event)),
|
||||
|
@ -1230,7 +1230,7 @@ pub inline fn setPosCallback(self: Window, comptime callback: ?fn (window: Windo
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn posCallbackWrapper(handle: ?*c.GLFWwindow, xpos: c_int, ypos: c_int) callconv(.C) void {
|
||||
pub fn posCallbackWrapper(handle: ?*c.GLFWwindow, xpos: c_int, ypos: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
@as(i32, @intCast(xpos)),
|
||||
@ -1263,7 +1263,7 @@ pub inline fn setSizeCallback(self: Window, comptime callback: ?fn (window: Wind
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn sizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
|
||||
pub fn sizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
@as(i32, @intCast(width)),
|
||||
@ -1304,7 +1304,7 @@ pub inline fn setCloseCallback(self: Window, comptime callback: ?fn (window: Win
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn closeCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.C) void {
|
||||
pub fn closeCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
});
|
||||
@ -1341,7 +1341,7 @@ pub inline fn setRefreshCallback(self: Window, comptime callback: ?fn (window: W
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn refreshCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.C) void {
|
||||
pub fn refreshCallbackWrapper(handle: ?*c.GLFWwindow) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
});
|
||||
@ -1379,7 +1379,7 @@ pub inline fn setFocusCallback(self: Window, comptime callback: ?fn (window: Win
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn focusCallbackWrapper(handle: ?*c.GLFWwindow, focused: c_int) callconv(.C) void {
|
||||
pub fn focusCallbackWrapper(handle: ?*c.GLFWwindow, focused: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
focused == c.GLFW_TRUE,
|
||||
@ -1413,7 +1413,7 @@ pub inline fn setIconifyCallback(self: Window, comptime callback: ?fn (window: W
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn iconifyCallbackWrapper(handle: ?*c.GLFWwindow, iconified: c_int) callconv(.C) void {
|
||||
pub fn iconifyCallbackWrapper(handle: ?*c.GLFWwindow, iconified: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
iconified == c.GLFW_TRUE,
|
||||
@ -1448,7 +1448,7 @@ pub inline fn setMaximizeCallback(self: Window, comptime callback: ?fn (window:
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn maximizeCallbackWrapper(handle: ?*c.GLFWwindow, maximized: c_int) callconv(.C) void {
|
||||
pub fn maximizeCallbackWrapper(handle: ?*c.GLFWwindow, maximized: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
maximized == c.GLFW_TRUE,
|
||||
@ -1483,7 +1483,7 @@ pub inline fn setFramebufferSizeCallback(self: Window, comptime callback: ?fn (w
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn framebufferSizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.C) void {
|
||||
pub fn framebufferSizeCallbackWrapper(handle: ?*c.GLFWwindow, width: c_int, height: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
@as(u32, @intCast(width)),
|
||||
@ -1519,7 +1519,7 @@ pub inline fn setContentScaleCallback(self: Window, comptime callback: ?fn (wind
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn windowScaleCallbackWrapper(handle: ?*c.GLFWwindow, xscale: f32, yscale: f32) callconv(.C) void {
|
||||
pub fn windowScaleCallbackWrapper(handle: ?*c.GLFWwindow, xscale: f32, yscale: f32) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
xscale,
|
||||
@ -1871,7 +1871,7 @@ pub inline fn setKeyCallback(self: Window, comptime callback: ?fn (window: Windo
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn keyCallbackWrapper(handle: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
pub fn keyCallbackWrapper(handle: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, mods: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
@as(Key, @enumFromInt(key)),
|
||||
@ -1917,7 +1917,7 @@ pub inline fn setCharCallback(self: Window, comptime callback: ?fn (window: Wind
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn charCallbackWrapper(handle: ?*c.GLFWwindow, codepoint: c_uint) callconv(.C) void {
|
||||
pub fn charCallbackWrapper(handle: ?*c.GLFWwindow, codepoint: c_uint) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
@as(u21, @intCast(codepoint)),
|
||||
@ -1958,7 +1958,7 @@ pub inline fn setMouseButtonCallback(self: Window, comptime callback: ?fn (windo
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn mouseButtonCallbackWrapper(handle: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.C) void {
|
||||
pub fn mouseButtonCallbackWrapper(handle: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
@as(MouseButton, @enumFromInt(button)),
|
||||
@ -1996,7 +1996,7 @@ pub inline fn setCursorPosCallback(self: Window, comptime callback: ?fn (window:
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn cursorPosCallbackWrapper(handle: ?*c.GLFWwindow, xpos: f64, ypos: f64) callconv(.C) void {
|
||||
pub fn cursorPosCallbackWrapper(handle: ?*c.GLFWwindow, xpos: f64, ypos: f64) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
xpos,
|
||||
@ -2030,7 +2030,7 @@ pub inline fn setCursorEnterCallback(self: Window, comptime callback: ?fn (windo
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn cursorEnterCallbackWrapper(handle: ?*c.GLFWwindow, entered: c_int) callconv(.C) void {
|
||||
pub fn cursorEnterCallbackWrapper(handle: ?*c.GLFWwindow, entered: c_int) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
entered == c.GLFW_TRUE,
|
||||
@ -2067,7 +2067,7 @@ pub inline fn setScrollCallback(self: Window, comptime callback: ?fn (window: Wi
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn scrollCallbackWrapper(handle: ?*c.GLFWwindow, xoffset: f64, yoffset: f64) callconv(.C) void {
|
||||
pub fn scrollCallbackWrapper(handle: ?*c.GLFWwindow, xoffset: f64, yoffset: f64) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
xoffset,
|
||||
@ -2110,7 +2110,7 @@ pub inline fn setDropCallback(self: Window, comptime callback: ?fn (window: Wind
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn dropCallbackWrapper(handle: ?*c.GLFWwindow, path_count: c_int, paths: [*c][*c]const u8) callconv(.C) void {
|
||||
pub fn dropCallbackWrapper(handle: ?*c.GLFWwindow, path_count: c_int, paths: [*c][*c]const u8) callconv(.c) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
from(handle.?),
|
||||
@as([*][*:0]const u8, @ptrCast(paths))[0..@as(u32, @intCast(path_count))],
|
||||
|
@ -300,7 +300,7 @@ pub inline fn mustGetErrorString() [:0]const u8 {
|
||||
pub fn setErrorCallback(comptime callback: ?fn (error_code: ErrorCode, description: [:0]const u8) void) void {
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn errorCallbackWrapper(err_int: c_int, c_description: [*c]const u8) callconv(.C) void {
|
||||
pub fn errorCallbackWrapper(err_int: c_int, c_description: [*c]const u8) callconv(.c) void {
|
||||
convertError(err_int) catch |error_code| {
|
||||
user_callback(error_code, mem.sliceTo(c_description, 0));
|
||||
};
|
||||
|
@ -161,7 +161,7 @@ pub const GLProc = *const fn () callconv(if (builtin.os.tag == .windows and buil
|
||||
/// @thread_safety This function may be called from any thread.
|
||||
///
|
||||
/// see also: context_glext, glfwExtensionSupported
|
||||
pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.C) ?GLProc {
|
||||
pub fn getProcAddress(proc_name: [*:0]const u8) callconv(.c) ?GLProc {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwGetProcAddress(proc_name)) |proc_address| return @ptrCast(proc_address);
|
||||
return null;
|
||||
|
@ -33,7 +33,7 @@ pub fn initVulkanLoader(loader_function: ?VKGetInstanceProcAddr) void {
|
||||
c.glfwInitVulkanLoader(loader_function orelse null);
|
||||
}
|
||||
|
||||
pub const VKGetInstanceProcAddr = *const fn (vk_instance: c.VkInstance, name: [*c]const u8) callconv(.C) ?VKProc;
|
||||
pub const VKGetInstanceProcAddr = *const fn (vk_instance: c.VkInstance, name: [*c]const u8) callconv(.c) ?VKProc;
|
||||
|
||||
/// Returns whether the Vulkan loader and an ICD have been found.
|
||||
///
|
||||
@ -127,7 +127,7 @@ pub const VKProc = *const fn () callconv(if (builtin.os.tag == .windows and buil
|
||||
/// @pointer_lifetime The returned function pointer is valid until the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread.
|
||||
pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.C) ?VKProc {
|
||||
pub fn getInstanceProcAddress(vk_instance: ?*anyopaque, proc_name: [*:0]const u8) callconv(.c) ?VKProc {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwGetInstanceProcAddress(if (vk_instance) |v| @as(c.VkInstance, @ptrCast(v)) else null, proc_name)) |proc_address| return proc_address;
|
||||
return null;
|
||||
|
@ -77,11 +77,11 @@ pub const Blob = struct {
|
||||
comptime T: type,
|
||||
key: ?*anyopaque,
|
||||
ptr: ?*T,
|
||||
comptime destroycb: ?*const fn (?*T) callconv(.C) void,
|
||||
comptime destroycb: ?*const fn (?*T) callconv(.c) void,
|
||||
replace: bool,
|
||||
) bool {
|
||||
const Callback = struct {
|
||||
pub fn callback(data: ?*anyopaque) callconv(.C) void {
|
||||
pub fn callback(data: ?*anyopaque) callconv(.c) void {
|
||||
@call(.{ .modifier = .always_inline }, destroycb, .{
|
||||
@as(?*T, @ptrCast(@alignCast(data))),
|
||||
});
|
||||
|
@ -84,7 +84,7 @@ pub const MutableArray = opaque {
|
||||
a: *const Elem,
|
||||
b: *const Elem,
|
||||
context: ?*Context,
|
||||
) callconv(.C) ComparisonResult,
|
||||
) callconv(.c) ComparisonResult,
|
||||
) void {
|
||||
CFArraySortValues(
|
||||
self,
|
||||
@ -155,7 +155,7 @@ test "array sorting" {
|
||||
void,
|
||||
null,
|
||||
struct {
|
||||
fn compare(a: *const u8, b: *const u8, _: ?*void) callconv(.C) ComparisonResult {
|
||||
fn compare(a: *const u8, b: *const u8, _: ?*void) callconv(.c) ComparisonResult {
|
||||
if (a.* > b.*) return .greater;
|
||||
if (a.* == b.*) return .equal;
|
||||
return .less;
|
||||
|
@ -66,7 +66,7 @@ pub const DisplayLink = opaque {
|
||||
flagsIn: c.CVOptionFlags,
|
||||
flagsOut: *c.CVOptionFlags,
|
||||
inner_userinfo: ?*anyopaque,
|
||||
) callconv(.C) c.CVReturn {
|
||||
) callconv(.c) c.CVReturn {
|
||||
_ = inNow;
|
||||
_ = inOutputTime;
|
||||
_ = flagsIn;
|
||||
|
@ -13,8 +13,8 @@ pub threadlocal var context: Context = undefined;
|
||||
/// The getProcAddress param is an anytype so that we can accept multiple
|
||||
/// forms of the function depending on what we're interfacing with.
|
||||
pub fn load(getProcAddress: anytype) !c_int {
|
||||
const GlProc = *const fn () callconv(.C) void;
|
||||
const GlfwFn = *const fn ([*:0]const u8) callconv(.C) ?GlProc;
|
||||
const GlProc = *const fn () callconv(.c) void;
|
||||
const GlfwFn = *const fn ([*:0]const u8) callconv(.c) ?GlProc;
|
||||
|
||||
const res = switch (@TypeOf(getProcAddress)) {
|
||||
// glfw
|
||||
|
@ -5,8 +5,8 @@ const Envelope = @import("envelope.zig").Envelope;
|
||||
|
||||
/// sentry_transport_t
|
||||
pub const Transport = opaque {
|
||||
pub const SendFunc = *const fn (envelope: *Envelope, state: ?*anyopaque) callconv(.C) void;
|
||||
pub const FreeFunc = *const fn (state: ?*anyopaque) callconv(.C) void;
|
||||
pub const SendFunc = *const fn (envelope: *Envelope, state: ?*anyopaque) callconv(.c) void;
|
||||
pub const FreeFunc = *const fn (state: ?*anyopaque) callconv(.c) void;
|
||||
|
||||
pub fn init(f: SendFunc) *Transport {
|
||||
return @ptrCast(c.sentry_transport_new(@ptrCast(f)).?);
|
||||
|
@ -4119,6 +4119,14 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
||||
}, .unlocked);
|
||||
},
|
||||
|
||||
.scroll_to_selection => {
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
const sel = self.io.terminal.screen.selection orelse return false;
|
||||
const tl = sel.topLeft(&self.io.terminal.screen);
|
||||
self.io.terminal.screen.scroll(.{ .pin = tl });
|
||||
},
|
||||
|
||||
.scroll_page_up => {
|
||||
const rows: isize = @intCast(self.size.grid().rows);
|
||||
self.io.queueMessage(.{
|
||||
|
@ -43,15 +43,15 @@ pub const App = struct {
|
||||
|
||||
/// Callback called to wakeup the event loop. This should trigger
|
||||
/// a full tick of the app loop.
|
||||
wakeup: *const fn (AppUD) callconv(.C) void,
|
||||
wakeup: *const fn (AppUD) callconv(.c) void,
|
||||
|
||||
/// Callback called to handle an action.
|
||||
action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.C) bool,
|
||||
action: *const fn (*App, apprt.Target.C, apprt.Action.C) callconv(.c) bool,
|
||||
|
||||
/// Read the clipboard value. The return value must be preserved
|
||||
/// by the host until the next call. If there is no valid clipboard
|
||||
/// value then this should return null.
|
||||
read_clipboard: *const fn (SurfaceUD, c_int, *apprt.ClipboardRequest) callconv(.C) void,
|
||||
read_clipboard: *const fn (SurfaceUD, c_int, *apprt.ClipboardRequest) callconv(.c) void,
|
||||
|
||||
/// This may be called after a read clipboard call to request
|
||||
/// confirmation that the clipboard value is safe to read. The embedder
|
||||
@ -61,13 +61,13 @@ pub const App = struct {
|
||||
[*:0]const u8,
|
||||
*apprt.ClipboardRequest,
|
||||
apprt.ClipboardRequestType,
|
||||
) callconv(.C) void,
|
||||
) callconv(.c) void,
|
||||
|
||||
/// Write the clipboard value.
|
||||
write_clipboard: *const fn (SurfaceUD, [*:0]const u8, c_int, bool) callconv(.C) void,
|
||||
write_clipboard: *const fn (SurfaceUD, [*:0]const u8, c_int, bool) callconv(.c) void,
|
||||
|
||||
/// Close the current surface given by this function.
|
||||
close_surface: ?*const fn (SurfaceUD, bool) callconv(.C) void = null,
|
||||
close_surface: ?*const fn (SurfaceUD, bool) callconv(.c) void = null,
|
||||
};
|
||||
|
||||
/// This is the key event sent for ghostty_surface_key and
|
||||
|
@ -74,6 +74,9 @@ cursor_none: ?*gdk.Cursor,
|
||||
/// The clipboard confirmation window, if it is currently open.
|
||||
clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null,
|
||||
|
||||
/// The config errors dialog, if it is currently open.
|
||||
config_errors_dialog: ?ConfigErrorsDialog = null,
|
||||
|
||||
/// The window containing the quick terminal.
|
||||
/// Null when never initialized.
|
||||
quick_terminal: ?*Window = null,
|
||||
@ -159,6 +162,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||
opengl: bool = false,
|
||||
/// disable GLES, Ghostty can't use GLES
|
||||
@"gl-disable-gles": bool = false,
|
||||
// GTK's new renderer can cause blurry font when using fractional scaling.
|
||||
@"gl-no-fractional": bool = false,
|
||||
/// Disabling Vulkan can improve startup times by hundreds of
|
||||
/// milliseconds on some systems. We don't use Vulkan so we can just
|
||||
@ -190,7 +194,6 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||
// For the remainder of "why" see the 4.14 comment below.
|
||||
gdk_disable.@"gles-api" = true;
|
||||
gdk_disable.vulkan = true;
|
||||
gdk_debug.@"gl-no-fractional" = true;
|
||||
break :environment;
|
||||
}
|
||||
if (gtk_version.runtimeAtLeast(4, 14, 0)) {
|
||||
@ -201,8 +204,12 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||
//
|
||||
// Upstream issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/6589
|
||||
gdk_debug.@"gl-disable-gles" = true;
|
||||
gdk_debug.@"gl-no-fractional" = true;
|
||||
gdk_debug.@"vulkan-disable" = true;
|
||||
|
||||
if (gtk_version.runtimeUntil(4, 17, 5)) {
|
||||
// Removed at GTK v4.17.5
|
||||
gdk_debug.@"gl-no-fractional" = true;
|
||||
}
|
||||
break :environment;
|
||||
}
|
||||
// Versions prior to 4.14 are a bit of an unknown for Ghostty. It
|
||||
@ -1523,7 +1530,7 @@ fn adwNotifyDark(
|
||||
style_manager: *adw.StyleManager,
|
||||
_: *gobject.ParamSpec,
|
||||
self: *App,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const color_scheme: apprt.ColorScheme = if (style_manager.getDark() == 0)
|
||||
.light
|
||||
else
|
||||
|
@ -18,88 +18,37 @@ pub fn init(
|
||||
/// The minor version of the minimum Adwaita version that is required to use
|
||||
/// this resource.
|
||||
comptime minor: u16,
|
||||
/// `blp` signifies that the resource is a Blueprint that has been compiled
|
||||
/// to GTK Builder XML at compile time. `ui` signifies that the resource is
|
||||
/// a GTK Builder XML file that is included in the Ghostty source (perhaps
|
||||
/// because the Blueprint compiler on some target platforms cannot compile a
|
||||
/// Blueprint that generates the necessary resources).
|
||||
comptime kind: enum { blp, ui },
|
||||
) Builder {
|
||||
const resource_path = comptime resource_path: {
|
||||
const gresource = @import("gresource.zig");
|
||||
switch (kind) {
|
||||
.blp => {
|
||||
// Check to make sure that our file is listed as a
|
||||
// `blueprint_file` in `gresource.zig`. If it isn't Ghostty
|
||||
// could crash at runtime when we try and load a nonexistent
|
||||
// GResource.
|
||||
for (gresource.blueprint_files) |file| {
|
||||
if (major != file.major or minor != file.minor or !std.mem.eql(u8, file.name, name)) continue;
|
||||
// Use @embedFile to make sure that the `.blp` file exists
|
||||
// at compile time. Zig _should_ discard the data so that
|
||||
// it doesn't end up in the final executable. At runtime we
|
||||
// will load the data from a GResource.
|
||||
const blp_filename = std.fmt.comptimePrint(
|
||||
"ui/{d}.{d}/{s}.blp",
|
||||
.{
|
||||
file.major,
|
||||
file.minor,
|
||||
file.name,
|
||||
},
|
||||
);
|
||||
_ = @embedFile(blp_filename);
|
||||
break :resource_path std.fmt.comptimePrint(
|
||||
"/com/mitchellh/ghostty/ui/{d}.{d}/{s}.ui",
|
||||
.{
|
||||
file.major,
|
||||
file.minor,
|
||||
file.name,
|
||||
},
|
||||
);
|
||||
} else @compileError("missing blueprint file '" ++ name ++ "' in gresource.zig");
|
||||
},
|
||||
.ui => {
|
||||
// Check to make sure that our file is listed as a `ui_file` in
|
||||
// `gresource.zig`. If it isn't Ghostty could crash at runtime
|
||||
// when we try and load a nonexistent GResource.
|
||||
for (gresource.ui_files) |file| {
|
||||
if (major != file.major or minor != file.minor or !std.mem.eql(u8, file.name, name)) continue;
|
||||
// Use @embedFile to make sure that the `.ui` file exists
|
||||
// at compile time. Zig _should_ discard the data so that
|
||||
// it doesn't end up in the final executable. At runtime we
|
||||
// will load the data from a GResource.
|
||||
const ui_filename = std.fmt.comptimePrint(
|
||||
"ui/{d}.{d}/{s}.ui",
|
||||
.{
|
||||
file.major,
|
||||
file.minor,
|
||||
file.name,
|
||||
},
|
||||
);
|
||||
_ = @embedFile(ui_filename);
|
||||
// Also use @embedFile to make sure that a matching `.blp`
|
||||
// file exists at compile time. Zig _should_ discard the
|
||||
// data so that it doesn't end up in the final executable.
|
||||
const blp_filename = std.fmt.comptimePrint(
|
||||
"ui/{d}.{d}/{s}.blp",
|
||||
.{
|
||||
file.major,
|
||||
file.minor,
|
||||
file.name,
|
||||
},
|
||||
);
|
||||
_ = @embedFile(blp_filename);
|
||||
break :resource_path std.fmt.comptimePrint(
|
||||
"/com/mitchellh/ghostty/ui/{d}.{d}/{s}.ui",
|
||||
.{
|
||||
file.major,
|
||||
file.minor,
|
||||
file.name,
|
||||
},
|
||||
);
|
||||
} else @compileError("missing ui file '" ++ name ++ "' in gresource.zig");
|
||||
},
|
||||
}
|
||||
// Check to make sure that our file is listed as a
|
||||
// `blueprint_file` in `gresource.zig`. If it isn't Ghostty
|
||||
// could crash at runtime when we try and load a nonexistent
|
||||
// GResource.
|
||||
for (gresource.blueprint_files) |file| {
|
||||
if (major != file.major or minor != file.minor or !std.mem.eql(u8, file.name, name)) continue;
|
||||
// Use @embedFile to make sure that the `.blp` file exists
|
||||
// at compile time. Zig _should_ discard the data so that
|
||||
// it doesn't end up in the final executable. At runtime we
|
||||
// will load the data from a GResource.
|
||||
const blp_filename = std.fmt.comptimePrint(
|
||||
"ui/{d}.{d}/{s}.blp",
|
||||
.{
|
||||
file.major,
|
||||
file.minor,
|
||||
file.name,
|
||||
},
|
||||
);
|
||||
_ = @embedFile(blp_filename);
|
||||
break :resource_path std.fmt.comptimePrint(
|
||||
"/com/mitchellh/ghostty/ui/{d}.{d}/{s}.ui",
|
||||
.{
|
||||
file.major,
|
||||
file.minor,
|
||||
file.name,
|
||||
},
|
||||
);
|
||||
} else @compileError("missing blueprint file '" ++ name ++ "' in gresource.zig");
|
||||
};
|
||||
|
||||
return .{
|
||||
|
@ -71,14 +71,14 @@ fn init(
|
||||
) !void {
|
||||
var builder = switch (DialogType) {
|
||||
adw.AlertDialog => switch (request) {
|
||||
.osc_52_read => Builder.init("ccw-osc-52-read", 1, 5, .blp),
|
||||
.osc_52_write => Builder.init("ccw-osc-52-write", 1, 5, .blp),
|
||||
.paste => Builder.init("ccw-paste", 1, 5, .blp),
|
||||
.osc_52_read => Builder.init("ccw-osc-52-read", 1, 5),
|
||||
.osc_52_write => Builder.init("ccw-osc-52-write", 1, 5),
|
||||
.paste => Builder.init("ccw-paste", 1, 5),
|
||||
},
|
||||
adw.MessageDialog => switch (request) {
|
||||
.osc_52_read => Builder.init("ccw-osc-52-read", 1, 2, .ui),
|
||||
.osc_52_write => Builder.init("ccw-osc-52-write", 1, 2, .ui),
|
||||
.paste => Builder.init("ccw-paste", 1, 2, .ui),
|
||||
.osc_52_read => Builder.init("ccw-osc-52-read", 1, 2),
|
||||
.osc_52_write => Builder.init("ccw-osc-52-write", 1, 2),
|
||||
.paste => Builder.init("ccw-paste", 1, 2),
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
@ -152,7 +152,7 @@ fn init(
|
||||
}
|
||||
}
|
||||
|
||||
fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) callconv(.C) void {
|
||||
fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation) callconv(.c) void {
|
||||
if (std.mem.orderZ(u8, response, "ok") == .eq) {
|
||||
self.core_surface.completeClipboardRequest(
|
||||
self.pending_req,
|
||||
@ -165,7 +165,7 @@ fn gtkResponse(_: *DialogType, response: [*:0]u8, self: *ClipboardConfirmation)
|
||||
self.destroy();
|
||||
}
|
||||
|
||||
fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.C) void {
|
||||
fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.c) void {
|
||||
self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(true));
|
||||
self.text_view.as(gtk.Widget).removeCssClass("blurred");
|
||||
|
||||
@ -173,7 +173,7 @@ fn gtkRevealButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv
|
||||
self.reveal_button.as(gtk.Widget).setVisible(@intFromBool(false));
|
||||
}
|
||||
|
||||
fn gtkHideButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.C) void {
|
||||
fn gtkHideButtonClicked(_: *gtk.Button, self: *ClipboardConfirmation) callconv(.c) void {
|
||||
self.text_view_scroll.as(gtk.Widget).setSensitive(@intFromBool(false));
|
||||
self.text_view.as(gtk.Widget).addCssClass("blurred");
|
||||
|
||||
|
@ -64,7 +64,7 @@ fn responseCallback(
|
||||
_: *DialogType,
|
||||
response: [*:0]const u8,
|
||||
target: *Target,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const alloc = target.allocator();
|
||||
defer alloc.destroy(target);
|
||||
|
||||
@ -141,7 +141,7 @@ pub const Target = union(enum) {
|
||||
}
|
||||
};
|
||||
|
||||
fn findActiveWindow(data: ?*const anyopaque, _: ?*const anyopaque) callconv(.C) c_int {
|
||||
fn findActiveWindow(data: ?*const anyopaque, _: ?*const anyopaque) callconv(.c) c_int {
|
||||
const window: *gtk.Window = @ptrCast(@alignCast(@constCast(data orelse return -1)));
|
||||
|
||||
// Confusingly, `isActive` returns 1 when active,
|
||||
|
@ -29,15 +29,38 @@ error_message: *gtk.TextBuffer,
|
||||
pub fn maybePresent(app: *App, window: ?*Window) void {
|
||||
if (app.config._diagnostics.empty()) return;
|
||||
|
||||
var builder = switch (DialogType) {
|
||||
adw.AlertDialog => Builder.init("config-errors-dialog", 1, 5, .blp),
|
||||
adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2, .ui),
|
||||
else => unreachable,
|
||||
};
|
||||
defer builder.deinit();
|
||||
const config_errors_dialog = config_errors_dialog: {
|
||||
if (app.config_errors_dialog) |config_errors_dialog| break :config_errors_dialog config_errors_dialog;
|
||||
|
||||
const dialog = builder.getObject(DialogType, "config_errors_dialog").?;
|
||||
const error_message = builder.getObject(gtk.TextBuffer, "error_message").?;
|
||||
var builder = switch (DialogType) {
|
||||
adw.AlertDialog => Builder.init("config-errors-dialog", 1, 5),
|
||||
adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const dialog = builder.getObject(DialogType, "config_errors_dialog").?;
|
||||
const error_message = builder.getObject(gtk.TextBuffer, "error_message").?;
|
||||
|
||||
_ = DialogType.signals.response.connect(dialog, *App, onResponse, app, .{});
|
||||
|
||||
app.config_errors_dialog = .{
|
||||
.builder = builder,
|
||||
.dialog = dialog,
|
||||
.error_message = error_message,
|
||||
};
|
||||
|
||||
break :config_errors_dialog app.config_errors_dialog.?;
|
||||
};
|
||||
|
||||
{
|
||||
var start = std.mem.zeroes(gtk.TextIter);
|
||||
config_errors_dialog.error_message.getStartIter(&start);
|
||||
|
||||
var end = std.mem.zeroes(gtk.TextIter);
|
||||
config_errors_dialog.error_message.getEndIter(&end);
|
||||
|
||||
config_errors_dialog.error_message.delete(&start, &end);
|
||||
}
|
||||
|
||||
var msg_buf: [4095:0]u8 = undefined;
|
||||
var fbs = std.io.fixedBufferStream(&msg_buf);
|
||||
@ -52,22 +75,24 @@ pub fn maybePresent(app: *App, window: ?*Window) void {
|
||||
continue;
|
||||
};
|
||||
|
||||
error_message.insertAtCursor(&msg_buf, @intCast(fbs.pos));
|
||||
error_message.insertAtCursor("\n", 1);
|
||||
config_errors_dialog.error_message.insertAtCursor(&msg_buf, @intCast(fbs.pos));
|
||||
config_errors_dialog.error_message.insertAtCursor("\n", 1);
|
||||
}
|
||||
|
||||
_ = DialogType.signals.response.connect(dialog, *App, onResponse, app, .{});
|
||||
|
||||
const parent = if (window) |w| w.window.as(gtk.Widget) else null;
|
||||
|
||||
switch (DialogType) {
|
||||
adw.AlertDialog => dialog.as(adw.Dialog).present(parent),
|
||||
adw.MessageDialog => dialog.as(gtk.Window).present(),
|
||||
adw.AlertDialog => {
|
||||
const parent = if (window) |w| w.window.as(gtk.Widget) else null;
|
||||
config_errors_dialog.dialog.as(adw.Dialog).present(parent);
|
||||
},
|
||||
adw.MessageDialog => config_errors_dialog.dialog.as(gtk.Window).present(),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn onResponse(_: *DialogType, response: [*:0]const u8, app: *App) callconv(.C) void {
|
||||
fn onResponse(_: *DialogType, response: [*:0]const u8, app: *App) callconv(.c) void {
|
||||
if (app.config_errors_dialog) |config_errors_dialog| config_errors_dialog.builder.deinit();
|
||||
app.config_errors_dialog = null;
|
||||
|
||||
if (std.mem.orderZ(u8, response, "reload") == .eq) {
|
||||
app.reloadConfig(.app, .{}) catch |err| {
|
||||
log.warn("error reloading config error={}", .{err});
|
||||
|
@ -221,12 +221,12 @@ fn translateMouseButton(button: c_uint) ?c_int {
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkDestroy(_: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
|
||||
fn gtkDestroy(_: *gtk.GLArea, self: *ImguiWidget) callconv(.c) void {
|
||||
log.debug("imgui widget destroy", .{});
|
||||
self.deinit();
|
||||
}
|
||||
|
||||
fn gtkRealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
|
||||
fn gtkRealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.c) void {
|
||||
log.debug("gl surface realized", .{});
|
||||
|
||||
// We need to make the context current so we can call GL functions.
|
||||
@ -242,7 +242,7 @@ fn gtkRealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
|
||||
_ = cimgui.ImGui_ImplOpenGL3_Init(null);
|
||||
}
|
||||
|
||||
fn gtkUnrealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
|
||||
fn gtkUnrealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.c) void {
|
||||
_ = area;
|
||||
log.debug("gl surface unrealized", .{});
|
||||
|
||||
@ -250,7 +250,7 @@ fn gtkUnrealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
|
||||
cimgui.ImGui_ImplOpenGL3_Shutdown();
|
||||
}
|
||||
|
||||
fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget) callconv(.C) void {
|
||||
fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget) callconv(.c) void {
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
|
||||
const scale_factor = area.as(gtk.Widget).getScaleFactor();
|
||||
@ -273,7 +273,7 @@ fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget)
|
||||
active_style.* = style.*;
|
||||
}
|
||||
|
||||
fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *ImguiWidget) callconv(.C) c_int {
|
||||
fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *ImguiWidget) callconv(.c) c_int {
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
|
||||
// Setup our frame. We render twice because some ImGui behaviors
|
||||
@ -307,7 +307,7 @@ fn gtkMouseMotion(
|
||||
x: f64,
|
||||
y: f64,
|
||||
self: *ImguiWidget,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
|
||||
const scale_factor: f64 = @floatFromInt(self.gl_area.as(gtk.Widget).getScaleFactor());
|
||||
@ -325,7 +325,7 @@ fn gtkMouseDown(
|
||||
_: f64,
|
||||
_: f64,
|
||||
self: *ImguiWidget,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.queueRender();
|
||||
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
@ -343,7 +343,7 @@ fn gtkMouseUp(
|
||||
_: f64,
|
||||
_: f64,
|
||||
self: *ImguiWidget,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.queueRender();
|
||||
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
@ -359,7 +359,7 @@ fn gtkMouseScroll(
|
||||
x: f64,
|
||||
y: f64,
|
||||
self: *ImguiWidget,
|
||||
) callconv(.C) c_int {
|
||||
) callconv(.c) c_int {
|
||||
self.queueRender();
|
||||
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
@ -373,7 +373,7 @@ fn gtkMouseScroll(
|
||||
return @intFromBool(true);
|
||||
}
|
||||
|
||||
fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void {
|
||||
fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.c) void {
|
||||
self.queueRender();
|
||||
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
@ -381,7 +381,7 @@ fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C)
|
||||
cimgui.c.ImGuiIO_AddFocusEvent(io, true);
|
||||
}
|
||||
|
||||
fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void {
|
||||
fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.c) void {
|
||||
self.queueRender();
|
||||
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
@ -393,7 +393,7 @@ fn gtkInputCommit(
|
||||
_: *gtk.IMMulticontext,
|
||||
bytes: [*:0]u8,
|
||||
self: *ImguiWidget,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.queueRender();
|
||||
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
@ -407,7 +407,7 @@ fn gtkKeyPressed(
|
||||
keycode: c_uint,
|
||||
gtk_mods: gdk.ModifierType,
|
||||
self: *ImguiWidget,
|
||||
) callconv(.C) c_int {
|
||||
) callconv(.c) c_int {
|
||||
return @intFromBool(self.keyEvent(
|
||||
.press,
|
||||
ec_key,
|
||||
@ -423,7 +423,7 @@ fn gtkKeyReleased(
|
||||
keycode: c_uint,
|
||||
gtk_mods: gdk.ModifierType,
|
||||
self: *ImguiWidget,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
_ = self.keyEvent(
|
||||
.release,
|
||||
ec_key,
|
||||
|
@ -104,7 +104,7 @@ pub fn maybeShow(self: *ResizeOverlay) void {
|
||||
|
||||
/// Actually update the overlay widget. This should only be called from a GTK
|
||||
/// idle handler.
|
||||
fn gtkUpdate(ud: ?*anyopaque) callconv(.C) c_int {
|
||||
fn gtkUpdate(ud: ?*anyopaque) callconv(.c) c_int {
|
||||
const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0));
|
||||
|
||||
// No matter what our idler is complete with this callback
|
||||
@ -198,7 +198,7 @@ fn setPosition(label: *gtk.Label, config: *DerivedConfig) void {
|
||||
|
||||
/// If this fires, it means that the delay period has expired and the resize
|
||||
/// overlay widget should be hidden.
|
||||
fn gtkTimerExpired(ud: ?*anyopaque) callconv(.C) c_int {
|
||||
fn gtkTimerExpired(ud: ?*anyopaque) callconv(.c) c_int {
|
||||
const self: *ResizeOverlay = @ptrCast(@alignCast(ud orelse return 0));
|
||||
self.timer = null;
|
||||
if (self.label) |label| hide(label);
|
||||
|
@ -1025,7 +1025,7 @@ pub fn setTitle(self: *Surface, slice: [:0]const u8, source: SetTitleSource) !vo
|
||||
self.update_title_timer = glib.timeoutAdd(75, updateTitleTimerExpired, self);
|
||||
}
|
||||
|
||||
fn updateTitleTimerExpired(ud: ?*anyopaque) callconv(.C) c_int {
|
||||
fn updateTitleTimerExpired(ud: ?*anyopaque) callconv(.c) c_int {
|
||||
const self: *Surface = @ptrCast(@alignCast(ud.?));
|
||||
|
||||
self.updateTitleLabels();
|
||||
@ -1061,7 +1061,7 @@ pub fn promptTitle(self: *Surface) !void {
|
||||
if (!adw_version.atLeast(1, 5, 0)) return;
|
||||
const window = self.container.window() orelse return;
|
||||
|
||||
var builder = Builder.init("prompt-title-dialog", 1, 5, .blp);
|
||||
var builder = Builder.init("prompt-title-dialog", 1, 5);
|
||||
defer builder.deinit();
|
||||
|
||||
const entry = builder.getObject(gtk.Entry, "title_entry").?;
|
||||
@ -1265,7 +1265,7 @@ fn gtkClipboardRead(
|
||||
source: ?*gobject.Object,
|
||||
res: *gio.AsyncResult,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const clipboard = gobject.ext.cast(gdk.Clipboard, source orelse return) orelse return;
|
||||
const req: *ClipboardRequest = @ptrCast(@alignCast(ud orelse return));
|
||||
const self = req.self;
|
||||
@ -1349,7 +1349,7 @@ pub fn showDesktopNotification(
|
||||
app.sendNotification(body.ptr, notification);
|
||||
}
|
||||
|
||||
fn gtkRealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void {
|
||||
fn gtkRealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.c) void {
|
||||
log.debug("gl surface realized", .{});
|
||||
|
||||
// We need to make the context current so we can call GL functions.
|
||||
@ -1377,7 +1377,7 @@ fn gtkRealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void {
|
||||
|
||||
/// This is called when the underlying OpenGL resources must be released.
|
||||
/// This is usually due to the OpenGL area changing GDK surfaces.
|
||||
fn gtkUnrealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void {
|
||||
fn gtkUnrealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.c) void {
|
||||
log.debug("gl surface unrealized", .{});
|
||||
|
||||
// See gtkRealize for why we do this here.
|
||||
@ -1405,7 +1405,7 @@ fn gtkUnrealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void {
|
||||
}
|
||||
|
||||
/// render signal
|
||||
fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Surface) callconv(.C) c_int {
|
||||
fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Surface) callconv(.c) c_int {
|
||||
self.render() catch |err| {
|
||||
log.err("surface failed to render: {}", .{err});
|
||||
return 0;
|
||||
@ -1415,7 +1415,7 @@ fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Surface) callconv(.C) c_i
|
||||
}
|
||||
|
||||
/// resize signal
|
||||
fn gtkResize(gl_area: *gtk.GLArea, width: c_int, height: c_int, self: *Surface) callconv(.C) void {
|
||||
fn gtkResize(gl_area: *gtk.GLArea, width: c_int, height: c_int, self: *Surface) callconv(.c) void {
|
||||
// Some debug output to help understand what GTK is telling us.
|
||||
{
|
||||
const scale_factor = scale: {
|
||||
@ -1471,7 +1471,7 @@ fn gtkResize(gl_area: *gtk.GLArea, width: c_int, height: c_int, self: *Surface)
|
||||
}
|
||||
|
||||
/// "destroy" signal for surface
|
||||
fn gtkDestroy(_: *gtk.GLArea, self: *Surface) callconv(.C) void {
|
||||
fn gtkDestroy(_: *gtk.GLArea, self: *Surface) callconv(.c) void {
|
||||
log.debug("gl destroy", .{});
|
||||
|
||||
const alloc = self.app.core_app.alloc;
|
||||
@ -1505,7 +1505,7 @@ fn gtkMouseDown(
|
||||
x: f64,
|
||||
y: f64,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return;
|
||||
|
||||
const gtk_mods = event.getModifierState();
|
||||
@ -1538,7 +1538,7 @@ fn gtkMouseUp(
|
||||
_: f64,
|
||||
_: f64,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return;
|
||||
|
||||
const gtk_mods = event.getModifierState();
|
||||
@ -1557,7 +1557,7 @@ fn gtkMouseMotion(
|
||||
x: f64,
|
||||
y: f64,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const event = ec.as(gtk.EventController).getCurrentEvent() orelse return;
|
||||
|
||||
const scaled = self.scaledCoordinates(x, y);
|
||||
@ -1603,7 +1603,7 @@ fn gtkMouseMotion(
|
||||
fn gtkMouseLeave(
|
||||
ec_motion: *gtk.EventControllerMotion,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const event = ec_motion.as(gtk.EventController).getCurrentEvent() orelse return;
|
||||
|
||||
// Get our modifiers
|
||||
@ -1618,14 +1618,14 @@ fn gtkMouseLeave(
|
||||
fn gtkMouseScrollPrecisionBegin(
|
||||
_: *gtk.EventControllerScroll,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.precision_scroll = true;
|
||||
}
|
||||
|
||||
fn gtkMouseScrollPrecisionEnd(
|
||||
_: *gtk.EventControllerScroll,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.precision_scroll = false;
|
||||
}
|
||||
|
||||
@ -1634,7 +1634,7 @@ fn gtkMouseScroll(
|
||||
x: f64,
|
||||
y: f64,
|
||||
self: *Surface,
|
||||
) callconv(.C) c_int {
|
||||
) callconv(.c) c_int {
|
||||
const scaled = self.scaledCoordinates(x, y);
|
||||
|
||||
// GTK doesn't support any of the scroll mods.
|
||||
@ -1664,7 +1664,7 @@ fn gtkKeyPressed(
|
||||
keycode: c_uint,
|
||||
gtk_mods: gdk.ModifierType,
|
||||
self: *Surface,
|
||||
) callconv(.C) c_int {
|
||||
) callconv(.c) c_int {
|
||||
return @intFromBool(self.keyEvent(
|
||||
.press,
|
||||
ec_key,
|
||||
@ -1680,7 +1680,7 @@ fn gtkKeyReleased(
|
||||
keycode: c_uint,
|
||||
state: gdk.ModifierType,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
_ = self.keyEvent(
|
||||
.release,
|
||||
ec_key,
|
||||
@ -1971,7 +1971,7 @@ pub fn keyEvent(
|
||||
fn gtkInputPreeditStart(
|
||||
_: *gtk.IMMulticontext,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
// log.warn("GTKIM: preedit start", .{});
|
||||
|
||||
// Start our composing state for the input method and reset our
|
||||
@ -1983,7 +1983,7 @@ fn gtkInputPreeditStart(
|
||||
fn gtkInputPreeditChanged(
|
||||
ctx: *gtk.IMMulticontext,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
// Any preedit change should mark that we're composing. Its possible this
|
||||
// is false using fcitx5-hangul and typing "dkssud<space>" ("안녕"). The
|
||||
// second "s" results in a "commit" for "안" which sets composing to false,
|
||||
@ -2009,7 +2009,7 @@ fn gtkInputPreeditChanged(
|
||||
fn gtkInputPreeditEnd(
|
||||
_: *gtk.IMMulticontext,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
// log.warn("GTKIM: preedit end", .{});
|
||||
|
||||
// End our composing state for GTK, allowing us to commit the text.
|
||||
@ -2025,7 +2025,7 @@ fn gtkInputCommit(
|
||||
_: *gtk.IMMulticontext,
|
||||
bytes: [*:0]u8,
|
||||
self: *Surface,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const str = std.mem.sliceTo(bytes, 0);
|
||||
|
||||
// log.debug("GTKIM: input commit composing={} keyevent={} str={s}", .{
|
||||
@ -2100,7 +2100,7 @@ fn gtkInputCommit(
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void {
|
||||
fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *Surface) callconv(.c) void {
|
||||
if (!self.realized) return;
|
||||
|
||||
// Notify our IM context
|
||||
@ -2125,7 +2125,7 @@ fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void
|
||||
};
|
||||
}
|
||||
|
||||
fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void {
|
||||
fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *Surface) callconv(.c) void {
|
||||
if (!self.realized) return;
|
||||
|
||||
// Notify our IM context
|
||||
@ -2243,7 +2243,7 @@ fn gtkDrop(
|
||||
_: f64,
|
||||
_: f64,
|
||||
self: *Surface,
|
||||
) callconv(.C) c_int {
|
||||
) callconv(.c) c_int {
|
||||
const alloc = self.app.core_app.alloc;
|
||||
|
||||
if (g_value_holds(value, gdk.FileList.getGObjectType())) {
|
||||
@ -2395,7 +2395,7 @@ fn g_value_holds(value_: ?*gobject.Value, g_type: gobject.Type) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn gtkPromptTitleResponse(source_object: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.C) void {
|
||||
fn gtkPromptTitleResponse(source_object: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.c) void {
|
||||
if (!adw_version.supportsDialogs()) return;
|
||||
const dialog = gobject.ext.cast(adw.AlertDialog, source_object.?).?;
|
||||
const self: *Surface = @ptrCast(@alignCast(ud));
|
||||
|
@ -161,7 +161,7 @@ pub fn closeWithConfirmation(tab: *Tab) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn gtkDestroy(_: *gtk.Box, self: *Tab) callconv(.C) void {
|
||||
fn gtkDestroy(_: *gtk.Box, self: *Tab) callconv(.c) void {
|
||||
log.debug("tab box destroy", .{});
|
||||
|
||||
const alloc = self.window.app.core_app.alloc;
|
||||
|
@ -227,7 +227,7 @@ pub fn createWindow(window: *Window) !*Window {
|
||||
return new_window;
|
||||
}
|
||||
|
||||
fn adwPageAttached(_: *adw.TabView, page: *adw.TabPage, _: c_int, self: *TabView) callconv(.C) void {
|
||||
fn adwPageAttached(_: *adw.TabView, page: *adw.TabPage, _: c_int, self: *TabView) callconv(.c) void {
|
||||
const child = page.getChild().as(gobject.Object);
|
||||
const tab: *Tab = @ptrCast(@alignCast(child.getData(Tab.GHOSTTY_TAB) orelse return));
|
||||
tab.window = self.window;
|
||||
@ -239,7 +239,7 @@ fn adwClosePage(
|
||||
_: *adw.TabView,
|
||||
page: *adw.TabPage,
|
||||
self: *TabView,
|
||||
) callconv(.C) c_int {
|
||||
) callconv(.c) c_int {
|
||||
const child = page.getChild().as(gobject.Object);
|
||||
const tab: *Tab = @ptrCast(@alignCast(child.getData(Tab.GHOSTTY_TAB) orelse return 0));
|
||||
self.tab_view.closePageFinish(page, @intFromBool(self.forcing_close));
|
||||
@ -251,7 +251,7 @@ fn adwClosePage(
|
||||
fn adwTabViewCreateWindow(
|
||||
_: *adw.TabView,
|
||||
self: *TabView,
|
||||
) callconv(.C) ?*adw.TabView {
|
||||
) callconv(.c) ?*adw.TabView {
|
||||
const window = createWindow(self.window) catch |err| {
|
||||
log.warn("error creating new window error={}", .{err});
|
||||
return null;
|
||||
@ -259,7 +259,7 @@ fn adwTabViewCreateWindow(
|
||||
return window.notebook.tab_view;
|
||||
}
|
||||
|
||||
fn adwSelectPage(_: *adw.TabView, _: *gobject.ParamSpec, self: *TabView) callconv(.C) void {
|
||||
fn adwSelectPage(_: *adw.TabView, _: *gobject.ParamSpec, self: *TabView) callconv(.c) void {
|
||||
const page = self.tab_view.getSelectedPage() orelse return;
|
||||
|
||||
// If the tab was previously marked as needing attention
|
||||
|
@ -101,7 +101,7 @@ fn gtkLeftEnter(
|
||||
_: f64,
|
||||
_: f64,
|
||||
right: *gtk.Label,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
right.as(gtk.Widget).removeCssClass("hidden");
|
||||
}
|
||||
|
||||
@ -110,6 +110,6 @@ fn gtkLeftEnter(
|
||||
fn gtkLeftLeave(
|
||||
_: *gtk.EventControllerMotion,
|
||||
right: *gtk.Label,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
right.as(gtk.Widget).addCssClass("hidden");
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
btn.as(gtk.Widget).setTooltipText(i18n._("New Tab"));
|
||||
btn.setDropdownTooltip(i18n._("New Split"));
|
||||
|
||||
var builder = Builder.init("menu-headerbar-split_menu", 1, 0, .blp);
|
||||
var builder = Builder.init("menu-headerbar-split_menu", 1, 0);
|
||||
defer builder.deinit();
|
||||
btn.setMenuModel(builder.getObject(gio.MenuModel, "menu"));
|
||||
|
||||
@ -792,7 +792,7 @@ fn gtkWindowNotifyIsActive(
|
||||
_: *adw.ApplicationWindow,
|
||||
_: *gobject.ParamSpec,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
if (!self.isQuickTerminal()) return;
|
||||
|
||||
// Hide when we're unfocused
|
||||
@ -883,7 +883,7 @@ fn adwTabOverviewOpen(
|
||||
|
||||
fn adwTabOverviewFocusTimer(
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) c_int {
|
||||
) callconv(.c) c_int {
|
||||
if (!adw_version.supportsTabOverview()) unreachable;
|
||||
const self: *Window = @ptrCast(@alignCast(ud orelse return 0));
|
||||
self.adw_tab_overview_focus_timer = null;
|
||||
@ -970,7 +970,7 @@ fn gtkActionAbout(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const name = "Ghostty";
|
||||
const icon = "com.mitchellh.ghostty";
|
||||
const website = "https://ghostty.org";
|
||||
@ -1014,7 +1014,7 @@ fn gtkActionClose(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.closeWithConfirmation();
|
||||
}
|
||||
|
||||
@ -1022,7 +1022,7 @@ fn gtkActionNewWindow(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .new_window = {} });
|
||||
}
|
||||
|
||||
@ -1030,7 +1030,7 @@ fn gtkActionNewTab(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .new_tab = {} });
|
||||
}
|
||||
|
||||
@ -1038,7 +1038,7 @@ fn gtkActionCloseTab(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .close_tab = {} });
|
||||
}
|
||||
|
||||
@ -1046,7 +1046,7 @@ fn gtkActionSplitRight(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .new_split = .right });
|
||||
}
|
||||
|
||||
@ -1054,7 +1054,7 @@ fn gtkActionSplitDown(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .new_split = .down });
|
||||
}
|
||||
|
||||
@ -1062,7 +1062,7 @@ fn gtkActionSplitLeft(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .new_split = .left });
|
||||
}
|
||||
|
||||
@ -1070,7 +1070,7 @@ fn gtkActionSplitUp(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .new_split = .up });
|
||||
}
|
||||
|
||||
@ -1078,7 +1078,7 @@ fn gtkActionToggleInspector(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .inspector = .toggle });
|
||||
}
|
||||
|
||||
@ -1086,7 +1086,7 @@ fn gtkActionCopy(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .copy_to_clipboard = {} });
|
||||
}
|
||||
|
||||
@ -1094,7 +1094,7 @@ fn gtkActionPaste(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .paste_from_clipboard = {} });
|
||||
}
|
||||
|
||||
@ -1102,7 +1102,7 @@ fn gtkActionReset(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .reset = {} });
|
||||
}
|
||||
|
||||
@ -1110,7 +1110,7 @@ fn gtkActionClear(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .clear_screen = {} });
|
||||
}
|
||||
|
||||
@ -1118,7 +1118,7 @@ fn gtkActionPromptTitle(
|
||||
_: *gio.SimpleAction,
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
self.performBindingAction(.{ .prompt_surface_title = {} });
|
||||
}
|
||||
|
||||
@ -1133,7 +1133,7 @@ fn gtkTitlebarMenuActivate(
|
||||
btn: *gtk.MenuButton,
|
||||
_: *gobject.ParamSpec,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
// debian 12 is stuck on GTK 4.8
|
||||
if (!gtk_version.atLeast(4, 10, 0)) return;
|
||||
const active = btn.getActive() != 0;
|
||||
|
@ -4,62 +4,157 @@ pub const c = @cImport({
|
||||
@cInclude("adwaita.h");
|
||||
});
|
||||
|
||||
const adwaita_version = std.SemanticVersion{
|
||||
.major = c.ADW_MAJOR_VERSION,
|
||||
.minor = c.ADW_MINOR_VERSION,
|
||||
.patch = c.ADW_MICRO_VERSION,
|
||||
};
|
||||
const required_blueprint_version = std.SemanticVersion{
|
||||
.major = 0,
|
||||
.minor = 16,
|
||||
.patch = 0,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alloc = gpa.allocator();
|
||||
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||
defer _ = debug_allocator.deinit();
|
||||
const alloc = debug_allocator.allocator();
|
||||
|
||||
var it = try std.process.argsWithAllocator(alloc);
|
||||
defer it.deinit();
|
||||
|
||||
_ = it.next();
|
||||
|
||||
const major = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMajorVersion, 10);
|
||||
const minor = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMinorVersion, 10);
|
||||
const required_adwaita_version = std.SemanticVersion{
|
||||
.major = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMajorVersion, 10),
|
||||
.minor = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMinorVersion, 10),
|
||||
.patch = 0,
|
||||
};
|
||||
const output = it.next() orelse return error.NoOutput;
|
||||
const input = it.next() orelse return error.NoInput;
|
||||
|
||||
if (c.ADW_MAJOR_VERSION < major or (c.ADW_MAJOR_VERSION == major and c.ADW_MINOR_VERSION < minor)) {
|
||||
// If the Adwaita version is too old, generate an "empty" file.
|
||||
const file = try std.fs.createFileAbsolute(output, .{
|
||||
.truncate = true,
|
||||
});
|
||||
try file.writeAll(
|
||||
\\<?xml version="1.0" encoding="UTF-8"?>
|
||||
\\<interface domain="com.mitchellh.ghostty"/>
|
||||
);
|
||||
defer file.close();
|
||||
|
||||
return;
|
||||
if (adwaita_version.order(required_adwaita_version) == .lt) {
|
||||
std.debug.print(
|
||||
\\`libadwaita` is too old.
|
||||
\\
|
||||
\\Ghostty requires a version {} or newer of `libadwaita` to
|
||||
\\compile this blueprint. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
, .{required_adwaita_version});
|
||||
std.posix.exit(1);
|
||||
}
|
||||
|
||||
var compiler = std.process.Child.init(
|
||||
&.{
|
||||
"blueprint-compiler",
|
||||
"compile",
|
||||
"--output",
|
||||
output,
|
||||
input,
|
||||
},
|
||||
alloc,
|
||||
);
|
||||
{
|
||||
var stdout: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer stdout.deinit(alloc);
|
||||
var stderr: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer stderr.deinit(alloc);
|
||||
|
||||
const term = compiler.spawnAndWait() catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
std.log.err(
|
||||
\\`blueprint-compiler` not found.
|
||||
var blueprint_compiler = std.process.Child.init(
|
||||
&.{
|
||||
"blueprint-compiler",
|
||||
"--version",
|
||||
},
|
||||
alloc,
|
||||
);
|
||||
blueprint_compiler.stdout_behavior = .Pipe;
|
||||
blueprint_compiler.stderr_behavior = .Pipe;
|
||||
try blueprint_compiler.spawn();
|
||||
try blueprint_compiler.collectOutput(
|
||||
alloc,
|
||||
&stdout,
|
||||
&stderr,
|
||||
std.math.maxInt(u16),
|
||||
);
|
||||
const term = blueprint_compiler.wait() catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
std.debug.print(
|
||||
\\`blueprint-compiler` not found.
|
||||
\\
|
||||
\\Ghostty requires version {} or newer of
|
||||
\\`blueprint-compiler` as a build-time dependency starting
|
||||
\\from version 1.2. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
\\
|
||||
, .{required_blueprint_version});
|
||||
std.posix.exit(1);
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
switch (term) {
|
||||
.Exited => |rc| {
|
||||
if (rc != 0) std.process.exit(1);
|
||||
},
|
||||
else => std.process.exit(1),
|
||||
}
|
||||
|
||||
const version = try std.SemanticVersion.parse(std.mem.trim(u8, stdout.items, &std.ascii.whitespace));
|
||||
if (version.order(required_blueprint_version) == .lt) {
|
||||
std.debug.print(
|
||||
\\`blueprint-compiler` is the wrong version.
|
||||
\\
|
||||
\\Ghostty requires `blueprint-compiler` as a build-time dependency starting from version 1.2.
|
||||
\\Please install it, ensure that it is available on your PATH, and then retry building Ghostty.
|
||||
, .{});
|
||||
\\Ghostty requires version {} or newer of
|
||||
\\`blueprint-compiler` as a build-time dependency starting
|
||||
\\from version 1.2. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
\\
|
||||
, .{required_blueprint_version});
|
||||
std.posix.exit(1);
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
switch (term) {
|
||||
.Exited => |rc| {
|
||||
if (rc != 0) std.process.exit(1);
|
||||
},
|
||||
else => std.process.exit(1),
|
||||
{
|
||||
var stdout: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer stdout.deinit(alloc);
|
||||
var stderr: std.ArrayListUnmanaged(u8) = .empty;
|
||||
defer stderr.deinit(alloc);
|
||||
|
||||
var blueprint_compiler = std.process.Child.init(
|
||||
&.{
|
||||
"blueprint-compiler",
|
||||
"compile",
|
||||
"--output",
|
||||
output,
|
||||
input,
|
||||
},
|
||||
alloc,
|
||||
);
|
||||
blueprint_compiler.stdout_behavior = .Pipe;
|
||||
blueprint_compiler.stderr_behavior = .Pipe;
|
||||
try blueprint_compiler.spawn();
|
||||
try blueprint_compiler.collectOutput(
|
||||
alloc,
|
||||
&stdout,
|
||||
&stderr,
|
||||
std.math.maxInt(u16),
|
||||
);
|
||||
const term = blueprint_compiler.wait() catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
std.debug.print(
|
||||
\\`blueprint-compiler` not found.
|
||||
\\
|
||||
\\Ghostty requires version {} or newer of
|
||||
\\`blueprint-compiler` as a build-time dependency starting
|
||||
\\from version 1.2. Please install it, ensure that it is
|
||||
\\available on your PATH, and then retry building Ghostty.
|
||||
\\
|
||||
, .{required_blueprint_version});
|
||||
std.posix.exit(1);
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
|
||||
switch (term) {
|
||||
.Exited => |rc| {
|
||||
if (rc != 0) {
|
||||
std.debug.print("{s}", .{stderr.items});
|
||||
std.process.exit(1);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
std.debug.print("{s}", .{stderr.items});
|
||||
std.process.exit(1);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
|
||||
const gtk = @import("gtk");
|
||||
const adw = @import("adw");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
const filename = filename: {
|
||||
var it = try std.process.argsWithAllocator(alloc);
|
||||
defer it.deinit();
|
||||
|
||||
_ = it.next() orelse return error.NoFilename;
|
||||
break :filename try alloc.dupeZ(u8, it.next() orelse return error.NoFilename);
|
||||
};
|
||||
defer alloc.free(filename);
|
||||
|
||||
const data = try std.fs.cwd().readFileAllocOptions(alloc, filename, std.math.maxInt(u16), null, 1, 0);
|
||||
defer alloc.free(data);
|
||||
|
||||
if (gtk.initCheck() == 0) {
|
||||
std.debug.print("{s}: skipping builder check because we can't connect to display!\n", .{filename});
|
||||
return;
|
||||
}
|
||||
|
||||
adw.init();
|
||||
|
||||
const builder = gtk.Builder.newFromString(data.ptr, @intCast(data.len));
|
||||
defer builder.unref();
|
||||
}
|
@ -53,19 +53,6 @@ const icons = [_]struct {
|
||||
},
|
||||
};
|
||||
|
||||
pub const VersionedBuilderXML = struct {
|
||||
major: u16,
|
||||
minor: u16,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
pub const ui_files = [_]VersionedBuilderXML{
|
||||
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
|
||||
.{ .major = 1, .minor = 2, .name = "ccw-osc-52-read" },
|
||||
.{ .major = 1, .minor = 2, .name = "ccw-osc-52-write" },
|
||||
.{ .major = 1, .minor = 2, .name = "ccw-paste" },
|
||||
};
|
||||
|
||||
pub const VersionedBlueprint = struct {
|
||||
major: u16,
|
||||
minor: u16,
|
||||
@ -81,16 +68,21 @@ pub const blueprint_files = [_]VersionedBlueprint{
|
||||
.{ .major = 1, .minor = 5, .name = "ccw-osc-52-read" },
|
||||
.{ .major = 1, .minor = 5, .name = "ccw-osc-52-write" },
|
||||
.{ .major = 1, .minor = 5, .name = "ccw-paste" },
|
||||
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
|
||||
.{ .major = 1, .minor = 2, .name = "ccw-osc-52-read" },
|
||||
.{ .major = 1, .minor = 2, .name = "ccw-osc-52-write" },
|
||||
.{ .major = 1, .minor = 2, .name = "ccw-paste" },
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alloc = gpa.allocator();
|
||||
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||
defer _ = debug_allocator.deinit();
|
||||
const alloc = debug_allocator.allocator();
|
||||
|
||||
var extra_ui_files = std.ArrayList([]const u8).init(alloc);
|
||||
var extra_ui_files: std.ArrayListUnmanaged([]const u8) = .empty;
|
||||
defer {
|
||||
for (extra_ui_files.items) |item| alloc.free(item);
|
||||
extra_ui_files.deinit();
|
||||
extra_ui_files.deinit(alloc);
|
||||
}
|
||||
|
||||
var it = try std.process.argsWithAllocator(alloc);
|
||||
@ -98,7 +90,7 @@ pub fn main() !void {
|
||||
|
||||
while (it.next()) |argument| {
|
||||
if (std.mem.eql(u8, std.fs.path.extension(argument), ".ui")) {
|
||||
try extra_ui_files.append(try alloc.dupe(u8, argument));
|
||||
try extra_ui_files.append(alloc, try alloc.dupe(u8, argument));
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,16 +124,11 @@ pub fn main() !void {
|
||||
\\ <gresource prefix="/com/mitchellh/ghostty/ui">
|
||||
\\
|
||||
);
|
||||
for (ui_files) |ui_file| {
|
||||
try writer.print(
|
||||
" <file compressed=\"true\" preprocess=\"xml-stripblanks\" alias=\"{0d}.{1d}/{2s}.ui\">src/apprt/gtk/ui/{0d}.{1d}/{2s}.ui</file>\n",
|
||||
.{ ui_file.major, ui_file.minor, ui_file.name },
|
||||
);
|
||||
}
|
||||
for (extra_ui_files.items) |ui_file| {
|
||||
const stem = std.fs.path.stem(ui_file);
|
||||
for (blueprint_files) |file| {
|
||||
if (!std.mem.eql(u8, file.name, stem)) continue;
|
||||
const expected = try std.fmt.allocPrint(alloc, "/{d}.{d}/{s}.ui", .{ file.major, file.minor, file.name });
|
||||
defer alloc.free(expected);
|
||||
if (!std.mem.endsWith(u8, ui_file, expected)) continue;
|
||||
try writer.print(
|
||||
" <file compressed=\"true\" preprocess=\"xml-stripblanks\" alias=\"{d}.{d}/{s}.ui\">{s}</file>\n",
|
||||
.{ file.major, file.minor, file.name, ui_file },
|
||||
@ -157,7 +144,7 @@ pub fn main() !void {
|
||||
}
|
||||
|
||||
pub const dependencies = deps: {
|
||||
const total = css_files.len + icons.len + ui_files.len + blueprint_files.len;
|
||||
const total = css_files.len + icons.len + blueprint_files.len;
|
||||
var deps: [total][]const u8 = undefined;
|
||||
var index: usize = 0;
|
||||
for (css_files) |css_file| {
|
||||
@ -168,14 +155,6 @@ pub const dependencies = deps: {
|
||||
deps[index] = std.fmt.comptimePrint("images/icons/icon_{s}.png", .{icon.source});
|
||||
index += 1;
|
||||
}
|
||||
for (ui_files) |ui_file| {
|
||||
deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{d}.{d}/{s}.ui", .{
|
||||
ui_file.major,
|
||||
ui_file.minor,
|
||||
ui_file.name,
|
||||
});
|
||||
index += 1;
|
||||
}
|
||||
for (blueprint_files) |blueprint_file| {
|
||||
deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{d}.{d}/{s}.blp", .{
|
||||
blueprint_file.major,
|
||||
|
@ -87,10 +87,23 @@ pub inline fn runtimeAtLeast(
|
||||
}) != .lt;
|
||||
}
|
||||
|
||||
pub inline fn runtimeUntil(
|
||||
comptime major: u16,
|
||||
comptime minor: u16,
|
||||
comptime micro: u16,
|
||||
) bool {
|
||||
const runtime_version = getRuntimeVersion();
|
||||
return runtime_version.order(.{
|
||||
.major = major,
|
||||
.minor = minor,
|
||||
.patch = micro,
|
||||
}) == .lt;
|
||||
}
|
||||
|
||||
test "atLeast" {
|
||||
const testing = std.testing;
|
||||
|
||||
const funs = &.{ atLeast, runtimeAtLeast };
|
||||
const funs = &.{ atLeast, runtimeAtLeast, runtimeUntil };
|
||||
inline for (funs) |fun| {
|
||||
try testing.expect(fun(c.GTK_MAJOR_VERSION, c.GTK_MINOR_VERSION, c.GTK_MICRO_VERSION));
|
||||
|
||||
|
@ -177,7 +177,7 @@ const Window = struct {
|
||||
}
|
||||
|
||||
/// "destroy" signal for the window
|
||||
fn gtkDestroy(_: *gtk.ApplicationWindow, self: *Window) callconv(.C) void {
|
||||
fn gtkDestroy(_: *gtk.ApplicationWindow, self: *Window) callconv(.c) void {
|
||||
log.debug("window destroy", .{});
|
||||
self.deinit();
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ pub fn Menu(
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
var builder = Builder.init("menu-" ++ object_type ++ "-" ++ menu_name, 1, 0, .blp);
|
||||
var builder = Builder.init("menu-" ++ object_type ++ "-" ++ menu_name, 1, 0);
|
||||
defer builder.deinit();
|
||||
|
||||
const menu_model = builder.getObject(gio.MenuModel, "menu").?;
|
||||
@ -130,7 +130,7 @@ pub fn Menu(
|
||||
}
|
||||
|
||||
/// Refocus tab that lost focus because of the popover menu
|
||||
fn gtkRefocusTerm(_: *gtk.PopoverMenu, self: *Self) callconv(.C) void {
|
||||
fn gtkRefocusTerm(_: *gtk.PopoverMenu, self: *Self) callconv(.c) void {
|
||||
const window: *Window = switch (T) {
|
||||
Window => self.parent,
|
||||
Surface => self.parent.container.window() orelse return,
|
||||
|
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
DO NOT EDIT!
|
||||
This file was @generated by blueprint-compiler. Instead, edit the
|
||||
corresponding .blp file and regenerate this file with blueprint-compiler.
|
||||
-->
|
||||
<interface domain="com.mitchellh.ghostty">
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<object class="AdwMessageDialog" id="clipboard_confirmation_window">
|
||||
<property name="heading" translatable="true">Authorize Clipboard Access</property>
|
||||
<property name="body" translatable="true">An application is attempting to read from the clipboard. The current clipboard contents are shown below.</property>
|
||||
<responses>
|
||||
<response id="cancel" translatable="true" appearance="suggested">Deny</response>
|
||||
<response id="ok" translatable="true" appearance="destructive">Allow</response>
|
||||
</responses>
|
||||
<property name="default-response">cancel</property>
|
||||
<property name="close-response">cancel</property>
|
||||
<property name="extra-child">
|
||||
<object class="GtkOverlay">
|
||||
<style>
|
||||
<class name="osd"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="text_view_scroll">
|
||||
<property name="width-request">500</property>
|
||||
<property name="height-request">250</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="cursor-visible">false</property>
|
||||
<property name="editable">false</property>
|
||||
<property name="monospace">true</property>
|
||||
<property name="top-margin">8</property>
|
||||
<property name="left-margin">8</property>
|
||||
<property name="bottom-margin">8</property>
|
||||
<property name="right-margin">8</property>
|
||||
<style>
|
||||
<class name="clipboard-content-view"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkButton" id="reveal_button">
|
||||
<property name="visible">false</property>
|
||||
<property name="halign">2</property>
|
||||
<property name="valign">1</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">view-reveal-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkButton" id="hide_button">
|
||||
<property name="visible">false</property>
|
||||
<property name="halign">2</property>
|
||||
<property name="valign">1</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<style>
|
||||
<class name="opaque"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">view-conceal-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</interface>
|
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
DO NOT EDIT!
|
||||
This file was @generated by blueprint-compiler. Instead, edit the
|
||||
corresponding .blp file and regenerate this file with blueprint-compiler.
|
||||
-->
|
||||
<interface domain="com.mitchellh.ghostty">
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<object class="AdwMessageDialog" id="clipboard_confirmation_window">
|
||||
<property name="heading" translatable="true">Authorize Clipboard Access</property>
|
||||
<property name="body" translatable="true">An application is attempting to write to the clipboard. The current clipboard contents are shown below.</property>
|
||||
<responses>
|
||||
<response id="cancel" translatable="true" appearance="suggested">Deny</response>
|
||||
<response id="ok" translatable="true" appearance="destructive">Allow</response>
|
||||
</responses>
|
||||
<property name="default-response">cancel</property>
|
||||
<property name="close-response">cancel</property>
|
||||
<property name="extra-child">
|
||||
<object class="GtkOverlay">
|
||||
<style>
|
||||
<class name="osd"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="text_view_scroll">
|
||||
<property name="width-request">500</property>
|
||||
<property name="height-request">250</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="cursor-visible">false</property>
|
||||
<property name="editable">false</property>
|
||||
<property name="monospace">true</property>
|
||||
<property name="top-margin">8</property>
|
||||
<property name="left-margin">8</property>
|
||||
<property name="bottom-margin">8</property>
|
||||
<property name="right-margin">8</property>
|
||||
<style>
|
||||
<class name="clipboard-content-view"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkButton" id="reveal_button">
|
||||
<property name="visible">false</property>
|
||||
<property name="halign">2</property>
|
||||
<property name="valign">1</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">view-reveal-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkButton" id="hide_button">
|
||||
<property name="visible">false</property>
|
||||
<property name="halign">2</property>
|
||||
<property name="valign">1</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<style>
|
||||
<class name="opaque"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">view-conceal-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</interface>
|
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
DO NOT EDIT!
|
||||
This file was @generated by blueprint-compiler. Instead, edit the
|
||||
corresponding .blp file and regenerate this file with blueprint-compiler.
|
||||
-->
|
||||
<interface domain="com.mitchellh.ghostty">
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<object class="AdwMessageDialog" id="clipboard_confirmation_window">
|
||||
<property name="heading" translatable="true">Warning: Potentially Unsafe Paste</property>
|
||||
<property name="body" translatable="true">Pasting this text into the terminal may be dangerous as it looks like some commands may be executed.</property>
|
||||
<responses>
|
||||
<response id="cancel" translatable="true" appearance="suggested">Cancel</response>
|
||||
<response id="ok" translatable="true" appearance="destructive">Paste</response>
|
||||
</responses>
|
||||
<property name="default-response">cancel</property>
|
||||
<property name="close-response">cancel</property>
|
||||
<property name="extra-child">
|
||||
<object class="GtkOverlay">
|
||||
<style>
|
||||
<class name="osd"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="text_view_scroll">
|
||||
<property name="width-request">500</property>
|
||||
<property name="height-request">250</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="cursor-visible">false</property>
|
||||
<property name="editable">false</property>
|
||||
<property name="monospace">true</property>
|
||||
<property name="top-margin">8</property>
|
||||
<property name="left-margin">8</property>
|
||||
<property name="bottom-margin">8</property>
|
||||
<property name="right-margin">8</property>
|
||||
<style>
|
||||
<class name="clipboard-content-view"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkButton" id="reveal_button">
|
||||
<property name="visible">false</property>
|
||||
<property name="halign">2</property>
|
||||
<property name="valign">1</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">view-reveal-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkButton" id="hide_button">
|
||||
<property name="visible">false</property>
|
||||
<property name="halign">2</property>
|
||||
<property name="valign">1</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="margin-top">12</property>
|
||||
<style>
|
||||
<class name="opaque"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">view-conceal-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</interface>
|
@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
DO NOT EDIT!
|
||||
This file was @generated by blueprint-compiler. Instead, edit the
|
||||
corresponding .blp file and regenerate this file with blueprint-compiler.
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<object class="AdwMessageDialog" id="config_errors_dialog">
|
||||
<property name="heading" translatable="yes">Configuration Errors</property>
|
||||
<property name="body" translatable="yes">One or more configuration errors were found. Please review the errors below, and either reload your configuration or ignore these errors.</property>
|
||||
<responses>
|
||||
<response id="ignore" translatable="yes">Ignore</response>
|
||||
<response id="reload" translatable="yes" appearance="suggested">Reload Configuration</response>
|
||||
</responses>
|
||||
<property name="extra-child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="min-content-width">500</property>
|
||||
<property name="min-content-height">100</property>
|
||||
<child>
|
||||
<object class="GtkTextView">
|
||||
<property name="editable">false</property>
|
||||
<property name="cursor-visible">false</property>
|
||||
<property name="top-margin">8</property>
|
||||
<property name="bottom-margin">8</property>
|
||||
<property name="left-margin">8</property>
|
||||
<property name="right-margin">8</property>
|
||||
<property name="buffer">
|
||||
<object class="GtkTextBuffer" id="error_message"></object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</interface>
|
@ -1,21 +1,15 @@
|
||||
# GTK UI files
|
||||
|
||||
This directory is for storing GTK resource definitions. With one exception, the
|
||||
files should be be in the Blueprint markup language.
|
||||
This directory is for storing GTK blueprints. GTK blueprints are compiled into
|
||||
GTK resource builder `.ui` files by `blueprint-compiler` at build time and then
|
||||
converted into an embeddable resource by `glib-compile-resources`.
|
||||
|
||||
Resource files should be stored in directories that represent the minimum
|
||||
Adwaita version needed to use that resource. Resource files should also be
|
||||
formatted using `blueprint-compiler format` as well to ensure consistency.
|
||||
Blueprint files should be stored in directories that represent the minimum
|
||||
Adwaita version needed to use that resource. Blueprint files should also be
|
||||
formatted using `blueprint-compiler format` as well to ensure consistency
|
||||
(formatting will be checked in CI).
|
||||
|
||||
The one exception to files being in Blueprint markup language is when Adwaita
|
||||
features are used that the `blueprint-compiler` on a supported platform does not
|
||||
compile. For example, Debian 12 includes Adwaita 1.2 and `blueprint-compiler`
|
||||
0.6.0. Adwaita 1.2 includes support for `MessageDialog` but `blueprint-compiler`
|
||||
0.6.0 does not. In cases like that the Blueprint markup should be compiled on a
|
||||
platform that provides a new enough `blueprint-compiler` and the resulting `.ui`
|
||||
file should be committed to the Ghostty source code. Care should be taken that
|
||||
the `.blp` file and the `.ui` file remain in sync.
|
||||
|
||||
In all other cases only the `.blp` should be committed to the Ghostty source
|
||||
code. The build process will use `blueprint-compiler` to generate the `.ui`
|
||||
files necessary at runtime.
|
||||
`blueprint-compiler` version 0.16.0 or newer is required to compile Blueprint
|
||||
files. If your system does not have `blueprint-compiler` or does not have a
|
||||
new enough version you can use the generated source tarballs, which contain
|
||||
precompiled versions of the blueprints.
|
||||
|
@ -410,7 +410,7 @@ pub const Window = struct {
|
||||
_: *gdk.Surface,
|
||||
monitor: *gdk.Monitor,
|
||||
apprt_window: *ApprtWindow,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const window = apprt_window.window.as(gtk.Window);
|
||||
const size = apprt_window.config.quick_terminal_size;
|
||||
const position = apprt_window.config.quick_terminal_position;
|
||||
|
@ -662,34 +662,6 @@ fn addGTK(
|
||||
}
|
||||
|
||||
{
|
||||
// For our actual build, we validate our GTK builder files if we can.
|
||||
{
|
||||
const gtk_builder_check = b.addExecutable(.{
|
||||
.name = "gtk_builder_check",
|
||||
.root_source_file = b.path("src/apprt/gtk/builder_check.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
gtk_builder_check.root_module.addOptions("build_options", self.options);
|
||||
if (gobject_) |gobject| {
|
||||
gtk_builder_check.root_module.addImport(
|
||||
"gtk",
|
||||
gobject.module("gtk4"),
|
||||
);
|
||||
gtk_builder_check.root_module.addImport(
|
||||
"adw",
|
||||
gobject.module("adw1"),
|
||||
);
|
||||
}
|
||||
|
||||
for (gresource.dependencies) |pathname| {
|
||||
const extension = std.fs.path.extension(pathname);
|
||||
if (!std.mem.eql(u8, extension, ".ui")) continue;
|
||||
const check = b.addRunArtifact(gtk_builder_check);
|
||||
check.addFileArg(b.path(pathname));
|
||||
step.step.dependOn(&check.step);
|
||||
}
|
||||
}
|
||||
|
||||
// Get our gresource c/h files and add them to our build.
|
||||
const dist = gtkDistResources(b);
|
||||
step.addCSourceFile(.{ .file = dist.resources_c.path(b), .flags = &.{} });
|
||||
|
@ -24,6 +24,9 @@ pub const Options = struct {
|
||||
/// If true, force a plain list of themes.
|
||||
plain: bool = false,
|
||||
|
||||
/// Specifies the color scheme of the themes to include in the list.
|
||||
color: enum { all, dark, light } = .all,
|
||||
|
||||
pub fn deinit(self: Options) void {
|
||||
_ = self;
|
||||
}
|
||||
@ -93,6 +96,9 @@ const ThemeListElement = struct {
|
||||
/// * `--path`: Show the full path to the theme.
|
||||
///
|
||||
/// * `--plain`: Force a plain listing of themes.
|
||||
///
|
||||
/// * `--color`: Specify the color scheme of the themes included in the list.
|
||||
/// This can be `dark`, `light`, or `all`. The default is `all`.
|
||||
pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
var opts: Options = .{};
|
||||
defer opts.deinit();
|
||||
@ -137,11 +143,30 @@ pub fn run(gpa_alloc: std.mem.Allocator) !u8 {
|
||||
if (std.mem.eql(u8, entry.name, ".DS_Store"))
|
||||
continue;
|
||||
count += 1;
|
||||
try themes.append(.{
|
||||
.location = loc.location,
|
||||
.path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name }),
|
||||
.theme = try alloc.dupe(u8, entry.name),
|
||||
});
|
||||
|
||||
const path = try std.fs.path.join(alloc, &.{ loc.dir, entry.name });
|
||||
// if there is no need to filter just append the theme to the list
|
||||
if (opts.color == .all) {
|
||||
try themes.append(.{
|
||||
.path = path,
|
||||
.location = loc.location,
|
||||
.theme = try alloc.dupe(u8, entry.name),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise check if the theme should be included based on the provided options
|
||||
var config = try Config.default(alloc);
|
||||
defer config.deinit();
|
||||
try config.loadFile(config._arena.?.allocator(), path);
|
||||
|
||||
if (shouldIncludeTheme(opts, config)) {
|
||||
try themes.append(.{
|
||||
.path = path,
|
||||
.location = loc.location,
|
||||
.theme = try alloc.dupe(u8, entry.name),
|
||||
});
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@ -1594,3 +1619,13 @@ fn preview(allocator: std.mem.Allocator, themes: []ThemeListElement) !void {
|
||||
defer app.deinit();
|
||||
try app.run();
|
||||
}
|
||||
|
||||
fn shouldIncludeTheme(opts: Options, theme_config: Config) bool {
|
||||
const rf = @as(f32, @floatFromInt(theme_config.background.r)) / 255.0;
|
||||
const gf = @as(f32, @floatFromInt(theme_config.background.g)) / 255.0;
|
||||
const bf = @as(f32, @floatFromInt(theme_config.background.b)) / 255.0;
|
||||
const luminance = 0.2126 * rf + 0.7152 * gf + 0.0722 * bf;
|
||||
const is_dark = luminance < 0.5;
|
||||
|
||||
return (opts.color == .dark and is_dark) or (opts.color == .light and !is_dark);
|
||||
}
|
||||
|
@ -2008,7 +2008,7 @@ keybind: Keybinds = .{},
|
||||
|
||||
/// macOS doesn't have a distinct "alt" key and instead has the "option"
|
||||
/// key which behaves slightly differently. On macOS by default, the
|
||||
/// option key plus a character will sometimes produces a Unicode character.
|
||||
/// option key plus a character will sometimes produce a Unicode character.
|
||||
/// For example, on US standard layouts option-b produces "∫". This may be
|
||||
/// undesirable if you want to use "option" as an "alt" key for keybindings
|
||||
/// in terminal programs or shells.
|
||||
|
@ -166,7 +166,7 @@ fn beforeSend(
|
||||
event_val: sentry.c.sentry_value_t,
|
||||
_: ?*anyopaque,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.C) sentry.c.sentry_value_t {
|
||||
) callconv(.c) sentry.c.sentry_value_t {
|
||||
// The native SDK at the time of writing doesn't support thread-local
|
||||
// scopes. The full SDK has one global scope. So we use the beforeSend
|
||||
// handler to set thread-specific data such as window size, grid size,
|
||||
@ -237,7 +237,7 @@ fn beforeSend(
|
||||
}
|
||||
|
||||
pub const Transport = struct {
|
||||
pub fn send(envelope: *sentry.Envelope, ud: ?*anyopaque) callconv(.C) void {
|
||||
pub fn send(envelope: *sentry.Envelope, ud: ?*anyopaque) callconv(.c) void {
|
||||
_ = ud;
|
||||
defer envelope.deinit();
|
||||
|
||||
|
@ -380,7 +380,7 @@ test getIndex {
|
||||
const testEmoji = font.embedded.emoji;
|
||||
const testEmojiText = font.embedded.emoji_text;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = Collection.init();
|
||||
@ -461,7 +461,7 @@ test "getIndex disabled font style" {
|
||||
var atlas_grayscale = try font.Atlas.init(alloc, 512, .grayscale);
|
||||
defer atlas_grayscale.deinit(alloc);
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = Collection.init();
|
||||
@ -513,7 +513,7 @@ test "getIndex box glyph" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
const c = Collection.init();
|
||||
|
@ -78,8 +78,8 @@ pub const AddError = Allocator.Error || error{
|
||||
/// next in priority if others exist already, i.e. it'll be the _last_ to be
|
||||
/// searched for a glyph in that list.
|
||||
///
|
||||
/// The collection takes ownership of the face. The face will be deallocated
|
||||
/// when the collection is deallocated.
|
||||
/// If no error is encountered then the collection takes ownership of the face,
|
||||
/// in which case face will be deallocated when the collection is deallocated.
|
||||
///
|
||||
/// If a loaded face is added to the collection, it should be the same
|
||||
/// size as all the other faces in the collection. This function will not
|
||||
@ -700,7 +700,7 @@ test "add full" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.regular;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
@ -714,15 +714,18 @@ test "add full" {
|
||||
) });
|
||||
}
|
||||
|
||||
try testing.expectError(error.CollectionFull, c.add(
|
||||
alloc,
|
||||
.regular,
|
||||
.{ .loaded = try Face.init(
|
||||
lib,
|
||||
testFont,
|
||||
.{ .size = .{ .points = 12 } },
|
||||
) },
|
||||
));
|
||||
var face = try Face.init(
|
||||
lib,
|
||||
testFont,
|
||||
.{ .size = .{ .points = 12 } },
|
||||
);
|
||||
// We have to deinit it manually since the
|
||||
// collection doesn't do it if adding fails.
|
||||
defer face.deinit();
|
||||
try testing.expectError(
|
||||
error.CollectionFull,
|
||||
c.add(alloc, .regular, .{ .loaded = face }),
|
||||
);
|
||||
}
|
||||
|
||||
test "add deferred without loading options" {
|
||||
@ -746,7 +749,7 @@ test getFace {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.regular;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
@ -770,7 +773,7 @@ test getIndex {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.regular;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
@ -801,7 +804,7 @@ test completeStyles {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.regular;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
@ -828,7 +831,7 @@ test setSize {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.regular;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
@ -851,7 +854,7 @@ test hasCodepoint {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.regular;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
@ -875,7 +878,7 @@ test "hasCodepoint emoji default graphical" {
|
||||
const alloc = testing.allocator;
|
||||
const testEmoji = font.embedded.emoji;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
@ -898,7 +901,7 @@ test "metrics" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.inconsolata;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var c = init();
|
||||
|
@ -407,7 +407,7 @@ test "fontconfig" {
|
||||
const alloc = testing.allocator;
|
||||
|
||||
// Load freetype
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
// Get a deferred face from fontconfig
|
||||
@ -425,7 +425,8 @@ test "fontconfig" {
|
||||
try testing.expect(n.len > 0);
|
||||
|
||||
// Load it and verify it works
|
||||
const face = try def.load(lib, .{ .size = .{ .points = 12 } });
|
||||
var face = try def.load(lib, .{ .size = .{ .points = 12 } });
|
||||
defer face.deinit();
|
||||
try testing.expect(face.glyphIndex(' ') != null);
|
||||
}
|
||||
|
||||
@ -437,7 +438,7 @@ test "coretext" {
|
||||
const alloc = testing.allocator;
|
||||
|
||||
// Load freetype
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
// Get a deferred face from fontconfig
|
||||
@ -456,6 +457,7 @@ test "coretext" {
|
||||
try testing.expect(n.len > 0);
|
||||
|
||||
// Load it and verify it works
|
||||
const face = try def.load(lib, .{ .size = .{ .points = 12 } });
|
||||
var face = try def.load(lib, .{ .size = .{ .points = 12 } });
|
||||
defer face.deinit();
|
||||
try testing.expect(face.glyphIndex(' ') != null);
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ test getIndex {
|
||||
const alloc = testing.allocator;
|
||||
// const testEmoji = @import("test.zig").fontEmoji;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var grid = try testGrid(.normal, alloc, lib);
|
||||
|
@ -50,7 +50,7 @@ pub const InitError = Library.InitError;
|
||||
|
||||
/// Initialize a new SharedGridSet.
|
||||
pub fn init(alloc: Allocator) InitError!SharedGridSet {
|
||||
var font_lib = try Library.init();
|
||||
var font_lib = try Library.init(alloc);
|
||||
errdefer font_lib.deinit();
|
||||
|
||||
return .{
|
||||
|
@ -46,7 +46,11 @@ pub const Face = struct {
|
||||
};
|
||||
|
||||
/// Initialize a CoreText-based font from a TTF/TTC in memory.
|
||||
pub fn init(lib: font.Library, source: [:0]const u8, opts: font.face.Options) !Face {
|
||||
pub fn init(
|
||||
lib: font.Library,
|
||||
source: [:0]const u8,
|
||||
opts: font.face.Options,
|
||||
) !Face {
|
||||
_ = lib;
|
||||
|
||||
const data = try macos.foundation.Data.createWithBytesNoCopy(source);
|
||||
@ -914,7 +918,7 @@ test "in-memory" {
|
||||
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
|
||||
defer atlas.deinit(alloc);
|
||||
|
||||
var lib = try font.Library.init();
|
||||
var lib = try font.Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
@ -941,7 +945,7 @@ test "variable" {
|
||||
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
|
||||
defer atlas.deinit(alloc);
|
||||
|
||||
var lib = try font.Library.init();
|
||||
var lib = try font.Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
@ -968,7 +972,7 @@ test "variable set variation" {
|
||||
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
|
||||
defer atlas.deinit(alloc);
|
||||
|
||||
var lib = try font.Library.init();
|
||||
var lib = try font.Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
@ -996,7 +1000,7 @@ test "svg font table" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.julia_mono;
|
||||
|
||||
var lib = try font.Library.init();
|
||||
var lib = try font.Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
@ -1010,9 +1014,10 @@ test "svg font table" {
|
||||
|
||||
test "glyphIndex colored vs text" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.julia_mono;
|
||||
|
||||
var lib = try font.Library.init();
|
||||
var lib = try font.Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
|
@ -29,12 +29,20 @@ pub const Face = struct {
|
||||
assert(font.face.FreetypeLoadFlags != void);
|
||||
}
|
||||
|
||||
/// Our freetype library
|
||||
lib: freetype.Library,
|
||||
/// Our Library
|
||||
lib: Library,
|
||||
|
||||
/// Our font face.
|
||||
face: freetype.Face,
|
||||
|
||||
/// This mutex MUST be held while doing anything with the
|
||||
/// glyph slot on the freetype face, because this struct
|
||||
/// may be shared across multiple surfaces.
|
||||
///
|
||||
/// This means that anywhere where `self.face.loadGlyph`
|
||||
/// is called, this mutex must be held.
|
||||
ft_mutex: *std.Thread.Mutex,
|
||||
|
||||
/// Harfbuzz font corresponding to this face.
|
||||
hb_font: harfbuzz.Font,
|
||||
|
||||
@ -59,30 +67,52 @@ pub const Face = struct {
|
||||
};
|
||||
|
||||
/// Initialize a new font face with the given source in-memory.
|
||||
pub fn initFile(lib: Library, path: [:0]const u8, index: i32, opts: font.face.Options) !Face {
|
||||
pub fn initFile(
|
||||
lib: Library,
|
||||
path: [:0]const u8,
|
||||
index: i32,
|
||||
opts: font.face.Options,
|
||||
) !Face {
|
||||
lib.mutex.lock();
|
||||
defer lib.mutex.unlock();
|
||||
const face = try lib.lib.initFace(path, index);
|
||||
errdefer face.deinit();
|
||||
return try initFace(lib, face, opts);
|
||||
}
|
||||
|
||||
/// Initialize a new font face with the given source in-memory.
|
||||
pub fn init(lib: Library, source: [:0]const u8, opts: font.face.Options) !Face {
|
||||
pub fn init(
|
||||
lib: Library,
|
||||
source: [:0]const u8,
|
||||
opts: font.face.Options,
|
||||
) !Face {
|
||||
lib.mutex.lock();
|
||||
defer lib.mutex.unlock();
|
||||
const face = try lib.lib.initMemoryFace(source, 0);
|
||||
errdefer face.deinit();
|
||||
return try initFace(lib, face, opts);
|
||||
}
|
||||
|
||||
fn initFace(lib: Library, face: freetype.Face, opts: font.face.Options) !Face {
|
||||
fn initFace(
|
||||
lib: Library,
|
||||
face: freetype.Face,
|
||||
opts: font.face.Options,
|
||||
) !Face {
|
||||
try face.selectCharmap(.unicode);
|
||||
try setSize_(face, opts.size);
|
||||
|
||||
var hb_font = try harfbuzz.freetype.createFont(face.handle);
|
||||
errdefer hb_font.destroy();
|
||||
|
||||
const ft_mutex = try lib.alloc.create(std.Thread.Mutex);
|
||||
errdefer lib.alloc.destroy(ft_mutex);
|
||||
ft_mutex.* = .{};
|
||||
|
||||
var result: Face = .{
|
||||
.lib = lib.lib,
|
||||
.lib = lib,
|
||||
.face = face,
|
||||
.hb_font = hb_font,
|
||||
.ft_mutex = ft_mutex,
|
||||
.load_flags = opts.freetype_load_flags,
|
||||
};
|
||||
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
||||
@ -114,7 +144,13 @@ pub const Face = struct {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Face) void {
|
||||
self.face.deinit();
|
||||
self.lib.alloc.destroy(self.ft_mutex);
|
||||
{
|
||||
self.lib.mutex.lock();
|
||||
defer self.lib.mutex.unlock();
|
||||
|
||||
self.face.deinit();
|
||||
}
|
||||
self.hb_font.destroy();
|
||||
self.* = undefined;
|
||||
}
|
||||
@ -147,11 +183,7 @@ pub const Face = struct {
|
||||
self.face.ref();
|
||||
errdefer self.face.deinit();
|
||||
|
||||
var f = try initFace(
|
||||
.{ .lib = self.lib },
|
||||
self.face,
|
||||
opts,
|
||||
);
|
||||
var f = try initFace(self.lib, self.face, opts);
|
||||
errdefer f.deinit();
|
||||
f.synthetic = self.synthetic;
|
||||
f.synthetic.bold = true;
|
||||
@ -166,11 +198,7 @@ pub const Face = struct {
|
||||
self.face.ref();
|
||||
errdefer self.face.deinit();
|
||||
|
||||
var f = try initFace(
|
||||
.{ .lib = self.lib },
|
||||
self.face,
|
||||
opts,
|
||||
);
|
||||
var f = try initFace(self.lib, self.face, opts);
|
||||
errdefer f.deinit();
|
||||
f.synthetic = self.synthetic;
|
||||
f.synthetic.italic = true;
|
||||
@ -228,7 +256,7 @@ pub const Face = struct {
|
||||
// first thing we have to do is get all the vars and put them into
|
||||
// an array.
|
||||
const mm = try self.face.getMMVar();
|
||||
defer self.lib.doneMMVar(mm);
|
||||
defer self.lib.lib.doneMMVar(mm);
|
||||
|
||||
// To avoid allocations, we cap the number of variation axes we can
|
||||
// support. This is arbitrary but Firefox caps this at 16 so I
|
||||
@ -270,6 +298,9 @@ pub const Face = struct {
|
||||
|
||||
/// Returns true if the given glyph ID is colorized.
|
||||
pub fn isColorGlyph(self: *const Face, glyph_id: u32) bool {
|
||||
self.ft_mutex.lock();
|
||||
defer self.ft_mutex.unlock();
|
||||
|
||||
// Load the glyph and see what pixel mode it renders with.
|
||||
// All modes other than BGRA are non-color.
|
||||
// If the glyph fails to load, just return false.
|
||||
@ -296,6 +327,9 @@ pub const Face = struct {
|
||||
glyph_index: u32,
|
||||
opts: font.face.RenderOptions,
|
||||
) !Glyph {
|
||||
self.ft_mutex.lock();
|
||||
defer self.ft_mutex.unlock();
|
||||
|
||||
const metrics = opts.grid_metrics;
|
||||
|
||||
// If we have synthetic italic, then we apply a transformation matrix.
|
||||
@ -741,6 +775,9 @@ pub const Face = struct {
|
||||
// If we fail to load any visible ASCII we just use max_advance from
|
||||
// the metrics provided by FreeType.
|
||||
const cell_width: f64 = cell_width: {
|
||||
self.ft_mutex.lock();
|
||||
defer self.ft_mutex.unlock();
|
||||
|
||||
var max: f64 = 0.0;
|
||||
var c: u8 = ' ';
|
||||
while (c < 127) : (c += 1) {
|
||||
@ -780,6 +817,8 @@ pub const Face = struct {
|
||||
|
||||
break :heights .{
|
||||
cap: {
|
||||
self.ft_mutex.lock();
|
||||
defer self.ft_mutex.unlock();
|
||||
if (face.getCharIndex('H')) |glyph_index| {
|
||||
if (face.loadGlyph(glyph_index, .{
|
||||
.render = true,
|
||||
@ -791,6 +830,8 @@ pub const Face = struct {
|
||||
break :cap null;
|
||||
},
|
||||
ex: {
|
||||
self.ft_mutex.lock();
|
||||
defer self.ft_mutex.unlock();
|
||||
if (face.getCharIndex('x')) |glyph_index| {
|
||||
if (face.loadGlyph(glyph_index, .{
|
||||
.render = true,
|
||||
@ -832,7 +873,7 @@ test {
|
||||
const testFont = font.embedded.inconsolata;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
|
||||
@ -881,7 +922,7 @@ test "color emoji" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.emoji;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var atlas = try font.Atlas.init(alloc, 512, .rgba);
|
||||
@ -936,7 +977,7 @@ test "mono to rgba" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.emoji;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var atlas = try font.Atlas.init(alloc, 512, .rgba);
|
||||
@ -958,7 +999,7 @@ test "svg font table" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.julia_mono;
|
||||
|
||||
var lib = try font.Library.init();
|
||||
var lib = try font.Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try Face.init(lib, testFont, .{ .size = .{ .points = 12, .xdpi = 72, .ydpi = 72 } });
|
||||
@ -995,7 +1036,7 @@ test "bitmap glyph" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.terminus_ttf;
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var atlas = try font.Atlas.init(alloc, 512, .grayscale);
|
||||
|
@ -1,5 +1,7 @@
|
||||
//! A library represents the shared state that the underlying font
|
||||
//! library implementation(s) require per-process.
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const builtin = @import("builtin");
|
||||
const options = @import("main.zig").options;
|
||||
const freetype = @import("freetype");
|
||||
@ -24,13 +26,26 @@ pub const Library = switch (options.backend) {
|
||||
pub const FreetypeLibrary = struct {
|
||||
lib: freetype.Library,
|
||||
|
||||
pub const InitError = freetype.Error;
|
||||
alloc: Allocator,
|
||||
|
||||
pub fn init() InitError!Library {
|
||||
return Library{ .lib = try freetype.Library.init() };
|
||||
/// Mutex to be held any time the library is
|
||||
/// being used to create or destroy a face.
|
||||
mutex: *std.Thread.Mutex,
|
||||
|
||||
pub const InitError = freetype.Error || Allocator.Error;
|
||||
|
||||
pub fn init(alloc: Allocator) InitError!Library {
|
||||
const lib = try freetype.Library.init();
|
||||
errdefer lib.deinit();
|
||||
|
||||
const mutex = try alloc.create(std.Thread.Mutex);
|
||||
mutex.* = .{};
|
||||
|
||||
return Library{ .lib = lib, .alloc = alloc, .mutex = mutex };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Library) void {
|
||||
self.alloc.destroy(self.mutex);
|
||||
self.lib.deinit();
|
||||
}
|
||||
};
|
||||
@ -38,7 +53,8 @@ pub const FreetypeLibrary = struct {
|
||||
pub const NoopLibrary = struct {
|
||||
pub const InitError = error{};
|
||||
|
||||
pub fn init() InitError!Library {
|
||||
pub fn init(alloc: Allocator) InitError!Library {
|
||||
_ = alloc;
|
||||
return Library{};
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ test "SVG" {
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.julia_mono;
|
||||
|
||||
var lib = try font.Library.init();
|
||||
var lib = try font.Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
||||
var face = try font.Face.init(lib, testFont, .{ .size = .{ .points = 12 } });
|
||||
|
@ -1761,7 +1761,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper {
|
||||
.nerd_font => font.embedded.nerd_font,
|
||||
};
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
errdefer lib.deinit();
|
||||
|
||||
var c = Collection.init();
|
||||
|
@ -1220,7 +1220,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper {
|
||||
.arabic => font.embedded.arabic,
|
||||
};
|
||||
|
||||
var lib = try Library.init();
|
||||
var lib = try Library.init(alloc);
|
||||
errdefer lib.deinit();
|
||||
|
||||
var c = Collection.init();
|
||||
|
@ -279,6 +279,7 @@ pub const Action = union(enum) {
|
||||
/// Scroll the screen varying amounts.
|
||||
scroll_to_top,
|
||||
scroll_to_bottom,
|
||||
scroll_to_selection,
|
||||
scroll_page_up,
|
||||
scroll_page_down,
|
||||
scroll_page_fractional: f32,
|
||||
@ -345,7 +346,7 @@ pub const Action = union(enum) {
|
||||
move_tab: isize,
|
||||
|
||||
/// Toggle the tab overview.
|
||||
/// This only works with libadwaita enabled currently.
|
||||
/// This only works with libadwaita version 1.4.0 or newer.
|
||||
toggle_tab_overview,
|
||||
|
||||
/// Change the title of the current focused surface via a prompt.
|
||||
@ -567,6 +568,8 @@ pub const Action = union(enum) {
|
||||
left,
|
||||
up,
|
||||
auto, // splits along the larger direction
|
||||
|
||||
pub const default: SplitDirection = .auto;
|
||||
};
|
||||
|
||||
pub const SplitFocusDirection = enum {
|
||||
@ -728,7 +731,28 @@ pub const Action = union(enum) {
|
||||
Action.CursorKey => return Error.InvalidAction,
|
||||
|
||||
else => {
|
||||
const idx = colonIdx orelse return Error.InvalidFormat;
|
||||
// Get the parameter after the colon. The parameter
|
||||
// can be optional for action types that can have a
|
||||
// "default" decl.
|
||||
const idx = colonIdx orelse {
|
||||
switch (@typeInfo(field.type)) {
|
||||
.@"struct",
|
||||
.@"union",
|
||||
.@"enum",
|
||||
=> if (@hasDecl(field.type, "default")) {
|
||||
return @unionInit(
|
||||
Action,
|
||||
field.name,
|
||||
@field(field.type, "default"),
|
||||
);
|
||||
},
|
||||
|
||||
else => {},
|
||||
}
|
||||
|
||||
return Error.InvalidFormat;
|
||||
};
|
||||
|
||||
const param = input[idx + 1 ..];
|
||||
return @unionInit(
|
||||
Action,
|
||||
@ -789,6 +813,7 @@ pub const Action = union(enum) {
|
||||
.select_all,
|
||||
.scroll_to_top,
|
||||
.scroll_to_bottom,
|
||||
.scroll_to_selection,
|
||||
.scroll_page_up,
|
||||
.scroll_page_down,
|
||||
.scroll_page_fractional,
|
||||
@ -2013,6 +2038,17 @@ test "parse: action with enum" {
|
||||
}
|
||||
}
|
||||
|
||||
test "parse: action with enum with default" {
|
||||
const testing = std.testing;
|
||||
|
||||
// parameter
|
||||
{
|
||||
const binding = try parseSingle("a=new_split");
|
||||
try testing.expect(binding.action == .new_split);
|
||||
try testing.expectEqual(Action.SplitDirection.auto, binding.action.new_split);
|
||||
}
|
||||
}
|
||||
|
||||
test "parse: action with int" {
|
||||
const testing = std.testing;
|
||||
|
||||
|
@ -170,6 +170,12 @@ fn actionCommands(action: Action.Key) []const Command {
|
||||
.description = "Scroll to the bottom of the screen.",
|
||||
}},
|
||||
|
||||
.scroll_to_selection => comptime &.{.{
|
||||
.action = .scroll_to_selection,
|
||||
.title = "Scroll to Selection",
|
||||
.description = "Scroll to the selected text.",
|
||||
}},
|
||||
|
||||
.scroll_page_up => comptime &.{.{
|
||||
.action = .scroll_page_up,
|
||||
.title = "Scroll Page Up",
|
||||
|
@ -401,7 +401,8 @@ pub const Key = enum(c_int) {
|
||||
kp_delete,
|
||||
kp_begin,
|
||||
|
||||
// TODO: media keys
|
||||
// special keys
|
||||
context_menu,
|
||||
|
||||
// modifiers
|
||||
left_shift,
|
||||
@ -579,6 +580,7 @@ pub const Key = enum(c_int) {
|
||||
.backspace => cimgui.c.ImGuiKey_Backspace,
|
||||
.print_screen => cimgui.c.ImGuiKey_PrintScreen,
|
||||
.pause => cimgui.c.ImGuiKey_Pause,
|
||||
.context_menu => cimgui.c.ImGuiKey_Menu,
|
||||
|
||||
.f1 => cimgui.c.ImGuiKey_F1,
|
||||
.f2 => cimgui.c.ImGuiKey_F2,
|
||||
|
@ -153,6 +153,7 @@ const code_to_key = code_to_key: {
|
||||
.{ "Numpad0", .kp_0 },
|
||||
.{ "NumpadDecimal", .kp_decimal },
|
||||
.{ "NumpadEqual", .kp_equal },
|
||||
.{ "ContextMenu", .context_menu },
|
||||
.{ "ControlLeft", .left_control },
|
||||
.{ "ShiftLeft", .left_shift },
|
||||
.{ "AltLeft", .left_alt },
|
||||
|
@ -444,7 +444,7 @@ pub const FlatpakHostCommand = struct {
|
||||
_: [*c]const u8,
|
||||
params: ?*c.GVariant,
|
||||
ud: ?*anyopaque,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const self = @as(*FlatpakHostCommand, @ptrCast(@alignCast(ud)));
|
||||
const state = state: {
|
||||
self.state_mutex.lock();
|
||||
|
@ -1524,7 +1524,7 @@ const CompletionBlock = objc.Block(struct { self: *Metal }, .{
|
||||
fn bufferCompleted(
|
||||
block: *const CompletionBlock.Context,
|
||||
buffer_id: objc.c.id,
|
||||
) callconv(.C) void {
|
||||
) callconv(.c) void {
|
||||
const self = block.self;
|
||||
const buffer = objc.Object.fromId(buffer_id);
|
||||
|
||||
|
@ -250,7 +250,7 @@ fn spvCross(
|
||||
// It would be better to get this out into an output parameter to
|
||||
// show users but for now we can just log it.
|
||||
c.spvc_context_set_error_callback(ctx, @ptrCast(&(struct {
|
||||
fn callback(_: ?*anyopaque, msg_ptr: [*c]const u8) callconv(.C) void {
|
||||
fn callback(_: ?*anyopaque, msg_ptr: [*c]const u8) callconv(.c) void {
|
||||
const msg = std.mem.sliceTo(msg_ptr, 0);
|
||||
std.log.warn("spirv-cross error message={s}", .{msg});
|
||||
}
|
||||
|
@ -745,7 +745,7 @@ const Subprocess = struct {
|
||||
});
|
||||
|
||||
arena: std.heap.ArenaAllocator,
|
||||
cwd: ?[]const u8,
|
||||
cwd: ?[:0]const u8,
|
||||
env: ?EnvMap,
|
||||
args: []const [:0]const u8,
|
||||
grid_size: renderer.GridSize,
|
||||
@ -985,8 +985,8 @@ const Subprocess = struct {
|
||||
|
||||
// We have to copy the cwd because there is no guarantee that
|
||||
// pointers in full_config remain valid.
|
||||
const cwd: ?[]u8 = if (cfg.working_directory) |cwd|
|
||||
try alloc.dupe(u8, cwd)
|
||||
const cwd: ?[:0]u8 = if (cfg.working_directory) |cwd|
|
||||
try alloc.dupeZ(u8, cwd)
|
||||
else
|
||||
null;
|
||||
|
||||
@ -1048,6 +1048,47 @@ const Subprocess = struct {
|
||||
|
||||
log.debug("starting command command={s}", .{self.args});
|
||||
|
||||
// If we can't access the cwd, then don't set any cwd and inherit.
|
||||
// This is important because our cwd can be set by the shell (OSC 7)
|
||||
// and we don't want to break new windows.
|
||||
const cwd: ?[:0]const u8 = if (self.cwd) |proposed| cwd: {
|
||||
if ((comptime build_config.flatpak) and internal_os.isFlatpak()) {
|
||||
// Flatpak sandboxing prevents access to certain reserved paths
|
||||
// regardless of configured permissions. Perform a test spawn
|
||||
// to get around this problem
|
||||
//
|
||||
// https://docs.flatpak.org/en/latest/sandbox-permissions.html#reserved-paths
|
||||
log.info("flatpak detected, will use host command to verify cwd access", .{});
|
||||
const dev_null = try std.fs.cwd().openFile("/dev/null", .{ .mode = .read_write });
|
||||
defer dev_null.close();
|
||||
var cmd: internal_os.FlatpakHostCommand = .{
|
||||
.argv = &[_][]const u8{
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
":",
|
||||
},
|
||||
.cwd = proposed,
|
||||
.stdin = dev_null.handle,
|
||||
.stdout = dev_null.handle,
|
||||
.stderr = dev_null.handle,
|
||||
};
|
||||
_ = cmd.spawn(alloc) catch |err| {
|
||||
log.warn("cannot spawn command at cwd, ignoring: {}", .{err});
|
||||
break :cwd null;
|
||||
};
|
||||
_ = try cmd.wait();
|
||||
|
||||
break :cwd proposed;
|
||||
}
|
||||
|
||||
if (std.fs.cwd().access(proposed, .{})) {
|
||||
break :cwd proposed;
|
||||
} else |err| {
|
||||
log.warn("cannot access cwd, ignoring: {}", .{err});
|
||||
break :cwd null;
|
||||
}
|
||||
} else null;
|
||||
|
||||
// In flatpak, we use the HostCommand to execute our shell.
|
||||
if (internal_os.isFlatpak()) flatpak: {
|
||||
if (comptime !build_config.flatpak) {
|
||||
@ -1058,6 +1099,7 @@ const Subprocess = struct {
|
||||
// Flatpak command must have a stable pointer.
|
||||
self.flatpak_command = .{
|
||||
.argv = self.args,
|
||||
.cwd = cwd,
|
||||
.env = if (self.env) |*env| env else null,
|
||||
.stdin = pty.slave,
|
||||
.stdout = pty.slave,
|
||||
@ -1083,18 +1125,6 @@ const Subprocess = struct {
|
||||
};
|
||||
}
|
||||
|
||||
// If we can't access the cwd, then don't set any cwd and inherit.
|
||||
// This is important because our cwd can be set by the shell (OSC 7)
|
||||
// and we don't want to break new windows.
|
||||
const cwd: ?[]const u8 = if (self.cwd) |proposed| cwd: {
|
||||
if (std.fs.cwd().access(proposed, .{})) {
|
||||
break :cwd proposed;
|
||||
} else |err| {
|
||||
log.warn("cannot access cwd, ignoring: {}", .{err});
|
||||
break :cwd null;
|
||||
}
|
||||
} else null;
|
||||
|
||||
// Build our subcommand
|
||||
var cmd: Command = .{
|
||||
.path = self.args[0],
|
||||
|
@ -239,7 +239,7 @@ fn setupBash(
|
||||
resource_dir: []const u8,
|
||||
env: *EnvMap,
|
||||
) !?config.Command {
|
||||
var args = try std.ArrayList([:0]const u8).initCapacity(alloc, 2);
|
||||
var args = try std.ArrayList([:0]const u8).initCapacity(alloc, 3);
|
||||
defer args.deinit();
|
||||
|
||||
// Iterator that yields each argument in the original command line.
|
||||
@ -247,12 +247,17 @@ fn setupBash(
|
||||
var iter = try command.argIterator(alloc);
|
||||
defer iter.deinit();
|
||||
|
||||
// Start accumulating arguments with the executable and `--posix` mode flag.
|
||||
// Start accumulating arguments with the executable and initial flags.
|
||||
if (iter.next()) |exe| {
|
||||
try args.append(try alloc.dupeZ(u8, exe));
|
||||
} else return null;
|
||||
try args.append("--posix");
|
||||
|
||||
// On macOS, we request a login shell to match that platform's norms.
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try args.append("--login");
|
||||
}
|
||||
|
||||
// Stores the list of intercepted command line flags that will be passed
|
||||
// to our shell integration script: --norc --noprofile
|
||||
// We always include at least "1" so the script can differentiate between
|
||||
@ -342,9 +347,12 @@ test "bash" {
|
||||
|
||||
const command = try setupBash(alloc, .{ .shell = "bash" }, ".", &env);
|
||||
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expect(command.?.direct.len >= 2);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try testing.expectEqualStrings("--login", command.?.direct[2]);
|
||||
}
|
||||
try testing.expectEqualStrings("./shell-integration/bash/ghostty.bash", env.get("ENV").?);
|
||||
try testing.expectEqualStrings("1", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
@ -387,9 +395,12 @@ test "bash: inject flags" {
|
||||
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --norc" }, ".", &env);
|
||||
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expect(command.?.direct.len >= 2);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try testing.expectEqualStrings("--login", command.?.direct[2]);
|
||||
}
|
||||
try testing.expectEqualStrings("1 --norc", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
|
||||
@ -400,9 +411,12 @@ test "bash: inject flags" {
|
||||
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --noprofile" }, ".", &env);
|
||||
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expect(command.?.direct.len >= 2);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try testing.expectEqualStrings("--login", command.?.direct[2]);
|
||||
}
|
||||
try testing.expectEqualStrings("1 --noprofile", env.get("GHOSTTY_BASH_INJECT").?);
|
||||
}
|
||||
}
|
||||
@ -419,18 +433,24 @@ test "bash: rcfile" {
|
||||
// bash --rcfile
|
||||
{
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --rcfile profile.sh" }, ".", &env);
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expect(command.?.direct.len >= 2);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try testing.expectEqualStrings("--login", command.?.direct[2]);
|
||||
}
|
||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||
}
|
||||
|
||||
// bash --init-file
|
||||
{
|
||||
const command = try setupBash(alloc, .{ .shell = "bash --init-file profile.sh" }, ".", &env);
|
||||
try testing.expectEqual(2, command.?.direct.len);
|
||||
try testing.expect(command.?.direct.len >= 2);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try testing.expectEqualStrings("--login", command.?.direct[2]);
|
||||
}
|
||||
try testing.expectEqualStrings("profile.sh", env.get("GHOSTTY_BASH_RCFILE").?);
|
||||
}
|
||||
}
|
||||
@ -476,25 +496,35 @@ test "bash: additional arguments" {
|
||||
// "-" argument separator
|
||||
{
|
||||
const command = try setupBash(alloc, .{ .shell = "bash - --arg file1 file2" }, ".", &env);
|
||||
try testing.expectEqual(6, command.?.direct.len);
|
||||
try testing.expect(command.?.direct.len >= 6);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("-", command.?.direct[2]);
|
||||
try testing.expectEqualStrings("--arg", command.?.direct[3]);
|
||||
try testing.expectEqualStrings("file1", command.?.direct[4]);
|
||||
try testing.expectEqualStrings("file2", command.?.direct[5]);
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try testing.expectEqualStrings("--login", command.?.direct[2]);
|
||||
}
|
||||
|
||||
const offset = if (comptime builtin.target.os.tag.isDarwin()) 3 else 2;
|
||||
try testing.expectEqualStrings("-", command.?.direct[offset + 0]);
|
||||
try testing.expectEqualStrings("--arg", command.?.direct[offset + 1]);
|
||||
try testing.expectEqualStrings("file1", command.?.direct[offset + 2]);
|
||||
try testing.expectEqualStrings("file2", command.?.direct[offset + 3]);
|
||||
}
|
||||
|
||||
// "--" argument separator
|
||||
{
|
||||
const command = try setupBash(alloc, .{ .shell = "bash -- --arg file1 file2" }, ".", &env);
|
||||
try testing.expectEqual(6, command.?.direct.len);
|
||||
try testing.expect(command.?.direct.len >= 6);
|
||||
try testing.expectEqualStrings("bash", command.?.direct[0]);
|
||||
try testing.expectEqualStrings("--posix", command.?.direct[1]);
|
||||
try testing.expectEqualStrings("--", command.?.direct[2]);
|
||||
try testing.expectEqualStrings("--arg", command.?.direct[3]);
|
||||
try testing.expectEqualStrings("file1", command.?.direct[4]);
|
||||
try testing.expectEqualStrings("file2", command.?.direct[5]);
|
||||
if (comptime builtin.target.os.tag.isDarwin()) {
|
||||
try testing.expectEqualStrings("--login", command.?.direct[2]);
|
||||
}
|
||||
|
||||
const offset = if (comptime builtin.target.os.tag.isDarwin()) 3 else 2;
|
||||
try testing.expectEqualStrings("--", command.?.direct[offset + 0]);
|
||||
try testing.expectEqualStrings("--arg", command.?.direct[offset + 1]);
|
||||
try testing.expectEqualStrings("file1", command.?.direct[offset + 2]);
|
||||
try testing.expectEqualStrings("file2", command.?.direct[offset + 3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user