Detailed changes
@@ -3438,6 +3438,7 @@ dependencies = [
"db",
"fs",
"gpui",
+ "gpui_platform",
"language",
"log",
"node_runtime",
@@ -5242,6 +5243,7 @@ dependencies = [
"fs",
"futures 0.3.31",
"gpui",
+ "gpui_platform",
"gpui_tokio",
"http_client",
"indoc",
@@ -5779,6 +5781,7 @@ dependencies = [
"fs",
"futures 0.3.31",
"gpui",
+ "gpui_platform",
"gpui_tokio",
"handlebars 4.5.0",
"language",
@@ -5814,7 +5817,7 @@ dependencies = [
name = "eval_utils"
version = "0.1.0"
dependencies = [
- "gpui",
+ "gpui_platform",
"serde",
"smol",
]
@@ -5931,7 +5934,7 @@ dependencies = [
"env_logger 0.11.8",
"extension",
"fs",
- "gpui",
+ "gpui_platform",
"language",
"log",
"reqwest_client",
@@ -6498,6 +6501,7 @@ version = "0.1.0"
dependencies = [
"fs",
"gpui",
+ "gpui_platform",
]
[[package]]
@@ -7348,16 +7352,11 @@ name = "gpui"
version = "0.2.2"
dependencies = [
"anyhow",
- "as-raw-xcb-connection",
- "ashpd",
"async-task",
"backtrace",
"bindgen 0.71.1",
"bitflags 2.10.0",
"block",
- "bytemuck",
- "calloop",
- "calloop-wayland-source",
"cbindgen",
"chrono",
"circular-buffer",
@@ -7369,21 +7368,19 @@ dependencies = [
"core-graphics 0.24.0",
"core-text",
"core-video",
- "cosmic-text",
"ctor",
"derive_more 0.99.20",
"embed-resource",
"env_logger 0.11.8",
"etagere",
- "filedescriptor",
"foreign-types 0.5.0",
"futures 0.3.31",
"gpui_macros",
+ "gpui_platform",
"http_client",
"image",
"inventory",
"itertools 0.14.0",
- "libc",
"log",
"lyon",
"mach2 0.5.0",
@@ -7394,8 +7391,6 @@ dependencies = [
"objc",
"objc2",
"objc2-metal",
- "oo7",
- "open",
"parking",
"parking_lot",
"pathfinder_geometry",
@@ -7411,7 +7406,6 @@ dependencies = [
"scheduler",
"schemars",
"seahash",
- "semver",
"serde",
"serde_json",
"slotmap",
@@ -7421,7 +7415,6 @@ dependencies = [
"stacksafe",
"strum 0.27.2",
"sum_tree",
- "swash",
"taffy",
"thiserror 2.0.17",
"unicode-segmentation",
@@ -7430,17 +7423,50 @@ dependencies = [
"util_macros",
"uuid",
"waker-fn",
+ "windows 0.61.3",
+ "zed-font-kit",
+ "zed-scap",
+]
+
+[[package]]
+name = "gpui_linux"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "as-raw-xcb-connection",
+ "ashpd",
+ "bitflags 2.10.0",
+ "bytemuck",
+ "calloop",
+ "calloop-wayland-source",
+ "collections",
+ "cosmic-text",
+ "filedescriptor",
+ "futures 0.3.31",
+ "gpui",
+ "gpui_wgpu",
+ "http_client",
+ "itertools 0.14.0",
+ "libc",
+ "log",
+ "oo7",
+ "open",
+ "parking_lot",
+ "pathfinder_geometry",
+ "profiling",
+ "raw-window-handle",
+ "smallvec",
+ "smol",
+ "strum 0.27.2",
+ "swash",
+ "util",
+ "uuid",
"wayland-backend",
"wayland-client",
"wayland-cursor",
"wayland-protocols",
"wayland-protocols-plasma",
"wayland-protocols-wlr",
- "wgpu",
- "windows 0.61.3",
- "windows-core 0.61.2",
- "windows-numerics 0.2.0",
- "windows-registry 0.5.3",
"x11-clipboard",
"x11rb",
"xkbcommon",
@@ -7449,6 +7475,47 @@ dependencies = [
"zed-xim",
]
+[[package]]
+name = "gpui_macos"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-task",
+ "bindgen 0.71.1",
+ "block",
+ "cbindgen",
+ "cocoa 0.26.0",
+ "collections",
+ "core-foundation 0.10.0",
+ "core-foundation-sys",
+ "core-graphics 0.24.0",
+ "core-text",
+ "core-video",
+ "ctor",
+ "derive_more 0.99.20",
+ "etagere",
+ "foreign-types 0.5.0",
+ "futures 0.3.31",
+ "gpui",
+ "image",
+ "itertools 0.14.0",
+ "libc",
+ "log",
+ "mach2 0.5.0",
+ "media",
+ "metal 0.29.0",
+ "objc",
+ "parking_lot",
+ "pathfinder_geometry",
+ "raw-window-handle",
+ "semver",
+ "smallvec",
+ "strum 0.27.2",
+ "util",
+ "uuid",
+ "zed-font-kit",
+]
+
[[package]]
name = "gpui_macros"
version = "0.1.0"
@@ -7460,6 +7527,16 @@ dependencies = [
"syn 2.0.106",
]
+[[package]]
+name = "gpui_platform"
+version = "0.1.0"
+dependencies = [
+ "gpui",
+ "gpui_linux",
+ "gpui_macos",
+ "gpui_windows",
+]
+
[[package]]
name = "gpui_tokio"
version = "0.1.0"
@@ -7470,6 +7547,49 @@ dependencies = [
"util",
]
+[[package]]
+name = "gpui_wgpu"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bytemuck",
+ "collections",
+ "etagere",
+ "gpui",
+ "log",
+ "parking_lot",
+ "profiling",
+ "raw-window-handle",
+ "smol",
+ "util",
+ "wgpu",
+]
+
+[[package]]
+name = "gpui_windows"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "collections",
+ "etagere",
+ "futures 0.3.31",
+ "gpui",
+ "image",
+ "itertools 0.14.0",
+ "log",
+ "parking_lot",
+ "rand 0.9.2",
+ "raw-window-handle",
+ "smallvec",
+ "util",
+ "uuid",
+ "windows 0.61.3",
+ "windows-core 0.61.2",
+ "windows-numerics 0.2.0",
+ "windows-registry 0.5.3",
+ "zed-scap",
+]
+
[[package]]
name = "grid"
version = "0.18.0"
@@ -9534,6 +9654,7 @@ dependencies = [
"cpal",
"futures 0.3.31",
"gpui",
+ "gpui_platform",
"gpui_tokio",
"http_client_tls",
"image",
@@ -9786,6 +9907,7 @@ dependencies = [
"fs",
"futures 0.3.31",
"gpui",
+ "gpui_platform",
"language",
"languages",
"linkify",
@@ -12736,6 +12858,7 @@ dependencies = [
"client",
"futures 0.3.31",
"gpui",
+ "gpui_platform",
"http_client",
"language",
"node_runtime",
@@ -13702,6 +13825,7 @@ dependencies = [
"git2",
"git_hosting_providers",
"gpui",
+ "gpui_platform",
"gpui_tokio",
"http_client",
"image",
@@ -15911,6 +16035,7 @@ dependencies = [
"editor",
"fuzzy",
"gpui",
+ "gpui_platform",
"indoc",
"language",
"log",
@@ -20724,7 +20849,7 @@ name = "worktree_benchmarks"
version = "0.1.0"
dependencies = [
"fs",
- "gpui",
+ "gpui_platform",
"settings",
"worktree",
]
@@ -21140,6 +21265,7 @@ dependencies = [
"git_ui",
"go_to_line",
"gpui",
+ "gpui_platform",
"gpui_tokio",
"http_client",
"image",
@@ -83,7 +83,12 @@ members = [
"crates/go_to_line",
"crates/google_ai",
"crates/gpui",
+ "crates/gpui_linux",
+ "crates/gpui_macos",
"crates/gpui_macros",
+ "crates/gpui_platform",
+ "crates/gpui_wgpu",
+ "crates/gpui_windows",
"crates/gpui_tokio",
"crates/html_to_markdown",
"crates/http_client",
@@ -321,7 +326,12 @@ git_ui = { path = "crates/git_ui" }
go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui", default-features = false }
+gpui_linux = { path = "crates/gpui_linux", default-features = false }
+gpui_macos = { path = "crates/gpui_macos", default-features = false }
gpui_macros = { path = "crates/gpui_macros" }
+gpui_platform = { path = "crates/gpui_platform", default-features = false }
+gpui_wgpu = { path = "crates/gpui_wgpu" }
+gpui_windows = { path = "crates/gpui_windows", default-features = false }
gpui_tokio = { path = "crates/gpui_tokio" }
html_to_markdown = { path = "crates/html_to_markdown" }
http_client = { path = "crates/http_client" }
@@ -819,6 +829,8 @@ codegen-units = 16
# (without this cargo will compile ~400 crates twice)
[profile.dev.build-override]
codegen-units = 16
+split-debuginfo = "unpacked"
+debug = true
[profile.dev.package]
# proc-macros start
@@ -13,7 +13,6 @@ path = "src/component_preview.rs"
[features]
default = []
-preview = []
test-support = ["db/test-support"]
[dependencies]
@@ -39,7 +38,9 @@ ui_input.workspace = true
uuid.workspace = true
workspace.workspace = true
+[dev-dependencies]
+gpui_platform = { workspace = true, features = ["screen-capture"] }
+
[[example]]
name = "component_preview"
path = "examples/component_preview.rs"
-required-features = ["preview"]
@@ -1,18 +1,143 @@
//! Component Preview Example
//!
-//! Run with: `cargo run -p component_preview --example component_preview --features="preview"`
-//!
-//! To use this in other projects, add the following to your `Cargo.toml`:
-//!
-//! ```toml
-//! [dependencies]
-//! component_preview = { path = "../component_preview", features = ["preview"] }
-//!
-//! [[example]]
-//! name = "component_preview"
-//! path = "examples/component_preview.rs"
-//! ```
+//! Run with: `cargo run -p component_preview --example component_preview"`
+use fs::RealFs;
+use gpui::{AppContext as _, Bounds, KeyBinding, WindowBounds, WindowOptions, actions, size};
+
+use client::{Client, UserStore};
+use language::LanguageRegistry;
+use node_runtime::NodeRuntime;
+use project::Project;
+use reqwest_client::ReqwestClient;
+use session::{AppSession, Session};
+use std::sync::Arc;
+use ui::{App, px};
+use workspace::{AppState, Workspace, WorkspaceStore};
+
+use component_preview::{ComponentPreview, init};
+
+actions!(zed, [Quit]);
+
+fn quit(_: &Quit, cx: &mut App) {
+ cx.quit();
+}
fn main() {
- component_preview::run_component_preview();
+ gpui_platform::application().run(|cx| {
+ component::init();
+
+ cx.on_action(quit);
+ cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
+ let version = release_channel::AppVersion::load(env!("CARGO_PKG_VERSION"), None, None);
+ release_channel::init(version, cx);
+
+ let http_client =
+ ReqwestClient::user_agent("component_preview").expect("Failed to create HTTP client");
+ cx.set_http_client(Arc::new(http_client));
+
+ let fs = Arc::new(RealFs::new(None, cx.background_executor().clone()));
+ <dyn fs::Fs>::set_global(fs.clone(), cx);
+
+ settings::init(cx);
+ theme::init(theme::LoadThemes::JustBase, cx);
+
+ let languages = Arc::new(LanguageRegistry::new(cx.background_executor().clone()));
+ let client = Client::production(cx);
+ client::init(&client, cx);
+
+ let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
+ let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
+ let session_id = uuid::Uuid::new_v4().to_string();
+ let session = cx.foreground_executor().block_on(Session::new(session_id));
+ let session = cx.new(|cx| AppSession::new(session, cx));
+ let node_runtime = NodeRuntime::unavailable();
+
+ let app_state = Arc::new(AppState {
+ languages,
+ client,
+ user_store,
+ workspace_store,
+ fs,
+ build_window_options: |_, _| Default::default(),
+ node_runtime,
+ session,
+ });
+ AppState::set_global(Arc::downgrade(&app_state), cx);
+
+ workspace::init(app_state.clone(), cx);
+ init(app_state.clone(), cx);
+
+ let size = size(px(1200.), px(800.));
+ let bounds = Bounds::centered(None, size, cx);
+
+ cx.open_window(
+ WindowOptions {
+ window_bounds: Some(WindowBounds::Windowed(bounds)),
+ ..Default::default()
+ },
+ {
+ move |window, cx| {
+ let app_state = app_state;
+ theme::setup_ui_font(window, cx);
+
+ let project = Project::local(
+ app_state.client.clone(),
+ app_state.node_runtime.clone(),
+ app_state.user_store.clone(),
+ app_state.languages.clone(),
+ app_state.fs.clone(),
+ None,
+ project::LocalProjectFlags {
+ init_worktree_trust: false,
+ ..Default::default()
+ },
+ cx,
+ );
+
+ let workspace = cx.new(|cx| {
+ Workspace::new(
+ Default::default(),
+ project.clone(),
+ app_state.clone(),
+ window,
+ cx,
+ )
+ });
+
+ workspace.update(cx, |workspace, cx| {
+ let weak_workspace = cx.entity().downgrade();
+ let language_registry = app_state.languages.clone();
+ let user_store = app_state.user_store.clone();
+
+ let component_preview = cx.new(|cx| {
+ ComponentPreview::new(
+ weak_workspace,
+ project,
+ language_registry,
+ user_store,
+ None,
+ None,
+ window,
+ cx,
+ )
+ .expect("Failed to create component preview")
+ });
+
+ workspace.add_item_to_active_pane(
+ Box::new(component_preview),
+ None,
+ true,
+ window,
+ cx,
+ );
+ });
+
+ workspace
+ }
+ },
+ )
+ .expect("Failed to open component preview window");
+
+ cx.activate(true);
+ });
}
@@ -1,4 +1,3 @@
-mod component_preview_example;
mod persistence;
use client::UserStore;
@@ -20,9 +19,6 @@ use workspace::{
Item, ItemId, SerializableItem, Workspace, WorkspaceId, delete_unloaded_items, item::ItemEvent,
};
-#[allow(unused_imports)]
-pub use component_preview_example::*;
-
pub fn init(app_state: Arc<AppState>, cx: &mut App) {
workspace::register_serializable_item::<ComponentPreview>(cx);
@@ -85,13 +81,13 @@ impl From<SharedString> for PreviewEntry {
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
-enum PreviewPage {
+pub enum PreviewPage {
#[default]
AllComponents,
Component(ComponentId),
}
-struct ComponentPreview {
+pub struct ComponentPreview {
active_page: PreviewPage,
reset_key: usize,
component_list: ListState,
@@ -1,148 +0,0 @@
-/// Run the component preview application.
-///
-/// This initializes the application with minimal required infrastructure
-/// and opens a workspace with the ComponentPreview item.
-#[cfg(feature = "preview")]
-pub fn run_component_preview() {
- use fs::RealFs;
- use gpui::{
- AppContext as _, Application, Bounds, KeyBinding, WindowBounds, WindowOptions, actions,
- size,
- };
-
- use client::{Client, UserStore};
- use language::LanguageRegistry;
- use node_runtime::NodeRuntime;
- use project::Project;
- use reqwest_client::ReqwestClient;
- use session::{AppSession, Session};
- use std::sync::Arc;
- use ui::{App, px};
- use workspace::{AppState, Workspace, WorkspaceStore};
-
- use crate::{ComponentPreview, init};
-
- actions!(zed, [Quit]);
-
- fn quit(_: &Quit, cx: &mut App) {
- cx.quit();
- }
-
- Application::new().run(|cx| {
- component::init();
-
- cx.on_action(quit);
- cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
- let version = release_channel::AppVersion::load(env!("CARGO_PKG_VERSION"), None, None);
- release_channel::init(version, cx);
-
- let http_client =
- ReqwestClient::user_agent("component_preview").expect("Failed to create HTTP client");
- cx.set_http_client(Arc::new(http_client));
-
- let fs = Arc::new(RealFs::new(None, cx.background_executor().clone()));
- <dyn fs::Fs>::set_global(fs.clone(), cx);
-
- settings::init(cx);
- theme::init(theme::LoadThemes::JustBase, cx);
-
- let languages = Arc::new(LanguageRegistry::new(cx.background_executor().clone()));
- let client = Client::production(cx);
- client::init(&client, cx);
-
- let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
- let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
- let session_id = uuid::Uuid::new_v4().to_string();
- let session = cx.foreground_executor().block_on(Session::new(session_id));
- let session = cx.new(|cx| AppSession::new(session, cx));
- let node_runtime = NodeRuntime::unavailable();
-
- let app_state = Arc::new(AppState {
- languages,
- client,
- user_store,
- workspace_store,
- fs,
- build_window_options: |_, _| Default::default(),
- node_runtime,
- session,
- });
- AppState::set_global(Arc::downgrade(&app_state), cx);
-
- workspace::init(app_state.clone(), cx);
- init(app_state.clone(), cx);
-
- let size = size(px(1200.), px(800.));
- let bounds = Bounds::centered(None, size, cx);
-
- cx.open_window(
- WindowOptions {
- window_bounds: Some(WindowBounds::Windowed(bounds)),
- ..Default::default()
- },
- {
- move |window, cx| {
- let app_state = app_state;
- theme::setup_ui_font(window, cx);
-
- let project = Project::local(
- app_state.client.clone(),
- app_state.node_runtime.clone(),
- app_state.user_store.clone(),
- app_state.languages.clone(),
- app_state.fs.clone(),
- None,
- project::LocalProjectFlags {
- init_worktree_trust: false,
- ..Default::default()
- },
- cx,
- );
-
- let workspace = cx.new(|cx| {
- Workspace::new(
- Default::default(),
- project.clone(),
- app_state.clone(),
- window,
- cx,
- )
- });
-
- workspace.update(cx, |workspace, cx| {
- let weak_workspace = cx.entity().downgrade();
- let language_registry = app_state.languages.clone();
- let user_store = app_state.user_store.clone();
-
- let component_preview = cx.new(|cx| {
- ComponentPreview::new(
- weak_workspace,
- project,
- language_registry,
- user_store,
- None,
- None,
- window,
- cx,
- )
- .expect("Failed to create component preview")
- });
-
- workspace.add_item_to_active_pane(
- Box::new(component_preview),
- None,
- true,
- window,
- cx,
- );
- });
-
- workspace
- }
- },
- )
- .expect("Failed to open component preview window");
-
- cx.activate(true);
- });
-}
@@ -27,6 +27,7 @@ extension.workspace = true
fs.workspace = true
futures.workspace = true
gpui.workspace = true
+gpui_platform.workspace = true
gpui_tokio.workspace = true
indoc.workspace = true
language.workspace = true
@@ -31,7 +31,7 @@ use collections::HashSet;
use edit_prediction::EditPredictionStore;
use futures::channel::mpsc;
use futures::{SinkExt as _, StreamExt as _};
-use gpui::{AppContext as _, Application, BackgroundExecutor, Task};
+use gpui::{AppContext as _, BackgroundExecutor, Task};
use zeta_prompt::ZetaFormat;
use reqwest_client::ReqwestClient;
@@ -851,7 +851,7 @@ fn main() {
}
let http_client = Arc::new(ReqwestClient::new());
- let app = Application::headless().with_http_client(http_client);
+ let app = gpui_platform::headless().with_http_client(http_client);
app.run(move |cx| {
let app_state = Arc::new(headless::init(cx));
@@ -38,6 +38,7 @@ extension.workspace = true
fs.workspace = true
futures.workspace = true
gpui.workspace = true
+gpui_platform.workspace = true
gpui_tokio.workspace = true
handlebars.workspace = true
language.workspace = true
@@ -18,7 +18,7 @@ use collections::{HashMap, HashSet};
use extension::ExtensionHostProxy;
use futures::future;
use gpui::http_client::read_proxy_from_env;
-use gpui::{App, AppContext, Application, AsyncApp, Entity, UpdateGlobal};
+use gpui::{App, AppContext, AsyncApp, Entity, UpdateGlobal};
use gpui_tokio::Tokio;
use language::LanguageRegistry;
use language_model::{ConfiguredModel, LanguageModel, LanguageModelRegistry, SelectedModel};
@@ -114,7 +114,7 @@ fn main() {
let languages: HashSet<String> = args.languages.into_iter().collect();
let http_client = Arc::new(ReqwestClient::new());
- let app = Application::headless().with_http_client(http_client);
+ let app = gpui_platform::headless().with_http_client(http_client);
let all_threads = examples::all(&examples_dir);
app.run(move |cx| {
@@ -13,6 +13,6 @@ path = "src/eval_utils.rs"
doctest = false
[dependencies]
-gpui.workspace = true
+gpui_platform.workspace = true
serde.workspace = true
smol.workspace = true
@@ -82,7 +82,7 @@ pub fn eval<P>(
let (tx, rx) = mpsc::channel();
- let executor = gpui::background_executor();
+ let executor = gpui_platform::background_executor();
let semaphore = Arc::new(smol::lock::Semaphore::new(32));
let evalf = Arc::new(evalf);
// Warm the cache once
@@ -19,7 +19,7 @@ cloud_api_types.workspace = true
env_logger.workspace = true
extension.workspace = true
fs.workspace = true
-gpui.workspace = true
+gpui_platform.workspace = true
language.workspace = true
log.workspace = true
reqwest_client.workspace = true
@@ -35,7 +35,7 @@ async fn main() -> Result<()> {
env_logger::init();
let args = Args::parse();
- let fs = Arc::new(RealFs::new(None, gpui::background_executor()));
+ let fs = Arc::new(RealFs::new(None, gpui_platform::background_executor()));
let engine = wasmtime::Engine::default();
let mut wasm_store = WasmStore::new(&engine)?;
@@ -6,7 +6,8 @@ edition.workspace = true
[dependencies]
fs.workspace = true
-gpui = {workspace = true, features = ["windows-manifest"]}
+gpui.workspace = true
+gpui_platform.workspace = true
[lints]
workspace = true
@@ -1,12 +1,14 @@
use fs::Fs;
-use gpui::{AppContext, Application};
+use gpui::AppContext;
+use gpui_platform::headless;
+
fn main() {
let Some(path_to_read) = std::env::args().nth(1) else {
println!("Expected path to read as 1st argument.");
return;
};
- let _ = Application::headless().run(|cx| {
+ let _ = headless().run(|cx| {
let fs = fs::RealFs::new(None, cx.background_executor().clone());
cx.background_spawn(async move {
let timer = std::time::Instant::now();
@@ -31,37 +31,8 @@ leak-detection = ["backtrace"]
runtime_shaders = []
wayland = [
"bitflags",
- "wgpu",
- "bytemuck",
- "ashpd/wayland",
- "cosmic-text",
- "font-kit",
- "calloop-wayland-source",
- "wayland-backend",
- "wayland-client",
- "wayland-cursor",
- "wayland-protocols",
- "wayland-protocols-plasma",
- "wayland-protocols-wlr",
- "filedescriptor",
- "xkbcommon",
- "open",
-]
-x11 = [
- "wgpu",
- "bytemuck",
- "ashpd",
- "cosmic-text",
- "font-kit",
- "as-raw-xcb-connection",
- "x11rb",
- "xkbcommon",
- "xim",
- "x11-clipboard",
- "filedescriptor",
- "open",
- "scap?/x11",
]
+x11 = []
screen-capture = [
"scap",
]
@@ -76,7 +47,7 @@ anyhow.workspace = true
async-task = "4.7"
backtrace = { workspace = true, optional = true }
bitflags = { workspace = true, optional = true }
-bytemuck = { version = "1", optional = true }
+
collections.workspace = true
ctor.workspace = true
derive_more.workspace = true
@@ -107,7 +78,6 @@ usvg = { version = "0.45.0", default-features = false }
util_macros.workspace = true
schemars.workspace = true
seahash = "4.1"
-semver.workspace = true
serde.workspace = true
serde_json.workspace = true
slotmap.workspace = true
@@ -122,7 +92,6 @@ util.workspace = true
uuid.workspace = true
waker-fn = "1.2.0"
lyon = "1.0"
-libc.workspace = true
pin-project = "1.1.10"
circular-buffer.workspace = true
spin = "0.10.0"
@@ -154,76 +123,17 @@ pathfinder_geometry = "0.5"
[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies]
scap = { workspace = true, optional = true }
-[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
-# Always used
-oo7 = { version = "0.5.0", default-features = false, features = [
- "async-std",
- "native_crypto",
-] }
-# Used in both windowing options
-ashpd = { workspace = true, optional = true }
-wgpu = { workspace = true, optional = true }
-cosmic-text = { version = "0.17.0", optional = true }
-swash = { version = "0.2.6" }
-# WARNING: If you change this, you must also publish a new version of zed-font-kit to crates.io
-font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "110523127440aefb11ce0cf280ae7c5071337ec5", package = "zed-font-kit", version = "0.14.1-zed", features = [
- "source-fontconfig-dlopen",
-], optional = true }
-calloop = "0.14.3"
-filedescriptor = { version = "0.8.2", optional = true }
-open = { version = "5.2.0", optional = true }
-xkbcommon = { version = "0.8.0", features = ["wayland", "x11"], optional = true }
-
-# Wayland
-calloop-wayland-source = { version = "0.4.1", optional = true }
-wayland-backend = { version = "0.3.3", features = [
- "client_system",
- "dlopen",
-], optional = true }
-wayland-client = { version = "0.31.11", optional = true }
-wayland-cursor = { version = "0.31.11", optional = true }
-wayland-protocols = { version = "0.32.9", features = [
- "client",
- "staging",
- "unstable",
-], optional = true }
-wayland-protocols-plasma = { version = "0.3.9", features = [
- "client",
-], optional = true }
-wayland-protocols-wlr = { version = "0.3.9", features = [
- "client",
-], optional = true }
-
-# X11
-as-raw-xcb-connection = { version = "1", optional = true }
-x11rb = { version = "0.13.1", features = [
- "allow-unsafe-code",
- "xkb",
- "randr",
- "xinput",
- "cursor",
- "resource_manager",
- "sync",
-], optional = true }
-# WARNING: If you change this, you must also publish a new version of zed-xim to crates.io
-xim = { git = "https://github.com/zed-industries/xim-rs.git", rev = "16f35a2c881b815a2b6cdfd6687988e84f8447d8" , features = [
- "x11rb-xcb",
- "x11rb-client",
-], package = "zed-xim", version = "0.4.0-zed", optional = true }
-x11-clipboard = { version = "0.9.3", optional = true }
[target.'cfg(target_os = "windows")'.dependencies]
-rand.workspace = true
-windows.workspace = true
-windows-core.workspace = true
-windows-numerics = "0.2"
-windows-registry = "0.5"
+windows = { version = "0.61", features = ["Win32_Foundation"] }
+
[dev-dependencies]
backtrace.workspace = true
collections = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
+gpui_platform.workspace = true
http_client = { workspace = true, features = ["test-support"] }
lyon = { version = "1.0", features = ["extra"] }
pretty_assertions.workspace = true
@@ -233,17 +143,17 @@ scheduler = { workspace = true, features = ["test-support"] }
unicode-segmentation.workspace = true
util = { workspace = true, features = ["test-support"] }
+
+
[target.'cfg(target_os = "windows")'.build-dependencies]
embed-resource = "3.0"
-windows-registry = "0.5"
[target.'cfg(target_os = "macos")'.build-dependencies]
bindgen = "0.71"
cbindgen = { version = "0.28.0", default-features = false }
naga.workspace = true
-[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.build-dependencies]
-naga.workspace = true
+
[[example]]
@@ -1,463 +1,20 @@
#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
#![cfg_attr(not(target_os = "macos"), allow(unused))]
-use std::env;
-
fn main() {
- let target = env::var("CARGO_CFG_TARGET_OS");
println!("cargo::rustc-check-cfg=cfg(gles)");
- match target.as_deref() {
- Ok("macos") => {
- #[cfg(target_os = "macos")]
- macos::build();
- }
- Ok("windows") => {
- #[cfg(target_os = "windows")]
- windows::build();
- }
- _ => (),
- };
+ #[cfg(all(target_os = "windows", feature = "windows-manifest"))]
+ embed_resource();
}
-#[cfg(target_os = "macos")]
-mod macos {
- use std::{
- env,
- path::{Path, PathBuf},
- };
-
- use cbindgen::Config;
-
- pub(super) fn build() {
- generate_dispatch_bindings();
-
- let header_path = generate_shader_bindings();
-
- #[cfg(feature = "runtime_shaders")]
- emit_stitched_shaders(&header_path);
- #[cfg(not(feature = "runtime_shaders"))]
- compile_metal_shaders(&header_path);
- }
-
- fn generate_dispatch_bindings() {
- println!("cargo:rustc-link-lib=framework=System");
-
- let bindings = bindgen::Builder::default()
- .header("src/platform/mac/dispatch.h")
- .allowlist_var("_dispatch_main_q")
- .allowlist_var("_dispatch_source_type_data_add")
- .allowlist_var("DISPATCH_QUEUE_PRIORITY_HIGH")
- .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
- .allowlist_var("DISPATCH_QUEUE_PRIORITY_LOW")
- .allowlist_var("DISPATCH_TIME_NOW")
- .allowlist_function("dispatch_get_global_queue")
- .allowlist_function("dispatch_async_f")
- .allowlist_function("dispatch_after_f")
- .allowlist_function("dispatch_time")
- .allowlist_function("dispatch_source_merge_data")
- .allowlist_function("dispatch_source_create")
- .allowlist_function("dispatch_source_set_event_handler_f")
- .allowlist_function("dispatch_resume")
- .allowlist_function("dispatch_suspend")
- .allowlist_function("dispatch_source_cancel")
- .allowlist_function("dispatch_set_context")
- .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
- .layout_tests(false)
- .generate()
- .expect("unable to generate bindings");
-
- let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
- bindings
- .write_to_file(out_path.join("dispatch_sys.rs"))
- .expect("couldn't write dispatch bindings");
- }
-
- fn generate_shader_bindings() -> PathBuf {
- let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
- let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
- let mut config = Config {
- include_guard: Some("SCENE_H".into()),
- language: cbindgen::Language::C,
- no_includes: true,
- ..Default::default()
- };
- config.export.include.extend([
- "Bounds".into(),
- "Corners".into(),
- "Edges".into(),
- "Size".into(),
- "Pixels".into(),
- "PointF".into(),
- "Hsla".into(),
- "ContentMask".into(),
- "Uniforms".into(),
- "AtlasTile".into(),
- "PathRasterizationInputIndex".into(),
- "PathVertex_ScaledPixels".into(),
- "PathRasterizationVertex".into(),
- "ShadowInputIndex".into(),
- "Shadow".into(),
- "QuadInputIndex".into(),
- "Underline".into(),
- "UnderlineInputIndex".into(),
- "Quad".into(),
- "BorderStyle".into(),
- "SpriteInputIndex".into(),
- "MonochromeSprite".into(),
- "PolychromeSprite".into(),
- "PathSprite".into(),
- "SurfaceInputIndex".into(),
- "SurfaceBounds".into(),
- "TransformationMatrix".into(),
- ]);
- config.no_includes = true;
- config.enumeration.prefix_with_name = true;
-
- let mut builder = cbindgen::Builder::new();
-
- let src_paths = [
- crate_dir.join("src/scene.rs"),
- crate_dir.join("src/geometry.rs"),
- crate_dir.join("src/color.rs"),
- crate_dir.join("src/window.rs"),
- crate_dir.join("src/platform.rs"),
- crate_dir.join("src/platform/mac/metal_renderer.rs"),
- ];
- for src_path in src_paths {
- println!("cargo:rerun-if-changed={}", src_path.display());
- builder = builder.with_src(src_path);
- }
-
- builder
- .with_config(config)
- .generate()
- .expect("Unable to generate bindings")
- .write_to_file(&output_path);
-
- output_path
- }
-
- /// To enable runtime compilation, we need to "stitch" the shaders file with the generated header
- /// so that it is self-contained.
- #[cfg(feature = "runtime_shaders")]
- fn emit_stitched_shaders(header_path: &Path) {
- use std::str::FromStr;
- fn stitch_header(header: &Path, shader_path: &Path) -> std::io::Result<PathBuf> {
- let header_contents = std::fs::read_to_string(header)?;
- let shader_contents = std::fs::read_to_string(shader_path)?;
- let stitched_contents = format!("{header_contents}\n{shader_contents}");
- let out_path =
- PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal");
- std::fs::write(&out_path, stitched_contents)?;
- Ok(out_path)
- }
- let shader_source_path = "./src/platform/mac/shaders.metal";
- let shader_path = PathBuf::from_str(shader_source_path).unwrap();
- stitch_header(header_path, &shader_path).unwrap();
- println!("cargo:rerun-if-changed={}", &shader_source_path);
- }
-
- #[cfg(not(feature = "runtime_shaders"))]
- fn compile_metal_shaders(header_path: &Path) {
- use std::process::{self, Command};
- let shader_path = "./src/platform/mac/shaders.metal";
- let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
- let metallib_output_path =
- PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
- println!("cargo:rerun-if-changed={}", shader_path);
-
- let output = Command::new("xcrun")
- .args([
- "-sdk",
- "macosx",
- "metal",
- "-gline-tables-only",
- "-mmacosx-version-min=10.15.7",
- "-MO",
- "-c",
- shader_path,
- "-include",
- (header_path.to_str().unwrap()),
- "-o",
- ])
- .arg(&air_output_path)
- .output()
- .unwrap();
-
- if !output.status.success() {
- println!(
- "cargo::error=metal shader compilation failed:\n{}",
- String::from_utf8_lossy(&output.stderr)
- );
- process::exit(1);
- }
-
- let output = Command::new("xcrun")
- .args(["-sdk", "macosx", "metallib"])
- .arg(air_output_path)
- .arg("-o")
- .arg(metallib_output_path)
- .output()
- .unwrap();
-
- if !output.status.success() {
- println!(
- "cargo::error=metallib compilation failed:\n{}",
- String::from_utf8_lossy(&output.stderr)
- );
- process::exit(1);
- }
- }
-}
-
-#[cfg(target_os = "windows")]
-mod windows {
- use std::{
- ffi::OsString,
- fs,
- io::Write,
- path::{Path, PathBuf},
- process::{self, Command},
- };
-
- pub(super) fn build() {
- // Compile HLSL shaders
- #[cfg(not(debug_assertions))]
- compile_shaders();
-
- // Embed the Windows manifest and resource file
- #[cfg(feature = "windows-manifest")]
- embed_resource();
- }
-
- #[cfg(feature = "windows-manifest")]
- fn embed_resource() {
- let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml");
- let rc_file = std::path::Path::new("resources/windows/gpui.rc");
- println!("cargo:rerun-if-changed={}", manifest.display());
- println!("cargo:rerun-if-changed={}", rc_file.display());
- embed_resource::compile(rc_file, embed_resource::NONE)
- .manifest_required()
- .unwrap();
- }
-
- /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler.
- fn compile_shaders() {
- let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
- .join("src/platform/windows/shaders.hlsl");
- let out_dir = std::env::var("OUT_DIR").unwrap();
-
- println!("cargo:rerun-if-changed={}", shader_path.display());
-
- // Check if fxc.exe is available
- let fxc_path = find_fxc_compiler();
-
- // Define all modules
- let modules = [
- "quad",
- "shadow",
- "path_rasterization",
- "path_sprite",
- "underline",
- "monochrome_sprite",
- "subpixel_sprite",
- "polychrome_sprite",
- ];
-
- let rust_binding_path = format!("{}/shaders_bytes.rs", out_dir);
- if Path::new(&rust_binding_path).exists() {
- fs::remove_file(&rust_binding_path)
- .expect("Failed to remove existing Rust binding file");
- }
- for module in modules {
- compile_shader_for_module(
- module,
- &out_dir,
- &fxc_path,
- shader_path.to_str().unwrap(),
- &rust_binding_path,
- );
- }
-
- {
- let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
- .join("src/platform/windows/color_text_raster.hlsl");
- compile_shader_for_module(
- "emoji_rasterization",
- &out_dir,
- &fxc_path,
- shader_path.to_str().unwrap(),
- &rust_binding_path,
- );
- }
- }
-
- /// Locate `binary` in the newest installed Windows SDK.
- pub fn find_latest_windows_sdk_binary(
- binary: &str,
- ) -> Result<Option<PathBuf>, Box<dyn std::error::Error>> {
- let key = windows_registry::LOCAL_MACHINE
- .open("SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0")?;
-
- let install_folder: String = key.get_string("InstallationFolder")?; // "C:\Program Files (x86)\Windows Kits\10\"
- let install_folder_bin = Path::new(&install_folder).join("bin");
-
- let mut versions: Vec<_> = std::fs::read_dir(&install_folder_bin)?
- .flatten()
- .filter(|entry| entry.path().is_dir())
- .filter_map(|entry| entry.file_name().into_string().ok())
- .collect();
-
- versions.sort_by_key(|s| {
- s.split('.')
- .filter_map(|p| p.parse().ok())
- .collect::<Vec<u32>>()
- });
-
- let arch = match std::env::consts::ARCH {
- "x86_64" => "x64",
- "aarch64" => "arm64",
- _ => Err(format!(
- "Unsupported architecture: {}",
- std::env::consts::ARCH
- ))?,
- };
-
- if let Some(highest_version) = versions.last() {
- return Ok(Some(
- install_folder_bin
- .join(highest_version)
- .join(arch)
- .join(binary),
- ));
- }
-
- Ok(None)
- }
-
- /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler.
- fn find_fxc_compiler() -> String {
- // Check environment variable
- if let Ok(path) = std::env::var("GPUI_FXC_PATH")
- && Path::new(&path).exists()
- {
- return path;
- }
-
- // Try to find in PATH
- // NOTE: This has to be `where.exe` on Windows, not `where`, it must be ended with `.exe`
- if let Ok(output) = std::process::Command::new("where.exe")
- .arg("fxc.exe")
- .output()
- && output.status.success()
- {
- let path = String::from_utf8_lossy(&output.stdout);
- return path.trim().to_string();
- }
-
- if let Ok(Some(path)) = find_latest_windows_sdk_binary("fxc.exe") {
- return path.to_string_lossy().into_owned();
- }
-
- panic!("Failed to find fxc.exe");
- }
-
- fn compile_shader_for_module(
- module: &str,
- out_dir: &str,
- fxc_path: &str,
- shader_path: &str,
- rust_binding_path: &str,
- ) {
- // Compile vertex shader
- let output_file = format!("{}/{}_vs.h", out_dir, module);
- let const_name = format!("{}_VERTEX_BYTES", module.to_uppercase());
- compile_shader_impl(
- fxc_path,
- &format!("{module}_vertex"),
- &output_file,
- &const_name,
- shader_path,
- "vs_4_1",
- );
- generate_rust_binding(&const_name, &output_file, rust_binding_path);
-
- // Compile fragment shader
- let output_file = format!("{}/{}_ps.h", out_dir, module);
- let const_name = format!("{}_FRAGMENT_BYTES", module.to_uppercase());
- compile_shader_impl(
- fxc_path,
- &format!("{module}_fragment"),
- &output_file,
- &const_name,
- shader_path,
- "ps_4_1",
- );
- generate_rust_binding(&const_name, &output_file, rust_binding_path);
- }
-
- fn compile_shader_impl(
- fxc_path: &str,
- entry_point: &str,
- output_path: &str,
- var_name: &str,
- shader_path: &str,
- target: &str,
- ) {
- let output = Command::new(fxc_path)
- .args([
- "/T",
- target,
- "/E",
- entry_point,
- "/Fh",
- output_path,
- "/Vn",
- var_name,
- "/O3",
- shader_path,
- ])
- .output();
-
- match output {
- Ok(result) => {
- if result.status.success() {
- return;
- }
- println!(
- "cargo::error=Shader compilation failed for {}:\n{}",
- entry_point,
- String::from_utf8_lossy(&result.stderr)
- );
- process::exit(1);
- }
- Err(e) => {
- println!("cargo::error=Failed to run fxc for {}: {}", entry_point, e);
- process::exit(1);
- }
- }
- }
- fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) {
- let header_content = fs::read_to_string(head_file).expect("Failed to read header file");
- let const_definition = {
- let global_var_start = header_content.find("const BYTE").unwrap();
- let global_var = &header_content[global_var_start..];
- let equal = global_var.find('=').unwrap();
- global_var[equal + 1..].trim()
- };
- let rust_binding = format!(
- "const {}: &[u8] = &{}\n",
- const_name,
- const_definition.replace('{', "[").replace('}', "]")
- );
- let mut options = fs::OpenOptions::new()
- .create(true)
- .append(true)
- .open(output_path)
- .expect("Failed to open Rust binding file");
- options
- .write_all(rust_binding.as_bytes())
- .expect("Failed to write Rust binding file");
- }
+#[cfg(all(target_os = "windows", feature = "windows-manifest"))]
+fn embed_resource() {
+ let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml");
+ let rc_file = std::path::Path::new("resources/windows/gpui.rc");
+ println!("cargo:rerun-if-changed={}", manifest.display());
+ println!("cargo:rerun-if-changed={}", rc_file.display());
+ embed_resource::compile(rc_file, embed_resource::NONE)
+ .manifest_required()
+ .unwrap();
}
@@ -2,10 +2,11 @@ use std::time::Duration;
use anyhow::Result;
use gpui::{
- Animation, AnimationExt as _, App, Application, AssetSource, Bounds, Context, SharedString,
- Transformation, Window, WindowBounds, WindowOptions, bounce, div, ease_in_out, percentage,
- prelude::*, px, size, svg,
+ Animation, AnimationExt as _, App, AssetSource, Bounds, Context, SharedString, Transformation,
+ Window, WindowBounds, WindowOptions, bounce, div, ease_in_out, percentage, prelude::*, px,
+ size, svg,
};
+use gpui_platform::application;
struct Assets {}
@@ -101,21 +102,19 @@ impl Render for AnimationExample {
}
fn main() {
- Application::new()
- .with_assets(Assets {})
- .run(|cx: &mut App| {
- let options = WindowOptions {
- window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
- None,
- size(px(300.), px(300.)),
- cx,
- ))),
- ..Default::default()
- };
- cx.open_window(options, |_, cx| {
- cx.activate(false);
- cx.new(|_| AnimationExample {})
- })
- .unwrap();
- });
+ application().with_assets(Assets {}).run(|cx: &mut App| {
+ let options = WindowOptions {
+ window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
+ None,
+ size(px(300.), px(300.)),
+ cx,
+ ))),
+ ..Default::default()
+ };
+ cx.open_window(options, |_, cx| {
+ cx.activate(false);
+ cx.new(|_| AnimationExample {})
+ })
+ .unwrap();
+ });
}
@@ -1,10 +1,11 @@
use std::{ops::Range, rc::Rc, time::Duration};
use gpui::{
- App, Application, Bounds, Context, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point,
- Render, SharedString, UniformListScrollHandle, Window, WindowBounds, WindowOptions, canvas,
- div, point, prelude::*, px, rgb, size, uniform_list,
+ App, Bounds, Context, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Point, Render,
+ SharedString, UniformListScrollHandle, Window, WindowBounds, WindowOptions, canvas, div, point,
+ prelude::*, px, rgb, size, uniform_list,
};
+use gpui_platform::application;
const TOTAL_ITEMS: usize = 10000;
const SCROLLBAR_THUMB_WIDTH: Pixels = px(8.);
@@ -447,7 +448,7 @@ impl Render for DataTable {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.open_window(
WindowOptions {
focus: true,
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, Half, Hsla, Pixels, Point, Window, WindowBounds,
- WindowOptions, div, prelude::*, px, rgb, size,
+ App, Bounds, Context, Half, Hsla, Pixels, Point, Window, WindowBounds, WindowOptions, div,
+ prelude::*, px, rgb, size,
};
+use gpui_platform::application;
#[derive(Clone, Copy)]
struct DragInfo {
@@ -121,7 +122,7 @@ impl Render for DragDrop {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx);
cx.open_window(
WindowOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, Div, ElementId, FocusHandle, KeyBinding, SharedString,
- Stateful, Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, size,
+ App, Bounds, Context, Div, ElementId, FocusHandle, KeyBinding, SharedString, Stateful, Window,
+ WindowBounds, WindowOptions, actions, div, prelude::*, px, size,
};
+use gpui_platform::application;
actions!(example, [Tab, TabPrev, Quit]);
@@ -192,7 +193,7 @@ impl Render for Example {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.bind_keys([
KeyBinding::new("tab", Tab, None),
KeyBinding::new("shift-tab", TabPrev, None),
@@ -1,4 +1,5 @@
-use gpui::{App, Application, Context, Render, Window, WindowOptions, div, img, prelude::*};
+use gpui::{App, Context, Render, Window, WindowOptions, div, img, prelude::*};
+use gpui_platform::application;
use std::path::PathBuf;
struct GifViewer {
@@ -24,7 +25,7 @@ impl Render for GifViewer {
fn main() {
env_logger::init();
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let gif_path =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples/image/black-cat-typing.gif");
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, ColorSpace, Context, Half, Render, Window, WindowOptions, canvas,
- div, linear_color_stop, linear_gradient, point, prelude::*, px, size,
+ App, Bounds, ColorSpace, Context, Half, Render, Window, WindowOptions, canvas, div,
+ linear_color_stop, linear_gradient, point, prelude::*, px, size,
};
+use gpui_platform::application;
struct GradientViewer {
color_space: ColorSpace,
@@ -243,7 +244,7 @@ impl Render for GradientViewer {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.open_window(
WindowOptions {
focus: true,
@@ -1,7 +1,7 @@
use gpui::{
- App, Application, Bounds, Context, Hsla, Window, WindowBounds, WindowOptions, div, prelude::*,
- px, rgb, size,
+ App, Bounds, Context, Hsla, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
// https://en.wikipedia.org/wiki/Holy_grail_(web_design)
struct HolyGrailExample {}
@@ -65,7 +65,7 @@ impl Render for HolyGrailExample {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
cx.open_window(
WindowOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div,
- prelude::*, px, rgb, size,
+ App, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div, prelude::*, px,
+ rgb, size,
};
+use gpui_platform::application;
struct HelloWorld {
text: SharedString,
@@ -87,7 +88,7 @@ impl Render for HelloWorld {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
cx.open_window(
WindowOptions {
@@ -4,10 +4,11 @@ use std::sync::Arc;
use anyhow::Result;
use gpui::{
- App, AppContext, Application, AssetSource, Bounds, Context, ImageSource, KeyBinding, Menu,
- MenuItem, Point, SharedString, SharedUri, TitlebarOptions, Window, WindowBounds, WindowOptions,
- actions, div, img, prelude::*, px, rgb, size,
+ App, AppContext, AssetSource, Bounds, Context, ImageSource, KeyBinding, Menu, MenuItem, Point,
+ SharedString, SharedUri, TitlebarOptions, Window, WindowBounds, WindowOptions, actions, div,
+ img, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
use reqwest_client::ReqwestClient;
struct Assets {
@@ -150,7 +151,7 @@ fn main() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
- Application::new()
+ application()
.with_assets(Assets {
base: manifest_dir.join("examples"),
})
@@ -1,10 +1,11 @@
use futures::FutureExt;
use gpui::{
- App, AppContext, Application, Asset as _, AssetLogger, Bounds, ClickEvent, Context, ElementId,
- Entity, ImageAssetLoader, ImageCache, ImageCacheProvider, KeyBinding, Menu, MenuItem,
+ App, AppContext, Asset as _, AssetLogger, Bounds, ClickEvent, Context, ElementId, Entity,
+ ImageAssetLoader, ImageCache, ImageCacheProvider, KeyBinding, Menu, MenuItem,
RetainAllImageCache, SharedString, TitlebarOptions, Window, WindowBounds, WindowOptions,
actions, div, hash, image_cache, img, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
use reqwest_client::ReqwestClient;
use std::{collections::HashMap, sync::Arc};
@@ -247,7 +248,7 @@ actions!(image, [Quit]);
fn main() {
env_logger::init();
- Application::new().run(move |cx: &mut App| {
+ application().run(move |cx: &mut App| {
let http_client = ReqwestClient::user_agent("gpui example").unwrap();
cx.set_http_client(Arc::new(http_client));
@@ -1,11 +1,12 @@
use std::{path::Path, sync::Arc, time::Duration};
use gpui::{
- Animation, AnimationExt, App, Application, Asset, AssetLogger, AssetSource, Bounds, Context,
- Hsla, ImageAssetLoader, ImageCacheError, ImgResourceLoader, LOADING_DELAY, Length, RenderImage,
+ Animation, AnimationExt, App, Asset, AssetLogger, AssetSource, Bounds, Context, Hsla,
+ ImageAssetLoader, ImageCacheError, ImgResourceLoader, LOADING_DELAY, Length, RenderImage,
Resource, SharedString, Window, WindowBounds, WindowOptions, black, div, img, prelude::*,
pulsating_between, px, red, size,
};
+use gpui_platform::application;
struct Assets {}
@@ -193,21 +194,19 @@ impl Render for ImageLoadingExample {
fn main() {
env_logger::init();
- Application::new()
- .with_assets(Assets {})
- .run(|cx: &mut App| {
- let options = WindowOptions {
- window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
- None,
- size(px(300.), px(300.)),
- cx,
- ))),
- ..Default::default()
- };
- cx.open_window(options, |_, cx| {
- cx.activate(false);
- cx.new(|_| ImageLoadingExample {})
- })
- .unwrap();
- });
+ application().with_assets(Assets {}).run(|cx: &mut App| {
+ let options = WindowOptions {
+ window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
+ None,
+ size(px(300.), px(300.)),
+ cx,
+ ))),
+ ..Default::default()
+ };
+ cx.open_window(options, |_, cx| {
+ cx.activate(false);
+ cx.new(|_| ImageLoadingExample {})
+ })
+ .unwrap();
+ });
}
@@ -1,13 +1,14 @@
use std::ops::Range;
use gpui::{
- App, Application, Bounds, ClipboardItem, Context, CursorStyle, ElementId, ElementInputHandler,
- Entity, EntityInputHandler, FocusHandle, Focusable, GlobalElementId, KeyBinding, Keystroke,
- LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
+ App, Bounds, ClipboardItem, Context, CursorStyle, ElementId, ElementInputHandler, Entity,
+ EntityInputHandler, FocusHandle, Focusable, GlobalElementId, KeyBinding, Keystroke, LayoutId,
+ MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
ShapedLine, SharedString, Style, TextRun, UTF16Selection, UnderlineStyle, Window, WindowBounds,
WindowOptions, actions, black, div, fill, hsla, opaque_grey, point, prelude::*, px, relative,
rgb, rgba, size, white, yellow,
};
+use gpui_platform::application;
use unicode_segmentation::*;
actions!(
@@ -682,7 +683,7 @@ impl Render for InputExample {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
cx.bind_keys([
KeyBinding::new("backspace", Backspace, None),
@@ -11,10 +11,10 @@ mod example {
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use gpui::{
- App, Application, Bounds, Context, FontWeight, Size, Window, WindowBackgroundAppearance,
- WindowBounds, WindowKind, WindowOptions, div, layer_shell::*, point, prelude::*, px, rems,
- rgba, white,
+ App, Bounds, Context, FontWeight, Size, Window, WindowBackgroundAppearance, WindowBounds,
+ WindowKind, WindowOptions, div, layer_shell::*, point, prelude::*, px, rems, rgba, white,
};
+ use gpui_platform::application;
struct LayerShellExample;
@@ -60,7 +60,7 @@ mod example {
}
pub fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.open_window(
WindowOptions {
titlebar: None,
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, MousePressureEvent, PressureStage, Window, WindowBounds,
- WindowOptions, div, prelude::*, px, rgb, size,
+ App, Bounds, Context, MousePressureEvent, PressureStage, Window, WindowBounds, WindowOptions,
+ div, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
struct MousePressureExample {
pressure_stage: PressureStage,
@@ -44,7 +45,7 @@ impl MousePressureExample {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
cx.open_window(
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, FocusHandle, KeyBinding, Window, WindowBounds,
- WindowOptions, actions, div, prelude::*, px, rgb, size,
+ App, Bounds, Context, FocusHandle, KeyBinding, Window, WindowBounds, WindowOptions, actions,
+ div, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
actions!(example, [CloseWindow]);
@@ -35,7 +36,7 @@ impl Render for ExampleWindow {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let mut bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
cx.bind_keys([KeyBinding::new("cmd-w", CloseWindow, None)]);
@@ -2,9 +2,10 @@ use std::{fs, path::PathBuf};
use anyhow::Result;
use gpui::{
- App, Application, AssetSource, Bounds, BoxShadow, ClickEvent, Context, SharedString, Task,
- Window, WindowBounds, WindowOptions, div, hsla, img, point, prelude::*, px, rgb, size, svg,
+ App, AssetSource, Bounds, BoxShadow, ClickEvent, Context, SharedString, Task, Window,
+ WindowBounds, WindowOptions, div, hsla, img, point, prelude::*, px, rgb, size, svg,
};
+use gpui_platform::application;
struct Assets {
base: PathBuf,
@@ -156,7 +157,7 @@ impl Render for HelloWorld {
}
fn main() {
- Application::new()
+ application()
.with_assets(Assets {
base: PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"),
})
@@ -1,4 +1,5 @@
-use gpui::{App, Application, Context, Entity, EventEmitter, prelude::*};
+use gpui::{App, Context, Entity, EventEmitter, prelude::*};
+use gpui_platform::application;
struct Counter {
count: usize,
@@ -11,7 +12,7 @@ struct Change {
impl EventEmitter<Change> for Counter {}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
let subscriber = cx.new(|cx: &mut Context<Counter>| {
cx.subscribe(&counter, |subscriber, _emitter, event, _cx| {
@@ -1,8 +1,9 @@
use gpui::{
- Application, Background, Bounds, ColorSpace, Context, MouseDownEvent, Path, PathBuilder,
- PathStyle, Pixels, Point, Render, StrokeOptions, Window, WindowOptions, canvas, div,
- linear_color_stop, linear_gradient, point, prelude::*, px, quad, rgb, size,
+ Background, Bounds, ColorSpace, Context, MouseDownEvent, Path, PathBuilder, PathStyle, Pixels,
+ Point, Render, StrokeOptions, Window, WindowOptions, canvas, div, linear_color_stop,
+ linear_gradient, point, prelude::*, px, quad, rgb, size,
};
+use gpui_platform::application;
struct PaintingViewer {
default_lines: Vec<(Path<Pixels>, Background)>,
@@ -445,7 +446,7 @@ impl Render for PaintingViewer {
}
fn main() {
- Application::new().run(|cx| {
+ application().run(|cx| {
cx.open_window(
WindowOptions {
focus: true,
@@ -1,8 +1,9 @@
use gpui::{
- Application, Background, Bounds, ColorSpace, Context, Path, PathBuilder, Pixels, Render,
- TitlebarOptions, Window, WindowBounds, WindowOptions, canvas, div, linear_color_stop,
- linear_gradient, point, prelude::*, px, rgb, size,
+ Background, Bounds, ColorSpace, Context, Path, PathBuilder, Pixels, Render, TitlebarOptions,
+ Window, WindowBounds, WindowOptions, canvas, div, linear_color_stop, linear_gradient, point,
+ prelude::*, px, rgb, size,
};
+use gpui_platform::application;
const DEFAULT_WINDOW_WIDTH: Pixels = px(1024.0);
const DEFAULT_WINDOW_HEIGHT: Pixels = px(768.0);
@@ -69,7 +70,7 @@ impl Render for PaintingViewer {
}
fn main() {
- Application::new().run(|cx| {
+ application().run(|cx| {
cx.open_window(
WindowOptions {
titlebar: Some(TitlebarOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, AppContext, Application, Bounds, Context, Window, WindowBounds, WindowOptions, div,
- linear_color_stop, linear_gradient, pattern_slash, prelude::*, px, rgb, size,
+ App, AppContext, Bounds, Context, Window, WindowBounds, WindowOptions, div, linear_color_stop,
+ linear_gradient, pattern_slash, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
struct PatternExample;
@@ -99,7 +100,7 @@ impl Render for PatternExample {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
cx.open_window(
WindowOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Context, Corner, Div, Hsla, Stateful, Window, WindowOptions, anchored,
- deferred, div, prelude::*, px,
+ App, Context, Corner, Div, Hsla, Stateful, Window, WindowOptions, anchored, deferred, div,
+ prelude::*, px,
};
+use gpui_platform::application;
/// An example show use deferred to create a floating layers.
struct HelloWorld {
@@ -161,7 +162,7 @@ impl Render for HelloWorld {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.open_window(WindowOptions::default(), |_, cx| {
cx.new(|_| HelloWorld {
open: false,
@@ -1,7 +1,5 @@
-use gpui::{
- App, Application, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px,
- size,
-};
+use gpui::{App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, size};
+use gpui_platform::application;
struct Scrollable {}
@@ -45,7 +43,7 @@ impl Render for Scrollable {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
cx.open_window(
WindowOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Context, Global, Menu, MenuItem, SharedString, SystemMenuType, Window,
- WindowOptions, actions, div, prelude::*, rgb,
+ App, Context, Global, Menu, MenuItem, SharedString, SystemMenuType, Window, WindowOptions,
+ actions, div, prelude::*, rgb,
};
+use gpui_platform::application;
struct SetMenus;
@@ -20,7 +21,7 @@ impl Render for SetMenus {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.set_global(AppState::new());
// Bring the menu bar to the foreground (so you can see the menu bar)
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, BoxShadow, Context, Div, SharedString, Window, WindowBounds,
- WindowOptions, div, hsla, point, prelude::*, px, relative, rgb, size,
+ App, Bounds, BoxShadow, Context, Div, SharedString, Window, WindowBounds, WindowOptions, div,
+ hsla, point, prelude::*, px, relative, rgb, size,
};
+use gpui_platform::application;
struct Shadow {}
@@ -569,7 +570,7 @@ impl Render for Shadow {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(1000.0), px(800.0)), cx);
cx.open_window(
WindowOptions {
@@ -3,9 +3,10 @@ use std::path::PathBuf;
use anyhow::Result;
use gpui::{
- App, Application, AssetSource, Bounds, Context, SharedString, Window, WindowBounds,
- WindowOptions, div, prelude::*, px, rgb, size, svg,
+ App, AssetSource, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div,
+ prelude::*, px, rgb, size, svg,
};
+use gpui_platform::application;
struct Assets {
base: PathBuf,
@@ -68,7 +69,7 @@ impl Render for SvgExample {
}
fn main() {
- Application::new()
+ application()
.with_assets(Assets {
base: PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("examples"),
})
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, Div, ElementId, FocusHandle, KeyBinding, SharedString,
- Stateful, Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, size,
+ App, Bounds, Context, Div, ElementId, FocusHandle, KeyBinding, SharedString, Stateful, Window,
+ WindowBounds, WindowOptions, actions, div, prelude::*, px, size,
};
+use gpui_platform::application;
actions!(example, [Tab, TabPrev]);
@@ -178,7 +179,7 @@ impl Render for Example {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.bind_keys([
KeyBinding::new("tab", Tab, None),
KeyBinding::new("shift-tab", TabPrev, None),
@@ -7,9 +7,10 @@
//! Run tests: cargo test -p gpui --example testing --features test-support
use gpui::{
- App, Application, Bounds, Context, FocusHandle, Focusable, Render, Task, Window, WindowBounds,
+ App, Bounds, Context, FocusHandle, Focusable, Render, Task, Window, WindowBounds,
WindowOptions, actions, div, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
actions!(counter, [Increment, Decrement]);
@@ -176,7 +177,7 @@ impl Render for Counter {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.bind_keys([
gpui::KeyBinding::new("up", Increment, Some("Counter")),
gpui::KeyBinding::new("down", Decrement, Some("Counter")),
@@ -4,10 +4,11 @@ use std::{
};
use gpui::{
- AbsoluteLength, App, Application, Context, DefiniteLength, ElementId, Global, Hsla, Menu,
- SharedString, TextStyle, TitlebarOptions, Window, WindowBounds, WindowOptions, bounds,
- colors::DefaultColors, div, point, prelude::*, px, relative, rgb, size,
+ AbsoluteLength, App, Context, DefiniteLength, ElementId, Global, Hsla, Menu, SharedString,
+ TextStyle, TitlebarOptions, Window, WindowBounds, WindowOptions, bounds, colors::DefaultColors,
+ div, point, prelude::*, px, relative, rgb, size,
};
+use gpui_platform::application;
use std::iter;
#[derive(Clone, Debug)]
@@ -298,7 +299,7 @@ impl Render for TextExample {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
cx.set_menus(vec![Menu {
name: "GPUI Typography".into(),
items: vec![],
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, FontStyle, FontWeight, StyledText, Window, WindowBounds,
- WindowOptions, div, prelude::*, px, size,
+ App, Bounds, Context, FontStyle, FontWeight, StyledText, Window, WindowBounds, WindowOptions,
+ div, prelude::*, px, size,
};
+use gpui_platform::application;
struct HelloWorld {}
@@ -81,7 +82,7 @@ impl Render for HelloWorld {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx);
cx.open_window(
WindowOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, TextOverflow, Window, WindowBounds, WindowOptions, div,
- prelude::*, px, size,
+ App, Bounds, Context, TextOverflow, Window, WindowBounds, WindowOptions, div, prelude::*, px,
+ size,
};
+use gpui_platform::application;
struct HelloWorld {}
@@ -108,7 +109,7 @@ impl Render for HelloWorld {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx);
cx.open_window(
WindowOptions {
@@ -2,10 +2,8 @@
//! handle deep hierarchies (even though it cannot just yet!).
use std::sync::LazyLock;
-use gpui::{
- App, Application, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px,
- size,
-};
+use gpui::{App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, size};
+use gpui_platform::application;
struct Tree {}
@@ -32,7 +30,7 @@ impl Render for Tree {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
cx.open_window(
WindowOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px,
- rgb, size, uniform_list,
+ App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size,
+ uniform_list,
};
+use gpui_platform::application;
struct UniformListExample {}
@@ -36,7 +37,7 @@ impl Render for UniformListExample {
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(300.0), px(300.0)), cx);
cx.open_window(
WindowOptions {
@@ -1,7 +1,8 @@
use gpui::{
- App, Application, Bounds, Context, KeyBinding, PromptButton, PromptLevel, Window, WindowBounds,
- WindowKind, WindowOptions, actions, div, prelude::*, px, rgb, size,
+ App, Bounds, Context, KeyBinding, PromptButton, PromptLevel, Window, WindowBounds, WindowKind,
+ WindowOptions, actions, div, prelude::*, px, rgb, size,
};
+use gpui_platform::application;
struct SubWindow {
custom_titlebar: bool,
@@ -306,7 +307,7 @@ impl Render for WindowDemo {
actions!(window, [Quit]);
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(800.0), px(600.0)), cx);
cx.open_window(
@@ -1,8 +1,9 @@
use gpui::{
- App, Application, Bounds, Context, DisplayId, Hsla, Pixels, SharedString, Size, Window,
+ App, Bounds, Context, DisplayId, Hsla, Pixels, SharedString, Size, Window,
WindowBackgroundAppearance, WindowBounds, WindowKind, WindowOptions, div, point, prelude::*,
px, rgb,
};
+use gpui_platform::application;
struct WindowContent {
text: SharedString,
@@ -68,7 +69,7 @@ fn build_window_options(display_id: DisplayId, bounds: Bounds<Pixels>) -> Window
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
// Create several new windows, positioned in the top right corner of each screen
let size = Size {
width: px(350.),
@@ -1,9 +1,10 @@
use gpui::{
- App, Application, Bounds, Context, CursorStyle, Decorations, HitboxBehavior, Hsla, MouseButton,
- Pixels, Point, ResizeEdge, Size, Window, WindowBackgroundAppearance, WindowBounds,
- WindowDecorations, WindowOptions, black, canvas, div, green, point, prelude::*, px, rgb, size,
- transparent_black, white,
+ App, Bounds, Context, CursorStyle, Decorations, HitboxBehavior, Hsla, MouseButton, Pixels,
+ Point, ResizeEdge, Size, Window, WindowBackgroundAppearance, WindowBounds, WindowDecorations,
+ WindowOptions, black, canvas, div, green, point, prelude::*, px, rgb, size, transparent_black,
+ white,
};
+use gpui_platform::application;
struct WindowShadow {}
@@ -203,7 +204,7 @@ fn resize_edge(pos: Point<Pixels>, shadow_size: Pixels, size: Size<Pixels>) -> O
}
fn main() {
- Application::new().run(|cx: &mut App| {
+ application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(600.0), px(600.0)), cx);
cx.open_window(
WindowOptions {
@@ -7,7 +7,7 @@
//! # struct Counter {
//! # count: usize,
//! # }
-//! Application::new().run(|cx: &mut App| {
+//! gpui_platform::application().run(|cx: &mut App| {
//! let _counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
//! // ...
//! });
@@ -22,7 +22,7 @@
//! # struct Counter {
//! # count: usize,
//! # }
-//! Application::new().run(|cx: &mut App| {
+//! gpui_platform::application().run(|cx: &mut App| {
//! let counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
//! // Call `update` to access the model's state.
//! counter.update(cx, |counter: &mut Counter, _cx: &mut Context<Counter>| {
@@ -42,7 +42,7 @@
//! # struct Counter {
//! # count: usize,
//! # }
-//! Application::new().run(|cx: &mut App| {
+//! gpui_platform::application().run(|cx: &mut App| {
//! let counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
//! counter.update(cx, |counter, cx| {
//! counter.count += 1;
@@ -60,7 +60,7 @@
//! # struct Counter {
//! # count: usize,
//! # }
-//! Application::new().run(|cx: &mut App| {
+//! gpui_platform::application().run(|cx: &mut App| {
//! let first_counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
//!
//! let second_counter = cx.new(|cx: &mut Context<Counter>| {
@@ -114,7 +114,7 @@
//! # increment: usize,
//! # }
//! # impl EventEmitter<CounterChangeEvent> for Counter {}
-//! Application::new().run(|cx: &mut App| {
+//! gpui_platform::application().run(|cx: &mut App| {
//! let first_counter: Entity<Counter> = cx.new(|_cx| Counter { count: 0 });
//!
//! let second_counter = cx.new(|cx: &mut Context<Counter>| {
@@ -46,7 +46,7 @@ use crate::{
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextRenderingMode, TextSystem,
ThermalState, Window, WindowAppearance, WindowHandle, WindowId, WindowInvalidator,
colors::{Colors, GlobalColors},
- current_platform, hash, init_app_menus,
+ hash, init_app_menus,
};
mod async_context;
@@ -132,25 +132,10 @@ pub struct Application(Rc<AppCell>);
/// Represents an application before it is fully launched. Once your app is
/// configured, you'll start the app with `App::run`.
impl Application {
- /// Builds an app with the given asset source.
- #[allow(clippy::new_without_default)]
- pub fn new() -> Self {
- #[cfg(any(test, feature = "test-support"))]
- log::info!("GPUI was compiled in test mode");
-
- Self(App::new_app(
- current_platform(false),
- Arc::new(()),
- Arc::new(NullHttpClient),
- ))
- }
-
- /// Build an app in headless mode. This prevents opening windows,
- /// but makes it possible to run an application in an context like
- /// SSH, where GUI applications are not allowed.
- pub fn headless() -> Self {
+ /// Builds an app with a caller-provided platform implementation.
+ pub fn with_platform(platform: Rc<dyn Platform>) -> Self {
Self(App::new_app(
- current_platform(true),
+ platform,
Arc::new(()),
Arc::new(NullHttpClient),
))
@@ -42,15 +42,18 @@ impl VisualTestAppContext {
///
/// Note: This uses a no-op asset source, so SVG icons won't render.
/// Use `with_asset_source` to provide real assets for icon rendering.
- pub fn new() -> Self {
- Self::with_asset_source(Arc::new(()))
+ pub fn new(platform: Rc<dyn Platform>) -> Self {
+ Self::with_asset_source(platform, Arc::new(()))
}
/// Creates a new `VisualTestAppContext` with a custom asset source.
///
/// Use this when you need SVG icons to render properly in visual tests.
/// Pass the real `Assets` struct to enable icon rendering.
- pub fn with_asset_source(asset_source: Arc<dyn AssetSource>) -> Self {
+ pub fn with_asset_source(
+ platform: Rc<dyn Platform>,
+ asset_source: Arc<dyn AssetSource>,
+ ) -> Self {
// Use a seeded RNG for deterministic behavior
let seed = std::env::var("SEED")
.ok()
@@ -59,7 +62,7 @@ impl VisualTestAppContext {
// Create a visual test platform that combines real Mac rendering
// with controllable TestDispatcher for deterministic task scheduling
- let platform = Rc::new(VisualTestPlatform::new(seed));
+ let platform = Rc::new(VisualTestPlatform::new(platform, seed));
// Get the dispatcher and executors from the platform
let dispatcher = platform.dispatcher().clone();
@@ -391,12 +394,6 @@ impl VisualTestAppContext {
}
}
-impl Default for VisualTestAppContext {
- fn default() -> Self {
- Self::new()
- }
-}
-
impl AppContext for VisualTestAppContext {
fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> {
let mut app = self.app.borrow_mut();
@@ -476,112 +473,3 @@ impl AppContext for VisualTestAppContext {
callback(app.global::<G>(), &app)
}
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::Empty;
- use std::cell::RefCell;
-
- // Note: All VisualTestAppContext tests are ignored by default because they require
- // the macOS main thread. Standard Rust tests run on worker threads, which causes
- // SIGABRT when interacting with macOS AppKit/Cocoa APIs.
- //
- // To run these tests, use:
- // cargo test -p gpui visual_test_context -- --ignored --test-threads=1
-
- #[test]
- #[ignore] // Requires macOS main thread
- fn test_foreground_tasks_run_with_run_until_parked() {
- let mut cx = VisualTestAppContext::new();
-
- let task_ran = Rc::new(RefCell::new(false));
-
- // Spawn a foreground task via the App's spawn method
- // This should use our TestDispatcher, not the MacDispatcher
- {
- let task_ran = task_ran.clone();
- cx.update(|cx| {
- cx.spawn(async move |_| {
- *task_ran.borrow_mut() = true;
- })
- .detach();
- });
- }
-
- // The task should not have run yet
- assert!(!*task_ran.borrow());
-
- // Run until parked should execute the foreground task
- cx.run_until_parked();
-
- // Now the task should have run
- assert!(*task_ran.borrow());
- }
-
- #[test]
- #[ignore] // Requires macOS main thread
- fn test_advance_clock_triggers_delayed_tasks() {
- let mut cx = VisualTestAppContext::new();
-
- let task_ran = Rc::new(RefCell::new(false));
-
- // Spawn a task that waits for a timer
- {
- let task_ran = task_ran.clone();
- let executor = cx.background_executor.clone();
- cx.update(|cx| {
- cx.spawn(async move |_| {
- executor.timer(Duration::from_millis(500)).await;
- *task_ran.borrow_mut() = true;
- })
- .detach();
- });
- }
-
- // Run until parked - the task should be waiting on the timer
- cx.run_until_parked();
- assert!(!*task_ran.borrow());
-
- // Advance clock past the timer duration
- cx.advance_clock(Duration::from_millis(600));
-
- // Now the task should have completed
- assert!(*task_ran.borrow());
- }
-
- #[test]
- #[ignore] // Requires macOS main thread - window creation fails on test threads
- fn test_window_spawn_uses_test_dispatcher() {
- let mut cx = VisualTestAppContext::new();
-
- let task_ran = Rc::new(RefCell::new(false));
-
- let window = cx
- .open_offscreen_window_default(|_, cx| cx.new(|_| Empty))
- .expect("Failed to open window");
-
- // Spawn a task via window.spawn - this is the critical test case
- // for tooltip behavior, as tooltips use window.spawn for delayed show
- {
- let task_ran = task_ran.clone();
- cx.update_window(window.into(), |_, window, cx| {
- window
- .spawn(cx, async move |_| {
- *task_ran.borrow_mut() = true;
- })
- .detach();
- })
- .ok();
- }
-
- // The task should not have run yet
- assert!(!*task_ran.borrow());
-
- // Run until parked should execute the foreground task spawned via window
- cx.run_until_parked();
-
- // Now the task should have run
- assert!(*task_ran.borrow());
- }
-}
@@ -33,9 +33,10 @@ impl AssetSource for () {
pub struct ImageId(pub usize);
#[derive(PartialEq, Eq, Hash, Clone)]
-pub(crate) struct RenderImageParams {
- pub(crate) image_id: ImageId,
- pub(crate) frame_index: usize,
+#[expect(missing_docs)]
+pub struct RenderImageParams {
+ pub image_id: ImageId,
+ pub frame_index: usize,
}
/// A cached and processed image, in BGRA format
@@ -23,7 +23,7 @@ pub fn rgba(hex: u32) -> Rgba {
}
/// Swap from RGBA with premultiplied alpha to BGRA
-pub(crate) fn swap_rgba_pa_to_bgra(color: &mut [u8]) {
+pub fn swap_rgba_pa_to_bgra(color: &mut [u8]) {
color.swap(0, 2);
if color[3] > 0 {
let a = color[3] as f32 / 255.;
@@ -1592,7 +1592,7 @@ impl<T: Clone + Debug + Default + PartialEq + Display + Add<T, Output = T>> Disp
impl Size<DevicePixels> {
/// Converts the size from physical to logical pixels.
- pub(crate) fn to_pixels(self, scale_factor: f32) -> Size<Pixels> {
+ pub fn to_pixels(self, scale_factor: f32) -> Size<Pixels> {
size(
px(self.width.0 as f32 / scale_factor),
px(self.height.0 as f32 / scale_factor),
@@ -1602,7 +1602,7 @@ impl Size<DevicePixels> {
impl Size<Pixels> {
/// Converts the size from logical to physical pixels.
- pub(crate) fn to_device_pixels(self, scale_factor: f32) -> Size<DevicePixels> {
+ pub fn to_device_pixels(self, scale_factor: f32) -> Size<DevicePixels> {
size(
DevicePixels((self.width.0 * scale_factor).round() as i32),
DevicePixels((self.height.0 * scale_factor).round() as i32),
@@ -2683,6 +2683,11 @@ impl Pixels {
/// The minimum value that can be represented by `Pixels`.
pub const MIN: Pixels = Pixels(f32::MIN);
+ /// Returns the raw `f32` value of this `Pixels`.
+ pub fn as_f32(self) -> f32 {
+ self.0
+ }
+
/// Floors the `Pixels` value to the nearest whole number.
///
/// # Returns
@@ -2964,9 +2969,14 @@ impl From<usize> for DevicePixels {
/// display resolutions.
#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, DivAssign, PartialEq)]
#[repr(transparent)]
-pub struct ScaledPixels(pub(crate) f32);
+pub struct ScaledPixels(pub f32);
impl ScaledPixels {
+ /// Returns the raw `f32` value of this `ScaledPixels`.
+ pub fn as_f32(self) -> f32 {
+ self.0
+ }
+
/// Floors the `ScaledPixels` value to the nearest whole number.
///
/// # Returns
@@ -5,7 +5,8 @@
#![allow(unused_mut)] // False positives in platform specific code
extern crate self as gpui;
-
+#[doc(hidden)]
+pub static GPUI_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR");
#[macro_use]
mod action;
mod app;
@@ -32,9 +33,11 @@ mod keymap;
mod path_builder;
mod platform;
pub mod prelude;
-mod profiler;
+/// Profiling utilities for task timing and thread performance tracking.
+pub mod profiler;
#[cfg(any(target_os = "windows", target_os = "linux"))]
-mod queue;
+#[expect(missing_docs)]
+pub mod queue;
mod scene;
mod shared_string;
mod shared_uri;
@@ -94,7 +97,7 @@ pub use path_builder::*;
pub use platform::*;
pub use profiler::*;
#[cfg(any(target_os = "windows", target_os = "linux"))]
-pub(crate) use queue::{PriorityQueueReceiver, PriorityQueueSender};
+pub use queue::{PriorityQueueReceiver, PriorityQueueSender};
pub use refineable::*;
pub use scene::*;
pub use shared_string::*;
@@ -557,7 +557,7 @@ impl Deref for MouseExitEvent {
/// A collection of paths from the platform, such as from a file drop.
#[derive(Debug, Clone, Default, Eq, PartialEq)]
-pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
+pub struct ExternalPaths(pub SmallVec<[PathBuf; 2]>);
impl ExternalPaths {
/// Convert this collection of paths into a slice.
@@ -262,7 +262,7 @@ impl KeyBindingContextPredicate {
/// Eval a predicate against a set of contexts, arranged from lowest to highest.
#[allow(unused)]
- pub(crate) fn eval(&self, contexts: &[KeyContext]) -> bool {
+ pub fn eval(&self, contexts: &[KeyContext]) -> bool {
self.eval_inner(contexts, contexts)
}
@@ -2,17 +2,9 @@ mod app_menu;
mod keyboard;
mod keystroke;
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
-mod linux;
-
-#[cfg(target_os = "macos")]
-mod mac;
-
-#[cfg(all(
- any(target_os = "linux", target_os = "freebsd"),
- any(feature = "wayland", feature = "x11")
-))]
-mod wgpu;
+#[cfg(all(target_os = "linux", feature = "wayland"))]
+#[expect(missing_docs)]
+pub mod layer_shell;
#[cfg(any(test, feature = "test-support"))]
mod test;
@@ -20,14 +12,21 @@ mod test;
#[cfg(all(target_os = "macos", any(test, feature = "test-support")))]
mod visual_test;
-#[cfg(target_os = "windows")]
-mod windows;
-
#[cfg(all(
feature = "screen-capture",
any(target_os = "windows", target_os = "linux", target_os = "freebsd",)
))]
-pub(crate) mod scap_screen_capture;
+pub mod scap_screen_capture;
+
+#[cfg(all(
+ any(target_os = "windows", target_os = "linux"),
+ feature = "screen-capture"
+))]
+pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame;
+#[cfg(not(feature = "screen-capture"))]
+pub(crate) type PlatformScreenCaptureFrame = ();
+#[cfg(all(target_os = "macos", feature = "screen-capture"))]
+pub(crate) type PlatformScreenCaptureFrame = core_video::image_buffer::CVImageBuffer;
use crate::{
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
@@ -69,17 +68,8 @@ pub use app_menu::*;
pub use keyboard::*;
pub use keystroke::*;
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
-pub(crate) use linux::*;
-#[cfg(target_os = "macos")]
-pub(crate) use mac::*;
#[cfg(any(test, feature = "test-support"))]
pub(crate) use test::*;
-#[cfg(target_os = "windows")]
-pub(crate) use windows::*;
-
-#[cfg(all(target_os = "linux", feature = "wayland"))]
-pub use linux::layer_shell;
#[cfg(any(test, feature = "test-support"))]
pub use test::{TestDispatcher, TestScreenCaptureSource, TestScreenCaptureStream};
@@ -87,52 +77,8 @@ pub use test::{TestDispatcher, TestScreenCaptureSource, TestScreenCaptureStream}
#[cfg(all(target_os = "macos", any(test, feature = "test-support")))]
pub use visual_test::VisualTestPlatform;
-/// Returns a background executor for the current platform.
-pub fn background_executor() -> BackgroundExecutor {
- current_platform(true).background_executor()
-}
-
-#[cfg(target_os = "macos")]
-pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
- Rc::new(MacPlatform::new(headless))
-}
-
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
-pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
- #[cfg(feature = "x11")]
- use anyhow::Context as _;
-
- if headless {
- return Rc::new(HeadlessClient::new());
- }
-
- match guess_compositor() {
- #[cfg(feature = "wayland")]
- "Wayland" => Rc::new(WaylandClient::new()),
-
- #[cfg(feature = "x11")]
- "X11" => Rc::new(
- X11Client::new()
- .context("Failed to initialize X11 client.")
- .unwrap(),
- ),
-
- "Headless" => Rc::new(HeadlessClient::new()),
- _ => unreachable!(),
- }
-}
-
-#[cfg(target_os = "windows")]
-pub(crate) fn current_platform(headless: bool) -> Rc<dyn Platform> {
- Rc::new(
- WindowsPlatform::new(headless)
- .inspect_err(|err| show_error("Failed to launch", err.to_string()))
- .unwrap(),
- )
-}
-
/// Return which compositor we're guessing we'll use.
-/// Does not attempt to connect to the given compositor
+/// Does not attempt to connect to the given compositor.
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[inline]
pub fn guess_compositor() -> &'static str {
@@ -162,7 +108,8 @@ pub fn guess_compositor() -> &'static str {
}
}
-pub(crate) trait Platform: 'static {
+#[expect(missing_docs)]
+pub trait Platform: 'static {
fn background_executor(&self) -> BackgroundExecutor;
fn foreground_executor(&self) -> ForegroundExecutor;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
@@ -182,16 +129,10 @@ pub(crate) trait Platform: 'static {
None
}
- #[cfg(feature = "screen-capture")]
- fn is_screen_capture_supported(&self) -> bool;
- #[cfg(not(feature = "screen-capture"))]
fn is_screen_capture_supported(&self) -> bool {
false
}
- #[cfg(feature = "screen-capture")]
- fn screen_capture_sources(&self)
- -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>>;
- #[cfg(not(feature = "screen-capture"))]
+
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<anyhow::Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
@@ -370,6 +311,19 @@ pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame);
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct DisplayId(pub(crate) u32);
+impl DisplayId {
+ /// Create a new `DisplayId` from a raw platform display identifier.
+ pub fn new(id: u32) -> Self {
+ Self(id)
+ }
+}
+
+impl From<u32> for DisplayId {
+ fn from(id: u32) -> Self {
+ Self(id)
+ }
+}
+
impl From<DisplayId> for u32 {
fn from(id: DisplayId) -> Self {
id.0
@@ -482,13 +436,16 @@ impl Tiling {
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
-pub(crate) struct RequestFrameOptions {
- pub(crate) require_presentation: bool,
- /// Force refresh of all rendering states when true
- pub(crate) force_render: bool,
+#[expect(missing_docs)]
+pub struct RequestFrameOptions {
+ /// Whether a presentation is required.
+ pub require_presentation: bool,
+ /// Force refresh of all rendering states when true.
+ pub force_render: bool,
}
-pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
+#[expect(missing_docs)]
+pub trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn bounds(&self) -> Bounds<Pixels>;
fn is_maximized(&self) -> bool;
fn window_bounds(&self) -> WindowBounds;
@@ -558,7 +515,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn set_tabbing_identifier(&self, _identifier: Option<String>) {}
#[cfg(target_os = "windows")]
- fn get_raw_handle(&self) -> windows::HWND;
+ fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND;
// Linux specific methods
fn inner_window_bounds(&self) -> WindowBounds {
@@ -603,17 +560,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
pub type RunnableVariant = Runnable<RunnableMeta>;
#[doc(hidden)]
-pub struct TimerResolutionGuard {
- cleanup: Option<Box<dyn FnOnce() + Send>>,
-}
-
-impl Drop for TimerResolutionGuard {
- fn drop(&mut self) {
- if let Some(cleanup) = self.cleanup.take() {
- cleanup();
- }
- }
-}
+pub type TimerResolutionGuard = util::Deferred<Box<dyn FnOnce() + Send>>;
/// This type is public so that our test macro can generate and use it, but it should not
/// be considered part of our public API.
@@ -632,7 +579,7 @@ pub trait PlatformDispatcher: Send + Sync {
}
fn increase_timer_resolution(&self) -> TimerResolutionGuard {
- TimerResolutionGuard { cleanup: None }
+ util::defer(Box::new(|| {}))
}
#[cfg(any(test, feature = "test-support"))]
@@ -641,7 +588,8 @@ pub trait PlatformDispatcher: Send + Sync {
}
}
-pub(crate) trait PlatformTextSystem: Send + Sync {
+#[expect(missing_docs)]
+pub trait PlatformTextSystem: Send + Sync {
fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()>;
fn all_font_names(&self) -> Vec<String>;
fn font_id(&self, descriptor: &Font) -> Result<FontId>;
@@ -660,8 +608,10 @@ pub(crate) trait PlatformTextSystem: Send + Sync {
-> TextRenderingMode;
}
-pub(crate) struct NoopTextSystem;
+#[expect(missing_docs)]
+pub struct NoopTextSystem;
+#[expect(missing_docs)]
impl NoopTextSystem {
#[allow(dead_code)]
pub fn new() -> Self {
@@ -791,8 +741,9 @@ impl PlatformTextSystem for NoopTextSystem {
// Adapted from https://github.com/microsoft/terminal/blob/1283c0f5b99a2961673249fa77c6b986efb5086c/src/renderer/atlas/dwrite.cpp
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
+/// Compute gamma correction ratios for subpixel text rendering.
#[allow(dead_code)]
-pub(crate) fn get_gamma_correction_ratios(gamma: f32) -> [f32; 4] {
+pub fn get_gamma_correction_ratios(gamma: f32) -> [f32; 4] {
const GAMMA_INCORRECT_TARGET_RATIOS: [[f32; 4]; 13] = [
[0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0], // gamma = 1.0
[0.0166 / 4.0, -0.0807 / 4.0, 0.2227 / 4.0, -0.0751 / 4.0], // gamma = 1.1
@@ -824,7 +775,8 @@ pub(crate) fn get_gamma_correction_ratios(gamma: f32) -> [f32; 4] {
}
#[derive(PartialEq, Eq, Hash, Clone)]
-pub(crate) enum AtlasKey {
+#[expect(missing_docs)]
+pub enum AtlasKey {
Glyph(RenderGlyphParams),
Svg(RenderSvgParams),
Image(RenderImageParams),
@@ -838,7 +790,8 @@ impl AtlasKey {
),
allow(dead_code)
)]
- pub(crate) fn texture_kind(&self) -> AtlasTextureKind {
+ /// Returns the texture kind for this atlas key.
+ pub fn texture_kind(&self) -> AtlasTextureKind {
match self {
AtlasKey::Glyph(params) => {
if params.is_emoji {
@@ -873,7 +826,8 @@ impl From<RenderImageParams> for AtlasKey {
}
}
-pub(crate) trait PlatformAtlas: Send + Sync {
+#[expect(missing_docs)]
+pub trait PlatformAtlas: Send + Sync {
fn get_or_insert_with<'a>(
&self,
key: &AtlasKey,
@@ -882,9 +836,10 @@ pub(crate) trait PlatformAtlas: Send + Sync {
fn remove(&self, key: &AtlasKey);
}
-struct AtlasTextureList<T> {
- textures: Vec<Option<T>>,
- free_list: Vec<usize>,
+#[doc(hidden)]
+pub struct AtlasTextureList<T> {
+ pub textures: Vec<Option<T>>,
+ pub free_list: Vec<usize>,
}
impl<T> Default for AtlasTextureList<T> {
@@ -906,32 +861,40 @@ impl<T> ops::Index<usize> for AtlasTextureList<T> {
impl<T> AtlasTextureList<T> {
#[allow(unused)]
- fn drain(&mut self) -> std::vec::Drain<'_, Option<T>> {
+ pub fn drain(&mut self) -> std::vec::Drain<'_, Option<T>> {
self.free_list.clear();
self.textures.drain(..)
}
#[allow(dead_code)]
- fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> {
+ pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> {
self.textures.iter_mut().flatten()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[repr(C)]
-pub(crate) struct AtlasTile {
- pub(crate) texture_id: AtlasTextureId,
- pub(crate) tile_id: TileId,
- pub(crate) padding: u32,
- pub(crate) bounds: Bounds<DevicePixels>,
+#[expect(missing_docs)]
+pub struct AtlasTile {
+ /// The texture this tile belongs to.
+ pub texture_id: AtlasTextureId,
+ /// The unique ID of this tile within its texture.
+ pub tile_id: TileId,
+ /// Padding around the tile content in pixels.
+ pub padding: u32,
+ /// The bounds of this tile within the texture.
+ pub bounds: Bounds<DevicePixels>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
-pub(crate) struct AtlasTextureId {
+#[expect(missing_docs)]
+pub struct AtlasTextureId {
// We use u32 instead of usize for Metal Shader Language compatibility
- pub(crate) index: u32,
- pub(crate) kind: AtlasTextureKind,
+ /// The index of this texture in the atlas.
+ pub index: u32,
+ /// The kind of content stored in this texture.
+ pub kind: AtlasTextureKind,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -943,7 +906,8 @@ pub(crate) struct AtlasTextureId {
),
allow(dead_code)
)]
-pub(crate) enum AtlasTextureKind {
+#[expect(missing_docs)]
+pub enum AtlasTextureKind {
Monochrome = 0,
Polychrome = 1,
Subpixel = 2,
@@ -951,7 +915,8 @@ pub(crate) enum AtlasTextureKind {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(C)]
-pub(crate) struct TileId(pub(crate) u32);
+#[expect(missing_docs)]
+pub struct TileId(pub u32);
impl From<etagere::AllocId> for TileId {
fn from(id: etagere::AllocId) -> Self {
@@ -965,11 +930,13 @@ impl From<TileId> for etagere::AllocId {
}
}
-pub(crate) struct PlatformInputHandler {
+#[expect(missing_docs)]
+pub struct PlatformInputHandler {
cx: AsyncWindowContext,
handler: Box<dyn InputHandler>,
}
+#[expect(missing_docs)]
#[cfg_attr(
all(
any(target_os = "linux", target_os = "freebsd"),
@@ -982,7 +949,7 @@ impl PlatformInputHandler {
Self { cx, handler }
}
- fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<UTF16Selection> {
+ pub fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<UTF16Selection> {
self.cx
.update(|window, cx| {
self.handler
@@ -993,7 +960,7 @@ impl PlatformInputHandler {
}
#[cfg_attr(target_os = "windows", allow(dead_code))]
- fn marked_text_range(&mut self) -> Option<Range<usize>> {
+ pub fn marked_text_range(&mut self) -> Option<Range<usize>> {
self.cx
.update(|window, cx| self.handler.marked_text_range(window, cx))
.ok()
@@ -1004,7 +971,7 @@ impl PlatformInputHandler {
any(target_os = "linux", target_os = "freebsd", target_os = "windows"),
allow(dead_code)
)]
- fn text_for_range(
+ pub fn text_for_range(
&mut self,
range_utf16: Range<usize>,
adjusted: &mut Option<Range<usize>>,
@@ -1018,7 +985,7 @@ impl PlatformInputHandler {
.flatten()
}
- fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
+ pub fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
self.cx
.update(|window, cx| {
self.handler
@@ -1047,13 +1014,13 @@ impl PlatformInputHandler {
}
#[cfg_attr(target_os = "windows", allow(dead_code))]
- fn unmark_text(&mut self) {
+ pub fn unmark_text(&mut self) {
self.cx
.update(|window, cx| self.handler.unmark_text(window, cx))
.ok();
}
- fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
+ pub fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
self.cx
.update(|window, cx| self.handler.bounds_for_range(range_utf16, window, cx))
.ok()
@@ -1061,11 +1028,11 @@ impl PlatformInputHandler {
}
#[allow(dead_code)]
- fn apple_press_and_hold_enabled(&mut self) -> bool {
+ pub fn apple_press_and_hold_enabled(&mut self) -> bool {
self.handler.apple_press_and_hold_enabled()
}
- pub(crate) fn dispatch_input(&mut self, input: &str, window: &mut Window, cx: &mut App) {
+ pub fn dispatch_input(&mut self, input: &str, window: &mut Window, cx: &mut App) {
self.handler.replace_text_in_range(None, input, window, cx);
}
@@ -1091,7 +1058,7 @@ impl PlatformInputHandler {
}
#[allow(dead_code)]
- pub(crate) fn accepts_text_input(&mut self, window: &mut Window, cx: &mut App) -> bool {
+ pub fn accepts_text_input(&mut self, window: &mut Window, cx: &mut App) -> bool {
self.handler.accepts_text_input(window, cx)
}
}
@@ -1268,7 +1235,8 @@ pub struct WindowOptions {
),
allow(dead_code)
)]
-pub(crate) struct WindowParams {
+#[expect(missing_docs)]
+pub struct WindowParams {
pub bounds: Bounds<Pixels>,
/// The titlebar configuration of the window
@@ -1523,8 +1491,9 @@ impl PromptButton {
PromptButton::Cancel(label.into())
}
+ /// Returns true if this button is a cancel button.
#[allow(dead_code)]
- pub(crate) fn is_cancel(&self) -> bool {
+ pub fn is_cancel(&self) -> bool {
matches!(self, PromptButton::Cancel(_))
}
@@ -1642,7 +1611,8 @@ pub enum CursorStyle {
/// A clipboard item that should be copied to the clipboard
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ClipboardItem {
- entries: Vec<ClipboardEntry>,
+ /// The entries in this clipboard item.
+ pub entries: Vec<ClipboardEntry>,
}
/// Either a ClipboardString or a ClipboardImage
@@ -1842,7 +1812,7 @@ pub struct Image {
/// The raw image bytes
pub bytes: Vec<u8>,
/// The unique ID for the image
- id: u64,
+ pub id: u64,
}
impl Hash for Image {
@@ -1960,8 +1930,10 @@ impl Image {
/// A clipboard item that should be copied to the clipboard
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ClipboardString {
- pub(crate) text: String,
- pub(crate) metadata: Option<String>,
+ /// The text content.
+ pub text: String,
+ /// Optional metadata associated with this clipboard string.
+ pub metadata: Option<String>,
}
impl ClipboardString {
@@ -2001,7 +1973,8 @@ impl ClipboardString {
}
#[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
- pub(crate) fn text_hash(text: &str) -> u64 {
+ /// Compute a hash of the given text for clipboard change detection.
+ pub fn text_hash(text: &str) -> u64 {
let mut hasher = SeaHasher::new();
text.hash(&mut hasher);
hasher.finish()
@@ -265,7 +265,8 @@ impl Keystroke {
impl KeybindingKeystroke {
#[cfg(target_os = "windows")]
- pub(crate) fn new(inner: Keystroke, display_modifiers: Modifiers, display_key: String) -> Self {
+ #[expect(missing_docs)]
+ pub fn new(inner: Keystroke, display_modifiers: Modifiers, display_key: String) -> Self {
KeybindingKeystroke {
inner,
display_modifiers,
@@ -1,6 +1,5 @@
use bitflags::bitflags;
use thiserror::Error;
-use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
use crate::Pixels;
@@ -22,17 +21,6 @@ pub enum Layer {
Overlay,
}
-impl From<Layer> for zwlr_layer_shell_v1::Layer {
- fn from(layer: Layer) -> Self {
- match layer {
- Layer::Background => Self::Background,
- Layer::Bottom => Self::Bottom,
- Layer::Top => Self::Top,
- Layer::Overlay => Self::Overlay,
- }
- }
-}
-
bitflags! {
/// Screen anchor point for layer_shell surfaces. These can be used in any combination, e.g.
/// specifying `Anchor::LEFT | Anchor::RIGHT` will stretch the surface across the width of the
@@ -50,12 +38,6 @@ bitflags! {
}
}
-impl From<Anchor> for zwlr_layer_surface_v1::Anchor {
- fn from(anchor: Anchor) -> Self {
- Self::from_bits_truncate(anchor.bits())
- }
-}
-
/// Keyboard interactivity mode for the layer_shell surfaces.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum KeyboardInteractivity {
@@ -72,16 +54,6 @@ pub enum KeyboardInteractivity {
OnDemand,
}
-impl From<KeyboardInteractivity> for zwlr_layer_surface_v1::KeyboardInteractivity {
- fn from(value: KeyboardInteractivity) -> Self {
- match value {
- KeyboardInteractivity::None => Self::None,
- KeyboardInteractivity::Exclusive => Self::Exclusive,
- KeyboardInteractivity::OnDemand => Self::OnDemand,
- }
- }
-}
-
/// Options for creating a layer_shell window.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LayerShellOptions {
@@ -1,32 +0,0 @@
-mod dispatcher;
-mod headless;
-mod keyboard;
-mod platform;
-#[cfg(any(feature = "wayland", feature = "x11"))]
-mod text_system;
-#[cfg(feature = "wayland")]
-mod wayland;
-#[cfg(feature = "x11")]
-mod x11;
-
-#[cfg(any(feature = "wayland", feature = "x11"))]
-mod xdg_desktop_portal;
-
-pub(crate) use dispatcher::*;
-pub(crate) use headless::*;
-pub(crate) use keyboard::*;
-pub(crate) use platform::*;
-#[cfg(any(feature = "wayland", feature = "x11"))]
-pub(crate) use text_system::*;
-#[cfg(feature = "wayland")]
-pub(crate) use wayland::*;
-#[cfg(feature = "x11")]
-pub(crate) use x11::*;
-
-#[cfg(all(feature = "screen-capture", any(feature = "wayland", feature = "x11")))]
-pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame;
-#[cfg(not(all(feature = "screen-capture", any(feature = "wayland", feature = "x11"))))]
-pub(crate) type PlatformScreenCaptureFrame = ();
-
-#[cfg(feature = "wayland")]
-pub use wayland::layer_shell;
@@ -1,49 +0,0 @@
-mod client;
-mod clipboard;
-mod cursor;
-mod display;
-mod serial;
-mod window;
-
-/// Contains Types for configuring layer_shell surfaces.
-pub mod layer_shell;
-
-pub(crate) use client::*;
-
-use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
-
-use crate::CursorStyle;
-
-impl CursorStyle {
- pub(super) fn to_shape(self) -> Shape {
- match self {
- CursorStyle::Arrow => Shape::Default,
- CursorStyle::IBeam => Shape::Text,
- CursorStyle::Crosshair => Shape::Crosshair,
- CursorStyle::ClosedHand => Shape::Grabbing,
- CursorStyle::OpenHand => Shape::Grab,
- CursorStyle::PointingHand => Shape::Pointer,
- CursorStyle::ResizeLeft => Shape::WResize,
- CursorStyle::ResizeRight => Shape::EResize,
- CursorStyle::ResizeLeftRight => Shape::EwResize,
- CursorStyle::ResizeUp => Shape::NResize,
- CursorStyle::ResizeDown => Shape::SResize,
- CursorStyle::ResizeUpDown => Shape::NsResize,
- CursorStyle::ResizeUpLeftDownRight => Shape::NwseResize,
- CursorStyle::ResizeUpRightDownLeft => Shape::NeswResize,
- CursorStyle::ResizeColumn => Shape::ColResize,
- CursorStyle::ResizeRow => Shape::RowResize,
- CursorStyle::IBeamCursorForVerticalLayout => Shape::VerticalText,
- CursorStyle::OperationNotAllowed => Shape::NotAllowed,
- CursorStyle::DragLink => Shape::Alias,
- CursorStyle::DragCopy => Shape::Copy,
- CursorStyle::ContextualMenu => Shape::ContextMenu,
- CursorStyle::None => {
- #[cfg(debug_assertions)]
- panic!("CursorStyle::None should be handled separately in the client");
- #[cfg(not(debug_assertions))]
- Shape::Default
- }
- }
- }
-}
@@ -1,387 +0,0 @@
-use crate::{
- Scene,
- geometry::{
- rect::RectF,
- vector::{Vector2F, vec2f},
- },
- platform::{
- self, Event, FontSystem, WindowBounds,
- mac::{platform::NSViewLayerContentsRedrawDuringViewResize, renderer::Renderer},
- },
-};
-use cocoa::{
- appkit::{NSScreen, NSSquareStatusItemLength, NSStatusBar, NSStatusItem, NSView, NSWindow},
- base::{YES, id, nil},
- foundation::{NSPoint, NSRect, NSSize},
-};
-use ctor::ctor;
-use foreign_types::ForeignTypeRef;
-use objc::{
- class,
- declare::ClassDecl,
- msg_send,
- rc::StrongPtr,
- runtime::{Class, Object, Protocol, Sel},
- sel, sel_impl,
-};
-use std::{
- cell::RefCell,
- ffi::c_void,
- ptr,
- rc::{Rc, Weak},
- sync::Arc,
-};
-
-use super::screen::Screen;
-
-static mut VIEW_CLASS: *const Class = ptr::null();
-const STATE_IVAR: &str = "state";
-
-#[ctor]
-unsafe fn build_classes() {
- VIEW_CLASS = {
- let mut decl = ClassDecl::new("GPUIStatusItemView", class!(NSView)).unwrap();
- decl.add_ivar::<*mut c_void>(STATE_IVAR);
-
- decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
-
- decl.add_method(
- sel!(mouseDown:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(mouseUp:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(rightMouseDown:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(rightMouseUp:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(otherMouseDown:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(otherMouseUp:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(mouseMoved:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(mouseDragged:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(scrollWheel:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(flagsChanged:),
- handle_view_event as extern "C" fn(&Object, Sel, id),
- );
- decl.add_method(
- sel!(makeBackingLayer),
- make_backing_layer as extern "C" fn(&Object, Sel) -> id,
- );
- decl.add_method(
- sel!(viewDidChangeEffectiveAppearance),
- view_did_change_effective_appearance as extern "C" fn(&Object, Sel),
- );
-
- decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
- decl.add_method(
- sel!(displayLayer:),
- display_layer as extern "C" fn(&Object, Sel, id),
- );
-
- decl.register()
- };
-}
-
-pub struct StatusItem(Rc<RefCell<StatusItemState>>);
-
-struct StatusItemState {
- native_item: StrongPtr,
- native_view: StrongPtr,
- renderer: Renderer,
- scene: Option<Scene>,
- event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
- appearance_changed_callback: Option<Box<dyn FnMut()>>,
-}
-
-impl StatusItem {
- pub fn add(fonts: Arc<dyn FontSystem>) -> Self {
- unsafe {
- let renderer = Renderer::new(false, fonts);
- let status_bar = NSStatusBar::systemStatusBar(nil);
- let native_item =
- StrongPtr::retain(status_bar.statusItemWithLength_(NSSquareStatusItemLength));
-
- let button = native_item.button();
- let _: () = msg_send![button, setHidden: YES];
-
- let native_view = msg_send![VIEW_CLASS, alloc];
- let state = Rc::new(RefCell::new(StatusItemState {
- native_item,
- native_view: StrongPtr::new(native_view),
- renderer,
- scene: None,
- event_callback: None,
- appearance_changed_callback: None,
- }));
-
- let parent_view = button.superview().superview();
- NSView::initWithFrame_(
- native_view,
- NSRect::new(NSPoint::new(0., 0.), NSView::frame(parent_view).size),
- );
- (*native_view).set_ivar(
- STATE_IVAR,
- Weak::into_raw(Rc::downgrade(&state)) as *const c_void,
- );
- native_view.setWantsBestResolutionOpenGLSurface_(YES);
- native_view.setWantsLayer(YES);
- let _: () = msg_send![
- native_view,
- setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
- ];
-
- parent_view.addSubview_(native_view);
-
- {
- let state = state.borrow();
- let layer = state.renderer.layer();
- let scale_factor = state.scale_factor();
- let size = state.content_size() * scale_factor;
- layer.set_contents_scale(scale_factor.into());
- layer.set_drawable_size(metal::CGSize::new(size.x().into(), size.y().into()));
- }
-
- Self(state)
- }
- }
-}
-
-impl platform::Window for StatusItem {
- fn bounds(&self) -> WindowBounds {
- self.0.borrow().bounds()
- }
-
- fn content_size(&self) -> Vector2F {
- self.0.borrow().content_size()
- }
-
- fn scale_factor(&self) -> f32 {
- self.0.borrow().scale_factor()
- }
-
- fn appearance(&self) -> platform::Appearance {
- unsafe {
- let appearance: id =
- msg_send![self.0.borrow().native_item.button(), effectiveAppearance];
- platform::Appearance::from_native(appearance)
- }
- }
-
- fn screen(&self) -> Rc<dyn platform::Screen> {
- unsafe {
- Rc::new(Screen {
- native_screen: self.0.borrow().native_window().screen(),
- })
- }
- }
-
- fn mouse_position(&self) -> Vector2F {
- unimplemented!()
- }
-
- fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
- self
- }
-
- fn set_input_handler(&mut self, _: Box<dyn platform::InputHandler>) {}
-
- fn prompt(
- &self,
- _: crate::platform::PromptLevel,
- _: &str,
- _: &[&str],
- ) -> postage::oneshot::Receiver<usize> {
- unimplemented!()
- }
-
- fn activate(&self) {
- unimplemented!()
- }
-
- fn set_title(&mut self, _: &str) {
- unimplemented!()
- }
-
- fn set_edited(&mut self, _: bool) {
- unimplemented!()
- }
-
- fn show_character_palette(&self) {
- unimplemented!()
- }
-
- fn minimize(&self) {
- unimplemented!()
- }
-
- fn zoom(&self) {
- unimplemented!()
- }
-
- fn present_scene(&mut self, scene: Scene) {
- self.0.borrow_mut().scene = Some(scene);
- unsafe {
- let _: () = msg_send![*self.0.borrow().native_view, setNeedsDisplay: YES];
- }
- }
-
- fn toggle_fullscreen(&self) {
- unimplemented!()
- }
-
- fn on_event(&mut self, callback: Box<dyn FnMut(platform::Event) -> bool>) {
- self.0.borrow_mut().event_callback = Some(callback);
- }
-
- fn on_active_status_change(&mut self, _: Box<dyn FnMut(bool)>) {}
-
- fn on_resize(&mut self, _: Box<dyn FnMut()>) {}
-
- fn on_fullscreen(&mut self, _: Box<dyn FnMut(bool)>) {}
-
- fn on_moved(&mut self, _: Box<dyn FnMut()>) {}
-
- fn on_should_close(&mut self, _: Box<dyn FnMut() -> bool>) {}
-
- fn on_close(&mut self, _: Box<dyn FnOnce()>) {}
-
- fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
- self.0.borrow_mut().appearance_changed_callback = Some(callback);
- }
-
- fn is_topmost_for_position(&self, _: Vector2F) -> bool {
- true
- }
-}
-
-impl StatusItemState {
- fn bounds(&self) -> WindowBounds {
- unsafe {
- let window: id = self.native_window();
- let screen_frame = window.screen().visibleFrame();
- let window_frame = NSWindow::frame(window);
- let origin = vec2f(
- window_frame.origin.x as f32,
- (window_frame.origin.y - screen_frame.size.height - window_frame.size.height)
- as f32,
- );
- let size = vec2f(
- window_frame.size.width as f32,
- window_frame.size.height as f32,
- );
- WindowBounds::Fixed(RectF::new(origin, size))
- }
- }
-
- fn content_size(&self) -> Vector2F {
- unsafe {
- let NSSize { width, height, .. } =
- NSView::frame(self.native_item.button().superview().superview()).size;
- vec2f(width as f32, height as f32)
- }
- }
-
- fn scale_factor(&self) -> f32 {
- unsafe {
- let window: id = msg_send![self.native_item.button(), window];
- NSScreen::backingScaleFactor(window.screen()) as f32
- }
- }
-
- pub fn native_window(&self) -> id {
- unsafe { msg_send![self.native_item.button(), window] }
- }
-}
-
-extern "C" fn dealloc_view(this: &Object, _: Sel) {
- unsafe {
- drop_state(this);
-
- let _: () = msg_send![super(this, class!(NSView)), dealloc];
- }
-}
-
-extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
- unsafe {
- if let Some(state) = get_state(this).upgrade() {
- let mut state_borrow = state.as_ref().borrow_mut();
- if let Some(event) =
- Event::from_native(native_event, Some(state_borrow.content_size().y()))
- {
- if let Some(mut callback) = state_borrow.event_callback.take() {
- drop(state_borrow);
- callback(event);
- state.borrow_mut().event_callback = Some(callback);
- }
- }
- }
- }
-}
-
-extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
- if let Some(state) = unsafe { get_state(this).upgrade() } {
- let state = state.borrow();
- state.renderer.layer().as_ptr() as id
- } else {
- nil
- }
-}
-
-extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
- unsafe {
- if let Some(state) = get_state(this).upgrade() {
- let mut state = state.borrow_mut();
- if let Some(scene) = state.scene.take() {
- state.renderer.render(&scene);
- }
- }
- }
-}
-
-extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
- unsafe {
- if let Some(state) = get_state(this).upgrade() {
- let mut state_borrow = state.as_ref().borrow_mut();
- if let Some(mut callback) = state_borrow.appearance_changed_callback.take() {
- drop(state_borrow);
- callback();
- state.borrow_mut().appearance_changed_callback = Some(callback);
- }
- }
- }
-}
-
-unsafe fn get_state(object: &Object) -> Weak<RefCell<StatusItemState>> {
- let raw: *mut c_void = *object.get_ivar(STATE_IVAR);
- let weak1 = Weak::from_raw(raw as *mut RefCell<StatusItemState>);
- let weak2 = weak1.clone();
- let _ = Weak::into_raw(weak1);
- weak2
-}
-
-unsafe fn drop_state(object: &Object) {
- let raw: *const c_void = *object.get_ivar(STATE_IVAR);
- Weak::from_raw(raw as *const RefCell<StatusItemState>);
-}
@@ -1,37 +0,0 @@
-use crate::WindowAppearance;
-use cocoa::{
- appkit::{NSAppearanceNameVibrantDark, NSAppearanceNameVibrantLight},
- base::id,
- foundation::NSString,
-};
-use objc::{msg_send, sel, sel_impl};
-use std::ffi::CStr;
-
-impl WindowAppearance {
- pub(crate) unsafe fn from_native(appearance: id) -> Self {
- let name: id = msg_send![appearance, name];
- unsafe {
- if name == NSAppearanceNameVibrantLight {
- Self::VibrantLight
- } else if name == NSAppearanceNameVibrantDark {
- Self::VibrantDark
- } else if name == NSAppearanceNameAqua {
- Self::Light
- } else if name == NSAppearanceNameDarkAqua {
- Self::Dark
- } else {
- println!(
- "unknown appearance: {:?}",
- CStr::from_ptr(name.UTF8String())
- );
- Self::Light
- }
- }
- }
-}
-
-#[link(name = "AppKit", kind = "framework")]
-unsafe extern "C" {
- pub static NSAppearanceNameAqua: id;
- pub static NSAppearanceNameDarkAqua: id;
-}
@@ -15,7 +15,7 @@ use std::sync::atomic::{self, AtomicBool};
/// `scap_default_target_source` should be used instead on Wayland, since `scap_screen_sources`
/// won't return any results.
#[allow(dead_code)]
-pub(crate) fn scap_screen_sources(
+pub fn scap_screen_sources(
foreground_executor: &ForegroundExecutor,
) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
let (sources_tx, sources_rx) = oneshot::channel();
@@ -15,11 +15,6 @@ use std::{
rc::{Rc, Weak},
sync::Arc,
};
-#[cfg(target_os = "windows")]
-use windows::Win32::{
- Graphics::Imaging::{CLSID_WICImagingFactory, IWICImagingFactory},
- System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance},
-};
/// TestPlatform implements the Platform trait for use in tests.
pub(crate) struct TestPlatform {
@@ -39,8 +34,6 @@ pub(crate) struct TestPlatform {
pub opened_url: RefCell<Option<String>>,
pub text_system: Arc<dyn PlatformTextSystem>,
pub expect_restart: RefCell<Option<oneshot::Sender<Option<PathBuf>>>>,
- #[cfg(target_os = "windows")]
- bitmap_factory: std::mem::ManuallyDrop<IWICImagingFactory>,
weak: Weak<Self>,
}
@@ -95,16 +88,6 @@ pub(crate) struct TestPrompts {
impl TestPlatform {
pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Rc<Self> {
- #[cfg(target_os = "windows")]
- let bitmap_factory = unsafe {
- windows::Win32::System::Ole::OleInitialize(None)
- .expect("unable to initialize Windows OLE");
- std::mem::ManuallyDrop::new(
- CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER)
- .expect("Error creating bitmap factory."),
- )
- };
-
let text_system = Arc::new(NoopTextSystem);
Rc::new_cyclic(|weak| TestPlatform {
@@ -123,8 +106,6 @@ impl TestPlatform {
current_find_pasteboard_item: Mutex::new(None),
weak: weak.clone(),
opened_url: Default::default(),
- #[cfg(target_os = "windows")]
- bitmap_factory,
text_system,
})
}
@@ -288,12 +269,10 @@ impl Platform for TestPlatform {
Some(self.active_display.clone())
}
- #[cfg(feature = "screen-capture")]
fn is_screen_capture_supported(&self) -> bool {
true
}
- #[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
@@ -458,16 +437,6 @@ impl TestScreenCaptureSource {
}
}
-#[cfg(target_os = "windows")]
-impl Drop for TestPlatform {
- fn drop(&mut self) {
- unsafe {
- std::mem::ManuallyDrop::drop(&mut self.bitmap_factory);
- windows::Win32::System::Ole::OleUninitialize();
- }
- }
-}
-
struct TestKeyboardLayout;
impl PlatformKeyboardLayout for TestKeyboardLayout {
@@ -32,7 +32,7 @@ pub(crate) struct TestWindowState {
}
#[derive(Clone)]
-pub(crate) struct TestWindow(pub(crate) Rc<Mutex<TestWindowState>>);
+pub struct TestWindow(pub(crate) Rc<Mutex<TestWindowState>>);
impl HasWindowHandle for TestWindow {
fn window_handle(
@@ -51,7 +51,7 @@ impl HasDisplayHandle for TestWindow {
}
impl TestWindow {
- pub fn new(
+ pub(crate) fn new(
handle: AnyWindowHandle,
params: WindowParams,
platform: Weak<TestPlatform>,
@@ -9,7 +9,7 @@
use crate::ScreenCaptureSource;
use crate::{
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor, Keymap,
- MacPlatform, Menu, MenuItem, OwnedMenu, PathPromptOptions, Platform, PlatformDisplay,
+ Menu, MenuItem, OwnedMenu, PathPromptOptions, Platform, PlatformDisplay,
PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, PlatformWindow, Task,
TestDispatcher, WindowAppearance, WindowParams,
};
@@ -33,7 +33,7 @@ pub struct VisualTestPlatform {
dispatcher: TestDispatcher,
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
- mac_platform: MacPlatform,
+ platform: Rc<dyn Platform>,
clipboard: Mutex<Option<ClipboardItem>>,
find_pasteboard: Mutex<Option<ClipboardItem>>,
}
@@ -42,20 +42,18 @@ impl VisualTestPlatform {
/// Creates a new VisualTestPlatform with the given random seed.
///
/// The seed is used for deterministic random number generation in the TestDispatcher.
- pub fn new(seed: u64) -> Self {
+ pub fn new(platform: Rc<dyn Platform>, seed: u64) -> Self {
let dispatcher = TestDispatcher::new(seed);
let arc_dispatcher = Arc::new(dispatcher.clone());
let background_executor = BackgroundExecutor::new(arc_dispatcher.clone());
let foreground_executor = ForegroundExecutor::new(arc_dispatcher);
- let mac_platform = MacPlatform::new(false);
-
Self {
dispatcher,
background_executor,
foreground_executor,
- mac_platform,
+ platform,
clipboard: Mutex::new(None),
find_pasteboard: Mutex::new(None),
}
@@ -77,7 +75,7 @@ impl Platform for VisualTestPlatform {
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
- self.mac_platform.text_system()
+ self.platform.text_system()
}
fn run(&self, _on_finish_launching: Box<dyn 'static + FnOnce()>) {
@@ -97,31 +95,29 @@ impl Platform for VisualTestPlatform {
fn unhide_other_apps(&self) {}
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
- self.mac_platform.displays()
+ self.platform.displays()
}
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
- self.mac_platform.primary_display()
+ self.platform.primary_display()
}
fn active_window(&self) -> Option<AnyWindowHandle> {
- self.mac_platform.active_window()
+ self.platform.active_window()
}
fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
- self.mac_platform.window_stack()
+ self.platform.window_stack()
}
- #[cfg(feature = "screen-capture")]
fn is_screen_capture_supported(&self) -> bool {
- self.mac_platform.is_screen_capture_supported()
+ self.platform.is_screen_capture_supported()
}
- #[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
- self.mac_platform.screen_capture_sources()
+ self.platform.screen_capture_sources()
}
fn open_window(
@@ -129,15 +125,15 @@ impl Platform for VisualTestPlatform {
handle: AnyWindowHandle,
options: WindowParams,
) -> Result<Box<dyn PlatformWindow>> {
- self.mac_platform.open_window(handle, options)
+ self.platform.open_window(handle, options)
}
fn window_appearance(&self) -> WindowAppearance {
- self.mac_platform.window_appearance()
+ self.platform.window_appearance()
}
fn open_url(&self, url: &str) {
- self.mac_platform.open_url(url)
+ self.platform.open_url(url)
}
fn on_open_urls(&self, _callback: Box<dyn FnMut(Vec<String>)>) {}
@@ -170,11 +166,11 @@ impl Platform for VisualTestPlatform {
}
fn reveal_path(&self, path: &Path) {
- self.mac_platform.reveal_path(path)
+ self.platform.reveal_path(path)
}
fn open_with_system(&self, path: &Path) {
- self.mac_platform.open_with_system(path)
+ self.platform.open_with_system(path)
}
fn on_quit(&self, _callback: Box<dyn FnMut()>) {}
@@ -196,19 +192,19 @@ impl Platform for VisualTestPlatform {
fn on_validate_app_menu_command(&self, _callback: Box<dyn FnMut(&dyn crate::Action) -> bool>) {}
fn app_path(&self) -> Result<PathBuf> {
- self.mac_platform.app_path()
+ self.platform.app_path()
}
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
- self.mac_platform.path_for_auxiliary_executable(name)
+ self.platform.path_for_auxiliary_executable(name)
}
fn set_cursor_style(&self, style: CursorStyle) {
- self.mac_platform.set_cursor_style(style)
+ self.platform.set_cursor_style(style)
}
fn should_auto_hide_scrollbars(&self) -> bool {
- self.mac_platform.should_auto_hide_scrollbars()
+ self.platform.should_auto_hide_scrollbars()
}
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
@@ -242,11 +238,11 @@ impl Platform for VisualTestPlatform {
}
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
- self.mac_platform.keyboard_layout()
+ self.platform.keyboard_layout()
}
fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper> {
- self.mac_platform.keyboard_mapper()
+ self.platform.keyboard_mapper()
}
fn on_keyboard_layout_change(&self, _callback: Box<dyn FnMut()>) {}
@@ -1,7 +0,0 @@
-mod wgpu_atlas;
-mod wgpu_context;
-mod wgpu_renderer;
-
-pub(crate) use wgpu_atlas::*;
-pub(crate) use wgpu_context::*;
-pub(crate) use wgpu_renderer::*;
@@ -30,7 +30,8 @@ pub struct ThreadTaskTimings {
}
impl ThreadTaskTimings {
- pub(crate) fn convert(timings: &[GlobalThreadTimings]) -> Vec<Self> {
+ /// Convert global thread timings into their structured format.
+ pub fn convert(timings: &[GlobalThreadTimings]) -> Vec<Self> {
timings
.iter()
.filter_map(|t| match t.timings.upgrade() {
@@ -245,19 +246,24 @@ impl ProfilingCollector {
// Allow 20mb of task timing entries
const MAX_TASK_TIMINGS: usize = (20 * 1024 * 1024) / core::mem::size_of::<TaskTiming>();
-pub(crate) type TaskTimings = circular_buffer::CircularBuffer<MAX_TASK_TIMINGS, TaskTiming>;
-pub(crate) type GuardedTaskTimings = spin::Mutex<ThreadTimings>;
+#[doc(hidden)]
+pub type TaskTimings = circular_buffer::CircularBuffer<MAX_TASK_TIMINGS, TaskTiming>;
+#[doc(hidden)]
+pub type GuardedTaskTimings = spin::Mutex<ThreadTimings>;
-pub(crate) struct GlobalThreadTimings {
+#[doc(hidden)]
+pub struct GlobalThreadTimings {
pub thread_id: ThreadId,
pub timings: std::sync::Weak<GuardedTaskTimings>,
}
-pub(crate) static GLOBAL_THREAD_TIMINGS: spin::Mutex<Vec<GlobalThreadTimings>> =
+#[doc(hidden)]
+pub static GLOBAL_THREAD_TIMINGS: spin::Mutex<Vec<GlobalThreadTimings>> =
spin::Mutex::new(Vec::new());
thread_local! {
- pub(crate) static THREAD_TIMINGS: LazyCell<Arc<GuardedTaskTimings>> = LazyCell::new(|| {
+ #[doc(hidden)]
+ pub static THREAD_TIMINGS: LazyCell<Arc<GuardedTaskTimings>> = LazyCell::new(|| {
let current_thread = std::thread::current();
let thread_name = current_thread.name();
let thread_id = current_thread.id();
@@ -277,7 +283,8 @@ thread_local! {
});
}
-pub(crate) struct ThreadTimings {
+#[doc(hidden)]
+pub struct ThreadTimings {
pub thread_name: Option<String>,
pub thread_id: ThreadId,
pub timings: Box<TaskTimings>,
@@ -285,7 +292,7 @@ pub(crate) struct ThreadTimings {
}
impl ThreadTimings {
- pub(crate) fn new(thread_name: Option<String>, thread_id: ThreadId) -> Self {
+ pub fn new(thread_name: Option<String>, thread_id: ThreadId) -> Self {
ThreadTimings {
thread_name,
thread_id,
@@ -310,8 +317,9 @@ impl Drop for ThreadTimings {
}
}
+#[doc(hidden)]
#[allow(dead_code)] // Used by Linux and Windows dispatchers, not macOS
-pub(crate) fn add_task_timing(timing: TaskTiming) {
+pub fn add_task_timing(timing: TaskTiming) {
THREAD_TIMINGS.with(|timings| {
let mut timings = timings.lock();
@@ -86,7 +86,8 @@ impl<T> PriorityQueueState<T> {
}
}
-pub(crate) struct PriorityQueueSender<T> {
+#[doc(hidden)]
+pub struct PriorityQueueSender<T> {
state: Arc<PriorityQueueState<T>>,
}
@@ -95,7 +96,7 @@ impl<T> PriorityQueueSender<T> {
Self { state }
}
- pub(crate) fn send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
+ pub fn send(&self, priority: Priority, item: T) -> Result<(), SendError<T>> {
self.state.send(priority, item)?;
Ok(())
}
@@ -109,7 +110,8 @@ impl<T> Drop for PriorityQueueSender<T> {
}
}
-pub(crate) struct PriorityQueueReceiver<T> {
+#[doc(hidden)]
+pub struct PriorityQueueReceiver<T> {
state: Arc<PriorityQueueState<T>>,
rand: SmallRng,
disconnected: bool,
@@ -128,7 +130,8 @@ impl<T> Clone for PriorityQueueReceiver<T> {
}
}
-pub(crate) struct SendError<T>(T);
+#[doc(hidden)]
+pub struct SendError<T>(pub T);
impl<T: fmt::Debug> fmt::Debug for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -137,11 +140,12 @@ impl<T: fmt::Debug> fmt::Debug for SendError<T> {
}
#[derive(Debug)]
-pub(crate) struct RecvError;
+#[doc(hidden)]
+pub struct RecvError;
#[allow(dead_code)]
impl<T> PriorityQueueReceiver<T> {
- pub(crate) fn new() -> (PriorityQueueSender<T>, Self) {
+ pub fn new() -> (PriorityQueueSender<T>, Self) {
let state = PriorityQueueState {
queues: parking_lot::Mutex::new(PriorityQueues {
high_priority: VecDeque::new(),
@@ -175,7 +179,7 @@ impl<T> PriorityQueueReceiver<T> {
/// # Errors
///
/// If the sender was dropped
- pub(crate) fn try_pop(&mut self) -> Result<Option<T>, RecvError> {
+ pub fn try_pop(&mut self) -> Result<Option<T>, RecvError> {
self.pop_inner(false)
}
@@ -187,13 +191,13 @@ impl<T> PriorityQueueReceiver<T> {
/// # Errors
///
/// If the sender was dropped
- pub(crate) fn pop(&mut self) -> Result<T, RecvError> {
+ pub fn pop(&mut self) -> Result<T, RecvError> {
self.pop_inner(true).map(|e| e.unwrap())
}
/// Returns an iterator over the elements of the queue
/// this iterator will end when all elements have been consumed and will not wait for new ones.
- pub(crate) fn try_iter(self) -> TryIter<T> {
+ pub fn try_iter(self) -> TryIter<T> {
TryIter {
receiver: self,
ended: false,
@@ -202,7 +206,7 @@ impl<T> PriorityQueueReceiver<T> {
/// Returns an iterator over the elements of the queue
/// this iterator will wait for new elements if the queue is empty.
- pub(crate) fn iter(self) -> Iter<T> {
+ pub fn iter(self) -> Iter<T> {
Iter(self)
}
@@ -261,8 +265,8 @@ impl<T> Drop for PriorityQueueReceiver<T> {
}
}
-/// If None is returned the sender disconnected
-pub(crate) struct Iter<T>(PriorityQueueReceiver<T>);
+#[doc(hidden)]
+pub struct Iter<T>(PriorityQueueReceiver<T>);
impl<T> Iterator for Iter<T> {
type Item = T;
@@ -272,8 +276,8 @@ impl<T> Iterator for Iter<T> {
}
impl<T> FusedIterator for Iter<T> {}
-/// If None is returned there are no more elements in the queue
-pub(crate) struct TryIter<T> {
+#[doc(hidden)]
+pub struct TryIter<T> {
receiver: PriorityQueueReceiver<T>,
ended: bool,
}
@@ -16,25 +16,29 @@ use std::{
};
#[allow(non_camel_case_types, unused)]
-pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
+#[expect(missing_docs)]
+pub type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
-pub(crate) type DrawOrder = u32;
+#[expect(missing_docs)]
+pub type DrawOrder = u32;
#[derive(Default)]
-pub(crate) struct Scene {
+#[expect(missing_docs)]
+pub struct Scene {
pub(crate) paint_operations: Vec<PaintOperation>,
primitive_bounds: BoundsTree<ScaledPixels>,
layer_stack: Vec<DrawOrder>,
- pub(crate) shadows: Vec<Shadow>,
- pub(crate) quads: Vec<Quad>,
- pub(crate) paths: Vec<Path<ScaledPixels>>,
- pub(crate) underlines: Vec<Underline>,
- pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
- pub(crate) subpixel_sprites: Vec<SubpixelSprite>,
- pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
- pub(crate) surfaces: Vec<PaintSurface>,
+ pub shadows: Vec<Shadow>,
+ pub quads: Vec<Quad>,
+ pub paths: Vec<Path<ScaledPixels>>,
+ pub underlines: Vec<Underline>,
+ pub monochrome_sprites: Vec<MonochromeSprite>,
+ pub subpixel_sprites: Vec<SubpixelSprite>,
+ pub polychrome_sprites: Vec<PolychromeSprite>,
+ pub surfaces: Vec<PaintSurface>,
}
+#[expect(missing_docs)]
impl Scene {
pub fn clear(&mut self) {
self.paint_operations.clear();
@@ -151,7 +155,7 @@ impl Scene {
),
allow(dead_code)
)]
- pub(crate) fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> + '_ {
+ pub fn batches(&self) -> impl Iterator<Item = PrimitiveBatch> + '_ {
BatchIterator {
shadows_start: 0,
shadows_iter: self.shadows.iter().peekable(),
@@ -200,7 +204,8 @@ pub(crate) enum PaintOperation {
}
#[derive(Clone)]
-pub(crate) enum Primitive {
+#[expect(missing_docs)]
+pub enum Primitive {
Shadow(Shadow),
Quad(Quad),
Path(Path<ScaledPixels>),
@@ -211,6 +216,7 @@ pub(crate) enum Primitive {
Surface(PaintSurface),
}
+#[expect(missing_docs)]
impl Primitive {
pub fn bounds(&self) -> &Bounds<ScaledPixels> {
match self {
@@ -453,7 +459,8 @@ impl<'a> Iterator for BatchIterator<'a> {
),
allow(dead_code)
)]
-pub(crate) enum PrimitiveBatch {
+#[expect(missing_docs)]
+pub enum PrimitiveBatch {
Shadows(Range<usize>),
Quads(Range<usize>),
Paths(Range<usize>),
@@ -476,7 +483,8 @@ pub(crate) enum PrimitiveBatch {
#[derive(Default, Debug, Clone)]
#[repr(C)]
-pub(crate) struct Quad {
+#[expect(missing_docs)]
+pub struct Quad {
pub order: DrawOrder,
pub border_style: BorderStyle,
pub bounds: Bounds<ScaledPixels>,
@@ -495,7 +503,8 @@ impl From<Quad> for Primitive {
#[derive(Debug, Clone)]
#[repr(C)]
-pub(crate) struct Underline {
+#[expect(missing_docs)]
+pub struct Underline {
pub order: DrawOrder,
pub pad: u32, // align to 8 bytes
pub bounds: Bounds<ScaledPixels>,
@@ -513,7 +522,8 @@ impl From<Underline> for Primitive {
#[derive(Debug, Clone)]
#[repr(C)]
-pub(crate) struct Shadow {
+#[expect(missing_docs)]
+pub struct Shadow {
pub order: DrawOrder,
pub blur_radius: ScaledPixels,
pub bounds: Bounds<ScaledPixels>,
@@ -644,7 +654,8 @@ impl Default for TransformationMatrix {
#[derive(Clone, Debug)]
#[repr(C)]
-pub(crate) struct MonochromeSprite {
+#[expect(missing_docs)]
+pub struct MonochromeSprite {
pub order: DrawOrder,
pub pad: u32, // align to 8 bytes
pub bounds: Bounds<ScaledPixels>,
@@ -662,7 +673,8 @@ impl From<MonochromeSprite> for Primitive {
#[derive(Clone, Debug)]
#[repr(C)]
-pub(crate) struct SubpixelSprite {
+#[expect(missing_docs)]
+pub struct SubpixelSprite {
pub order: DrawOrder,
pub pad: u32, // align to 8 bytes
pub bounds: Bounds<ScaledPixels>,
@@ -680,7 +692,8 @@ impl From<SubpixelSprite> for Primitive {
#[derive(Clone, Debug)]
#[repr(C)]
-pub(crate) struct PolychromeSprite {
+#[expect(missing_docs)]
+pub struct PolychromeSprite {
pub order: DrawOrder,
pub pad: u32, // align to 8 bytes
pub grayscale: bool,
@@ -698,7 +711,8 @@ impl From<PolychromeSprite> for Primitive {
}
#[derive(Clone, Debug)]
-pub(crate) struct PaintSurface {
+#[expect(missing_docs)]
+pub struct PaintSurface {
pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ContentMask<ScaledPixels>,
@@ -713,17 +727,19 @@ impl From<PaintSurface> for Primitive {
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub(crate) struct PathId(pub(crate) usize);
+#[expect(missing_docs)]
+pub struct PathId(pub usize);
/// A line made up of a series of vertices and control points.
#[derive(Clone, Debug)]
+#[expect(missing_docs)]
pub struct Path<P: Clone + Debug + Default + PartialEq> {
- pub(crate) id: PathId,
- pub(crate) order: DrawOrder,
- pub(crate) bounds: Bounds<P>,
- pub(crate) content_mask: ContentMask<P>,
- pub(crate) vertices: Vec<PathVertex<P>>,
- pub(crate) color: Background,
+ pub id: PathId,
+ pub order: DrawOrder,
+ pub bounds: Bounds<P>,
+ pub content_mask: ContentMask<P>,
+ pub vertices: Vec<PathVertex<P>>,
+ pub color: Background,
start: Point<P>,
current: Point<P>,
contour_count: usize,
@@ -847,7 +863,8 @@ where
T: Clone + Debug + Default + PartialEq + PartialOrd + Add<T, Output = T> + Sub<Output = T>,
{
#[allow(unused)]
- pub(crate) fn clipped_bounds(&self) -> Bounds<T> {
+ #[expect(missing_docs)]
+ pub fn clipped_bounds(&self) -> Bounds<T> {
self.bounds.intersect(&self.content_mask.bounds)
}
}
@@ -860,12 +877,14 @@ impl From<Path<ScaledPixels>> for Primitive {
#[derive(Clone, Debug)]
#[repr(C)]
-pub(crate) struct PathVertex<P: Clone + Debug + Default + PartialEq> {
- pub(crate) xy_position: Point<P>,
- pub(crate) st_position: Point<f32>,
- pub(crate) content_mask: ContentMask<P>,
+#[expect(missing_docs)]
+pub struct PathVertex<P: Clone + Debug + Default + PartialEq> {
+ pub xy_position: Point<P>,
+ pub st_position: Point<f32>,
+ pub content_mask: ContentMask<P>,
}
+#[expect(missing_docs)]
impl PathVertex<Pixels> {
pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
PathVertex {
@@ -14,9 +14,10 @@ use std::{
pub const SMOOTH_SVG_SCALE_FACTOR: f32 = 2.;
#[derive(Clone, PartialEq, Hash, Eq)]
-pub(crate) struct RenderSvgParams {
- pub(crate) path: SharedString,
- pub(crate) size: Size<DevicePixels>,
+#[expect(missing_docs)]
+pub struct RenderSvgParams {
+ pub path: SharedString,
+ pub size: Size<DevicePixels>,
}
#[derive(Clone)]
@@ -41,14 +41,15 @@ pub struct FontId(pub usize);
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
pub struct FontFamilyId(pub usize);
-pub(crate) const SUBPIXEL_VARIANTS_X: u8 = 4;
-
-pub(crate) const SUBPIXEL_VARIANTS_Y: u8 =
- if cfg!(target_os = "windows") || cfg!(target_os = "linux") {
- 1
- } else {
- SUBPIXEL_VARIANTS_X
- };
+/// Number of subpixel glyph variants along the X axis.
+pub const SUBPIXEL_VARIANTS_X: u8 = 4;
+
+/// Number of subpixel glyph variants along the Y axis.
+pub const SUBPIXEL_VARIANTS_Y: u8 = if cfg!(target_os = "windows") || cfg!(target_os = "linux") {
+ 1
+} else {
+ SUBPIXEL_VARIANTS_X
+};
/// The GPUI text rendering sub system.
pub struct TextSystem {
@@ -799,17 +800,18 @@ impl TextRun {
/// An identifier for a specific glyph, as returned by [`WindowTextSystem::layout_line`].
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)]
-pub struct GlyphId(pub(crate) u32);
+pub struct GlyphId(pub u32);
#[derive(Clone, Debug, PartialEq)]
-pub(crate) struct RenderGlyphParams {
- pub(crate) font_id: FontId,
- pub(crate) glyph_id: GlyphId,
- pub(crate) font_size: Pixels,
- pub(crate) subpixel_variant: Point<u8>,
- pub(crate) scale_factor: f32,
- pub(crate) is_emoji: bool,
- pub(crate) subpixel_rendering: bool,
+#[expect(missing_docs)]
+pub struct RenderGlyphParams {
+ pub font_id: FontId,
+ pub glyph_id: GlyphId,
+ pub font_size: Pixels,
+ pub subpixel_variant: Point<u8>,
+ pub scale_factor: f32,
+ pub is_emoji: bool,
+ pub subpixel_rendering: bool,
}
impl Eq for RenderGlyphParams {}
@@ -884,32 +886,32 @@ impl Font {
pub struct FontMetrics {
/// The number of font units that make up the "em square",
/// a scalable grid for determining the size of a typeface.
- pub(crate) units_per_em: u32,
+ pub units_per_em: u32,
/// The vertical distance from the baseline of the font to the top of the glyph covers.
- pub(crate) ascent: f32,
+ pub ascent: f32,
/// The vertical distance from the baseline of the font to the bottom of the glyph covers.
- pub(crate) descent: f32,
+ pub descent: f32,
/// The recommended additional space to add between lines of type.
- pub(crate) line_gap: f32,
+ pub line_gap: f32,
/// The suggested position of the underline.
- pub(crate) underline_position: f32,
+ pub underline_position: f32,
/// The suggested thickness of the underline.
- pub(crate) underline_thickness: f32,
+ pub underline_thickness: f32,
/// The height of a capital letter measured from the baseline of the font.
- pub(crate) cap_height: f32,
+ pub cap_height: f32,
/// The height of a lowercase x.
- pub(crate) x_height: f32,
+ pub x_height: f32,
/// The outer limits of the area that the font covers.
/// Corresponds to the xMin / xMax / yMin / yMax values in the OpenType `head` table
- pub(crate) bounding_box: Bounds<f32>,
+ pub bounding_box: Bounds<f32>,
}
impl FontMetrics {
@@ -954,8 +956,9 @@ impl FontMetrics {
}
}
+/// Maps well-known virtual font names to their concrete equivalents.
#[allow(unused)]
-pub(crate) fn font_name_with_fallbacks<'a>(name: &'a str, system: &'a str) -> &'a str {
+pub fn font_name_with_fallbacks<'a>(name: &'a str, system: &'a str) -> &'a str {
// Note: the "Zed Plex" fonts were deprecated as we are not allowed to use "Plex"
// in a derived font name. They are essentially indistinguishable from IBM Plex/Lilex,
// and so retained here for backward compatibility.
@@ -967,8 +970,9 @@ pub(crate) fn font_name_with_fallbacks<'a>(name: &'a str, system: &'a str) -> &'
}
}
+/// Like [`font_name_with_fallbacks`] but accepts and returns [`SharedString`] references.
#[allow(unused)]
-pub(crate) fn font_name_with_fallbacks_shared<'a>(
+pub fn font_name_with_fallbacks_shared<'a>(
name: &'a SharedString,
system: &'a SharedString,
) -> &'a SharedString {
@@ -594,9 +594,10 @@ impl LineLayoutCache {
/// A run of text with a single font.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
+#[expect(missing_docs)]
pub struct FontRun {
- pub(crate) len: usize,
- pub(crate) font_id: FontId,
+ pub len: usize,
+ pub font_id: FontId,
}
trait AsCacheKeyRef {
@@ -59,7 +59,8 @@ mod prompts;
use crate::util::atomic_incr_if_not_zero;
pub use prompts::*;
-pub(crate) const DEFAULT_WINDOW_SIZE: Size<Pixels> = size(px(1536.), px(864.));
+/// Default window size used when no explicit size is provided.
+pub const DEFAULT_WINDOW_SIZE: Size<Pixels> = size(px(1536.), px(864.));
/// A 6:5 aspect ratio minimum window size to be used for functional,
/// additional-to-main-Zed windows, like the settings and rules library windows.
@@ -1447,7 +1448,8 @@ impl Window {
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
-pub(crate) struct DispatchEventResult {
+#[expect(missing_docs)]
+pub struct DispatchEventResult {
pub propagate: bool,
pub default_prevented: bool,
}
@@ -0,0 +1,134 @@
+[package]
+name = "gpui_linux"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/gpui_linux.rs"
+
+[features]
+default = ["wayland", "x11"]
+test-support = ["gpui/test-support"]
+wayland = [
+ "bitflags",
+ "gpui_wgpu",
+ "ashpd/wayland",
+ "cosmic-text",
+ "font-kit",
+ "calloop-wayland-source",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-plasma",
+ "wayland-protocols-wlr",
+ "filedescriptor",
+ "xkbcommon",
+ "open",
+]
+x11 = [
+ "gpui_wgpu",
+ "ashpd",
+ "cosmic-text",
+ "font-kit",
+ "as-raw-xcb-connection",
+ "x11rb",
+ "xkbcommon",
+ "xim",
+ "x11-clipboard",
+ "filedescriptor",
+ "open",
+ "scap?/x11",
+]
+screen-capture = [
+ "gpui/screen-capture",
+ "scap",
+]
+
+
+[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
+anyhow.workspace = true
+bytemuck = "1"
+collections.workspace = true
+futures.workspace = true
+gpui.workspace = true
+gpui_wgpu = { workspace = true, optional = true }
+http_client.workspace = true
+itertools.workspace = true
+libc.workspace = true
+log.workspace = true
+parking_lot.workspace = true
+pathfinder_geometry = "0.5"
+profiling.workspace = true
+smallvec.workspace = true
+smol.workspace = true
+strum.workspace = true
+util.workspace = true
+uuid.workspace = true
+
+# Always used
+oo7 = { version = "0.5.0", default-features = false, features = [
+ "async-std",
+ "native_crypto",
+] }
+calloop = "0.14.3"
+raw-window-handle = "0.6"
+
+# Used in both windowing options
+ashpd = { workspace = true, optional = true }
+cosmic-text = { version = "0.17.0", optional = true }
+swash = { version = "0.2.6" }
+# WARNING: If you change this, you must also publish a new version of zed-font-kit to crates.io
+font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "110523127440aefb11ce0cf280ae7c5071337ec5", package = "zed-font-kit", version = "0.14.1-zed", features = [
+ "source-fontconfig-dlopen",
+], optional = true }
+bitflags = { workspace = true, optional = true }
+filedescriptor = { version = "0.8.2", optional = true }
+open = { version = "5.2.0", optional = true }
+xkbcommon = { version = "0.8.0", features = ["wayland", "x11"], optional = true }
+
+# Screen capture
+scap = { workspace = true, optional = true }
+
+# Wayland
+calloop-wayland-source = { version = "0.4.1", optional = true }
+wayland-backend = { version = "0.3.3", features = [
+ "client_system",
+ "dlopen",
+], optional = true }
+wayland-client = { version = "0.31.11", optional = true }
+wayland-cursor = { version = "0.31.11", optional = true }
+wayland-protocols = { version = "0.32.9", features = [
+ "client",
+ "staging",
+ "unstable",
+], optional = true }
+wayland-protocols-plasma = { version = "0.3.9", features = [
+ "client",
+], optional = true }
+wayland-protocols-wlr = { version = "0.3.9", features = [
+ "client",
+], optional = true }
+
+# X11
+as-raw-xcb-connection = { version = "1", optional = true }
+x11rb = { version = "0.13.1", features = [
+ "allow-unsafe-code",
+ "xkb",
+ "randr",
+ "xinput",
+ "cursor",
+ "resource_manager",
+ "sync",
+], optional = true }
+# WARNING: If you change this, you must also publish a new version of zed-xim to crates.io
+xim = { git = "https://github.com/zed-industries/xim-rs.git", rev = "16f35a2c881b815a2b6cdfd6687988e84f8447d8", features = [
+ "x11rb-xcb",
+ "x11rb-client",
+], package = "zed-xim", version = "0.4.0-zed", optional = true }
+x11-clipboard = { version = "0.9.3", optional = true }
@@ -0,0 +1 @@
+../../LICENSE-APACHE
@@ -0,0 +1,4 @@
+#![cfg(any(target_os = "linux", target_os = "freebsd"))]
+mod linux;
+
+pub use linux::current_platform;
@@ -0,0 +1,57 @@
+mod dispatcher;
+mod headless;
+mod keyboard;
+mod platform;
+#[cfg(any(feature = "wayland", feature = "x11"))]
+mod text_system;
+#[cfg(feature = "wayland")]
+mod wayland;
+#[cfg(feature = "x11")]
+mod x11;
+
+#[cfg(any(feature = "wayland", feature = "x11"))]
+mod xdg_desktop_portal;
+
+pub use dispatcher::*;
+pub(crate) use headless::*;
+pub(crate) use keyboard::*;
+pub use platform::*;
+#[cfg(any(feature = "wayland", feature = "x11"))]
+pub(crate) use text_system::*;
+#[cfg(feature = "wayland")]
+pub(crate) use wayland::*;
+#[cfg(feature = "x11")]
+pub(crate) use x11::*;
+
+use std::rc::Rc;
+
+/// Returns the default platform implementation for the current OS.
+pub fn current_platform(headless: bool) -> Rc<dyn gpui::Platform> {
+ #[cfg(feature = "x11")]
+ use anyhow::Context as _;
+
+ if headless {
+ return Rc::new(LinuxPlatform {
+ inner: HeadlessClient::new(),
+ });
+ }
+
+ match gpui::guess_compositor() {
+ #[cfg(feature = "wayland")]
+ "Wayland" => Rc::new(LinuxPlatform {
+ inner: WaylandClient::new(),
+ }),
+
+ #[cfg(feature = "x11")]
+ "X11" => Rc::new(LinuxPlatform {
+ inner: X11Client::new()
+ .context("Failed to initialize X11 client.")
+ .unwrap(),
+ }),
+
+ "Headless" => Rc::new(LinuxPlatform {
+ inner: HeadlessClient::new(),
+ }),
+ _ => unreachable!(),
+ }
+}
@@ -11,7 +11,7 @@ use std::{
time::{Duration, Instant},
};
-use crate::{
+use gpui::{
GLOBAL_THREAD_TIMINGS, PlatformDispatcher, Priority, PriorityQueueReceiver,
PriorityQueueSender, RunnableVariant, THREAD_TIMINGS, TaskTiming, ThreadTaskTimings, profiler,
};
@@ -39,8 +39,7 @@ impl LinuxDispatcher {
let mut background_threads = (0..thread_count)
.map(|i| {
- let mut receiver: PriorityQueueReceiver<RunnableVariant> =
- background_receiver.clone();
+ let receiver: PriorityQueueReceiver<RunnableVariant> = background_receiver.clone();
std::thread::Builder::new()
.name(format!("Worker-{i}"))
.spawn(move || {
@@ -140,12 +139,12 @@ impl LinuxDispatcher {
}
impl PlatformDispatcher for LinuxDispatcher {
- fn get_all_timings(&self) -> Vec<crate::ThreadTaskTimings> {
+ fn get_all_timings(&self) -> Vec<gpui::ThreadTaskTimings> {
let global_timings = GLOBAL_THREAD_TIMINGS.lock();
ThreadTaskTimings::convert(&global_timings)
}
- fn get_current_thread_timings(&self) -> crate::ThreadTaskTimings {
+ fn get_current_thread_timings(&self) -> gpui::ThreadTaskTimings {
THREAD_TIMINGS.with(|timings| {
let timings = timings.lock();
let thread_name = timings.thread_name.clone();
@@ -158,7 +157,7 @@ impl PlatformDispatcher for LinuxDispatcher {
vec.extend_from_slice(s1);
vec.extend_from_slice(s2);
- crate::ThreadTaskTimings {
+ gpui::ThreadTaskTimings {
thread_name,
thread_id: std::thread::current().id(),
timings: vec,
@@ -232,7 +231,7 @@ impl<T> PriorityQueueCalloopSender<T> {
Self { sender: tx, ping }
}
- fn send(&self, priority: Priority, item: T) -> Result<(), crate::queue::SendError<T>> {
+ fn send(&self, priority: Priority, item: T) -> Result<(), gpui::queue::SendError<T>> {
let res = self.sender.send(priority, item);
if res.is_ok() {
self.ping.ping();
@@ -312,7 +311,7 @@ impl<T> calloop::EventSource for PriorityQueueCalloopReceiver<T> {
.process_events(readiness, token, |(), &mut ()| {
let mut is_empty = true;
- let mut receiver = self.receiver.clone();
+ let receiver = self.receiver.clone();
for runnable in receiver.try_iter() {
match runnable {
Ok(r) => {
@@ -429,11 +428,11 @@ mod tests {
}
// running 1 test
-// test platform::linux::dispatcher::tests::tomato ... FAILED
+// test linux::dispatcher::tests::tomato ... FAILED
// failures:
-// ---- platform::linux::dispatcher::tests::tomato stdout ----
+// ---- linux::dispatcher::tests::tomato stdout ----
// [crates/gpui/src/platform/linux/dispatcher.rs:262:9]
// returning 1 tasks to process
// [crates/gpui/src/platform/linux/dispatcher.rs:480:75] evt = Msg(
@@ -441,6 +440,6 @@ mod tests {
// )
// returning 0 tasks to process
-// thread 'platform::linux::dispatcher::tests::tomato' (478301) panicked at crates/gpui/src/platform/linux/dispatcher.rs:515:9:
+// thread 'linux::dispatcher::tests::tomato' (478301) panicked at crates/gpui/src/platform/linux/dispatcher.rs:515:9:
// assertion failed: data.got_closed
// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
@@ -4,11 +4,10 @@ use std::rc::Rc;
use calloop::{EventLoop, LoopHandle};
use util::ResultExt;
-use crate::platform::linux::LinuxClient;
-use crate::platform::{LinuxCommon, PlatformWindow};
-use crate::{
- AnyWindowHandle, CursorStyle, DisplayId, LinuxKeyboardLayout, PlatformDisplay,
- PlatformKeyboardLayout, WindowParams,
+use crate::linux::{LinuxClient, LinuxCommon, LinuxKeyboardLayout};
+use gpui::{
+ AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, PlatformKeyboardLayout,
+ PlatformWindow, WindowParams,
};
pub struct HeadlessClientState {
@@ -65,17 +64,11 @@ impl LinuxClient for HeadlessClient {
None
}
- #[cfg(feature = "screen-capture")]
- fn is_screen_capture_supported(&self) -> bool {
- false
- }
-
- #[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
- ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>>
+ ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>>
{
- let (mut tx, rx) = futures::channel::oneshot::channel();
+ let (tx, rx) = futures::channel::oneshot::channel();
tx.send(Err(anyhow::anyhow!(
"Headless mode does not support screen capture."
)))
@@ -109,15 +102,15 @@ impl LinuxClient for HeadlessClient {
fn reveal_path(&self, _path: std::path::PathBuf) {}
- fn write_to_primary(&self, _item: crate::ClipboardItem) {}
+ fn write_to_primary(&self, _item: gpui::ClipboardItem) {}
- fn write_to_clipboard(&self, _item: crate::ClipboardItem) {}
+ fn write_to_clipboard(&self, _item: gpui::ClipboardItem) {}
- fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
+ fn read_from_primary(&self) -> Option<gpui::ClipboardItem> {
None
}
- fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
+ fn read_from_clipboard(&self) -> Option<gpui::ClipboardItem> {
None
}
@@ -1,4 +1,4 @@
-use crate::{PlatformKeyboardLayout, SharedString};
+use gpui::{PlatformKeyboardLayout, SharedString};
#[derive(Clone)]
pub(crate) struct LinuxKeyboardLayout {
@@ -21,15 +21,15 @@ use util::command::{new_command, new_std_command};
#[cfg(any(feature = "wayland", feature = "x11"))]
use xkbcommon::xkb::{self, Keycode, Keysym, State};
-use crate::{
+use crate::linux::{LinuxDispatcher, PriorityQueueCalloopReceiver};
+use gpui::{
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
- ForegroundExecutor, Keymap, LinuxDispatcher, Menu, MenuItem, OwnedMenu, PathPromptOptions,
- Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem,
- PlatformWindow, PriorityQueueCalloopReceiver, Result, RunnableVariant, Task, ThermalState,
- WindowAppearance, WindowParams,
+ ForegroundExecutor, Keymap, Menu, MenuItem, OwnedMenu, PathPromptOptions, Platform,
+ PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem,
+ PlatformWindow, Result, RunnableVariant, Task, ThermalState, WindowAppearance, WindowParams,
};
#[cfg(any(feature = "wayland", feature = "x11"))]
-use crate::{Pixels, Point, px};
+use gpui::{Pixels, Point, px};
#[cfg(any(feature = "wayland", feature = "x11"))]
pub(crate) const SCROLL_LINES: f32 = 3.0;
@@ -90,7 +90,7 @@ impl<T> ResultExt for anyhow::Result<T> {
}
}
-pub trait LinuxClient {
+pub(crate) trait LinuxClient {
fn compositor_name(&self) -> &'static str;
fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R;
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout>;
@@ -99,11 +99,21 @@ pub trait LinuxClient {
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
#[cfg(feature = "screen-capture")]
- fn is_screen_capture_supported(&self) -> bool;
+ fn is_screen_capture_supported(&self) -> bool {
+ false
+ }
#[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
- ) -> oneshot::Receiver<Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>>;
+ ) -> oneshot::Receiver<Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>> {
+ let (sources_tx, sources_rx) = oneshot::channel();
+ sources_tx
+ .send(Err(anyhow::anyhow!(
+ "gpui_linux was compiled without the screen-capture feature"
+ )))
+ .ok();
+ sources_rx
+ }
fn open_window(
&self,
@@ -156,7 +166,7 @@ impl LinuxCommon {
let (main_sender, main_receiver) = PriorityQueueCalloopReceiver::new();
#[cfg(any(feature = "wayland", feature = "x11"))]
- let text_system = Arc::new(crate::CosmicTextSystem::new());
+ let text_system = Arc::new(crate::linux::CosmicTextSystem::new());
#[cfg(not(any(feature = "wayland", feature = "x11")))]
let text_system = Arc::new(crate::NoopTextSystem::new());
@@ -181,29 +191,36 @@ impl LinuxCommon {
}
}
-impl<P: LinuxClient + 'static> Platform for P {
+pub(crate) struct LinuxPlatform<P> {
+ pub(crate) inner: P,
+}
+
+impl<P: LinuxClient + 'static> Platform for LinuxPlatform<P> {
fn background_executor(&self) -> BackgroundExecutor {
- self.with_common(|common| common.background_executor.clone())
+ self.inner
+ .with_common(|common| common.background_executor.clone())
}
fn foreground_executor(&self) -> ForegroundExecutor {
- self.with_common(|common| common.foreground_executor.clone())
+ self.inner
+ .with_common(|common| common.foreground_executor.clone())
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
- self.with_common(|common| common.text_system.clone())
+ self.inner.with_common(|common| common.text_system.clone())
}
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
- self.keyboard_layout()
+ self.inner.keyboard_layout()
}
fn keyboard_mapper(&self) -> Rc<dyn PlatformKeyboardMapper> {
- Rc::new(crate::DummyKeyboardMapper)
+ Rc::new(gpui::DummyKeyboardMapper)
}
fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>) {
- self.with_common(|common| common.callbacks.keyboard_layout_change = Some(callback));
+ self.inner
+ .with_common(|common| common.callbacks.keyboard_layout_change = Some(callback));
}
fn on_thermal_state_change(&self, _callback: Box<dyn FnMut()>) {}
@@ -215,20 +232,22 @@ impl<P: LinuxClient + 'static> Platform for P {
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
on_finish_launching();
- LinuxClient::run(self);
+ LinuxClient::run(&self.inner);
- let quit = self.with_common(|common| common.callbacks.quit.take());
+ let quit = self
+ .inner
+ .with_common(|common| common.callbacks.quit.take());
if let Some(mut fun) = quit {
fun();
}
}
fn quit(&self) {
- self.with_common(|common| common.signal.stop());
+ self.inner.with_common(|common| common.signal.stop());
}
fn compositor_name(&self) -> &'static str {
- self.compositor_name()
+ self.inner.compositor_name()
}
fn restart(&self, binary_path: Option<PathBuf>) {
@@ -298,31 +317,31 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
- self.primary_display()
+ self.inner.primary_display()
}
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
- self.displays()
+ self.inner.displays()
}
#[cfg(feature = "screen-capture")]
fn is_screen_capture_supported(&self) -> bool {
- self.is_screen_capture_supported()
+ self.inner.is_screen_capture_supported()
}
#[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
- ) -> oneshot::Receiver<Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>> {
- self.screen_capture_sources()
+ ) -> oneshot::Receiver<Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>> {
+ self.inner.screen_capture_sources()
}
fn active_window(&self) -> Option<AnyWindowHandle> {
- self.active_window()
+ self.inner.active_window()
}
fn window_stack(&self) -> Option<Vec<AnyWindowHandle>> {
- self.window_stack()
+ self.inner.window_stack()
}
fn open_window(
@@ -330,15 +349,16 @@ impl<P: LinuxClient + 'static> Platform for P {
handle: AnyWindowHandle,
options: WindowParams,
) -> anyhow::Result<Box<dyn PlatformWindow>> {
- self.open_window(handle, options)
+ self.inner.open_window(handle, options)
}
fn open_url(&self, url: &str) {
- self.open_uri(url);
+ self.inner.open_uri(url);
}
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
- self.with_common(|common| common.callbacks.open_urls = Some(callback));
+ self.inner
+ .with_common(|common| common.callbacks.open_urls = Some(callback));
}
fn prompt_for_paths(
@@ -351,7 +371,7 @@ impl<P: LinuxClient + 'static> Platform for P {
let _ = (done_tx.send(Ok(None)), options);
#[cfg(any(feature = "wayland", feature = "x11"))]
- let identifier = self.window_identifier();
+ let identifier = self.inner.window_identifier();
#[cfg(any(feature = "wayland", feature = "x11"))]
self.foreground_executor()
@@ -366,7 +386,7 @@ impl<P: LinuxClient + 'static> Platform for P {
.identifier(identifier.await)
.modal(true)
.title(title)
- .accept_label(options.prompt.as_ref().map(crate::SharedString::as_str))
+ .accept_label(options.prompt.as_ref().map(gpui::SharedString::as_str))
.multiple(options.multiple)
.directory(options.directories)
.send()
@@ -411,7 +431,7 @@ impl<P: LinuxClient + 'static> Platform for P {
let _ = (done_tx.send(Ok(None)), directory, suggested_name);
#[cfg(any(feature = "wayland", feature = "x11"))]
- let identifier = self.window_identifier();
+ let identifier = self.inner.window_identifier();
#[cfg(any(feature = "wayland", feature = "x11"))]
self.foreground_executor()
@@ -468,7 +488,7 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn reveal_path(&self, path: &Path) {
- self.reveal_path(path.to_owned());
+ self.inner.reveal_path(path.to_owned());
}
fn open_with_system(&self, path: &Path) {
@@ -489,31 +509,31 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn on_quit(&self, callback: Box<dyn FnMut()>) {
- self.with_common(|common| {
+ self.inner.with_common(|common| {
common.callbacks.quit = Some(callback);
});
}
fn on_reopen(&self, callback: Box<dyn FnMut()>) {
- self.with_common(|common| {
+ self.inner.with_common(|common| {
common.callbacks.reopen = Some(callback);
});
}
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
- self.with_common(|common| {
+ self.inner.with_common(|common| {
common.callbacks.app_menu_action = Some(callback);
});
}
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
- self.with_common(|common| {
+ self.inner.with_common(|common| {
common.callbacks.will_open_app_menu = Some(callback);
});
}
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
- self.with_common(|common| {
+ self.inner.with_common(|common| {
common.callbacks.validate_app_menu_command = Some(callback);
});
}
@@ -525,13 +545,13 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn set_menus(&self, menus: Vec<Menu>, _keymap: &Keymap) {
- self.with_common(|common| {
+ self.inner.with_common(|common| {
common.menus = menus.into_iter().map(|menu| menu.owned()).collect();
})
}
fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
- self.with_common(|common| Some(common.menus.clone()))
+ self.inner.with_common(|common| Some(common.menus.clone()))
}
fn set_dock_menu(&self, _menu: Vec<MenuItem>, _keymap: &Keymap) {
@@ -545,11 +565,11 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn set_cursor_style(&self, style: CursorStyle) {
- self.set_cursor_style(style)
+ self.inner.set_cursor_style(style)
}
fn should_auto_hide_scrollbars(&self) -> bool {
- self.with_common(|common| common.auto_hide_scrollbars)
+ self.inner.with_common(|common| common.auto_hide_scrollbars)
}
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>> {
@@ -619,7 +639,7 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn window_appearance(&self) -> WindowAppearance {
- self.with_common(|common| common.appearance)
+ self.inner.with_common(|common| common.appearance)
}
fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
@@ -627,19 +647,19 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn write_to_primary(&self, item: ClipboardItem) {
- self.write_to_primary(item)
+ self.inner.write_to_primary(item)
}
fn write_to_clipboard(&self, item: ClipboardItem) {
- self.write_to_clipboard(item)
+ self.inner.write_to_clipboard(item)
}
fn read_from_primary(&self) -> Option<ClipboardItem> {
- self.read_from_primary()
+ self.inner.read_from_primary()
}
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
- self.read_from_clipboard()
+ self.inner.read_from_clipboard()
}
fn add_recent_document(&self, _path: &Path) {}
@@ -750,39 +770,37 @@ pub(super) unsafe fn read_fd(fd: filedescriptor::FileDescriptor) -> Result<Vec<u
#[cfg(any(feature = "wayland", feature = "x11"))]
pub(super) const DEFAULT_CURSOR_ICON_NAME: &str = "left_ptr";
-impl CursorStyle {
- #[cfg(any(feature = "wayland", feature = "x11"))]
- pub(super) fn to_icon_names(self) -> &'static [&'static str] {
- // Based on cursor names from chromium:
- // https://github.com/chromium/chromium/blob/d3069cf9c973dc3627fa75f64085c6a86c8f41bf/ui/base/cursor/cursor_factory.cc#L113
- match self {
- CursorStyle::Arrow => &[DEFAULT_CURSOR_ICON_NAME],
- CursorStyle::IBeam => &["text", "xterm"],
- CursorStyle::Crosshair => &["crosshair", "cross"],
- CursorStyle::ClosedHand => &["closedhand", "grabbing", "hand2"],
- CursorStyle::OpenHand => &["openhand", "grab", "hand1"],
- CursorStyle::PointingHand => &["pointer", "hand", "hand2"],
- CursorStyle::ResizeLeft => &["w-resize", "left_side"],
- CursorStyle::ResizeRight => &["e-resize", "right_side"],
- CursorStyle::ResizeLeftRight => &["ew-resize", "sb_h_double_arrow"],
- CursorStyle::ResizeUp => &["n-resize", "top_side"],
- CursorStyle::ResizeDown => &["s-resize", "bottom_side"],
- CursorStyle::ResizeUpDown => &["sb_v_double_arrow", "ns-resize"],
- CursorStyle::ResizeUpLeftDownRight => &["size_fdiag", "bd_double_arrow", "nwse-resize"],
- CursorStyle::ResizeUpRightDownLeft => &["size_bdiag", "nesw-resize", "fd_double_arrow"],
- CursorStyle::ResizeColumn => &["col-resize", "sb_h_double_arrow"],
- CursorStyle::ResizeRow => &["row-resize", "sb_v_double_arrow"],
- CursorStyle::IBeamCursorForVerticalLayout => &["vertical-text"],
- CursorStyle::OperationNotAllowed => &["not-allowed", "crossed_circle"],
- CursorStyle::DragLink => &["alias"],
- CursorStyle::DragCopy => &["copy"],
- CursorStyle::ContextualMenu => &["context-menu"],
- CursorStyle::None => {
- #[cfg(debug_assertions)]
- panic!("CursorStyle::None should be handled separately in the client");
- #[cfg(not(debug_assertions))]
- &[DEFAULT_CURSOR_ICON_NAME]
- }
+#[cfg(any(feature = "wayland", feature = "x11"))]
+pub(super) fn cursor_style_to_icon_names(style: CursorStyle) -> &'static [&'static str] {
+ // Based on cursor names from chromium:
+ // https://github.com/chromium/chromium/blob/d3069cf9c973dc3627fa75f64085c6a86c8f41bf/ui/base/cursor/cursor_factory.cc#L113
+ match style {
+ CursorStyle::Arrow => &[DEFAULT_CURSOR_ICON_NAME],
+ CursorStyle::IBeam => &["text", "xterm"],
+ CursorStyle::Crosshair => &["crosshair", "cross"],
+ CursorStyle::ClosedHand => &["closedhand", "grabbing", "hand2"],
+ CursorStyle::OpenHand => &["openhand", "grab", "hand1"],
+ CursorStyle::PointingHand => &["pointer", "hand", "hand2"],
+ CursorStyle::ResizeLeft => &["w-resize", "left_side"],
+ CursorStyle::ResizeRight => &["e-resize", "right_side"],
+ CursorStyle::ResizeLeftRight => &["ew-resize", "sb_h_double_arrow"],
+ CursorStyle::ResizeUp => &["n-resize", "top_side"],
+ CursorStyle::ResizeDown => &["s-resize", "bottom_side"],
+ CursorStyle::ResizeUpDown => &["sb_v_double_arrow", "ns-resize"],
+ CursorStyle::ResizeUpLeftDownRight => &["size_fdiag", "bd_double_arrow", "nwse-resize"],
+ CursorStyle::ResizeUpRightDownLeft => &["size_bdiag", "nesw-resize", "fd_double_arrow"],
+ CursorStyle::ResizeColumn => &["col-resize", "sb_h_double_arrow"],
+ CursorStyle::ResizeRow => &["row-resize", "sb_v_double_arrow"],
+ CursorStyle::IBeamCursorForVerticalLayout => &["vertical-text"],
+ CursorStyle::OperationNotAllowed => &["not-allowed", "crossed_circle"],
+ CursorStyle::DragLink => &["alias"],
+ CursorStyle::DragCopy => &["copy"],
+ CursorStyle::ContextualMenu => &["context-menu"],
+ CursorStyle::None => {
+ #[cfg(debug_assertions)]
+ panic!("CursorStyle::None should be handled separately in the client");
+ #[cfg(not(debug_assertions))]
+ &[DEFAULT_CURSOR_ICON_NAME]
}
}
}
@@ -856,222 +874,214 @@ fn guess_ascii(keycode: Keycode, shift: bool) -> Option<char> {
}
#[cfg(any(feature = "wayland", feature = "x11"))]
-impl crate::Keystroke {
- pub(super) fn from_xkb(
- state: &State,
- mut modifiers: crate::Modifiers,
- keycode: Keycode,
- ) -> Self {
- let key_utf32 = state.key_get_utf32(keycode);
- let key_utf8 = state.key_get_utf8(keycode);
- let key_sym = state.key_get_one_sym(keycode);
-
- let key = match key_sym {
- Keysym::Return => "enter".to_owned(),
- Keysym::Prior => "pageup".to_owned(),
- Keysym::Next => "pagedown".to_owned(),
- Keysym::ISO_Left_Tab => "tab".to_owned(),
- Keysym::KP_Prior => "pageup".to_owned(),
- Keysym::KP_Next => "pagedown".to_owned(),
- Keysym::XF86_Back => "back".to_owned(),
- Keysym::XF86_Forward => "forward".to_owned(),
- Keysym::XF86_Cut => "cut".to_owned(),
- Keysym::XF86_Copy => "copy".to_owned(),
- Keysym::XF86_Paste => "paste".to_owned(),
- Keysym::XF86_New => "new".to_owned(),
- Keysym::XF86_Open => "open".to_owned(),
- Keysym::XF86_Save => "save".to_owned(),
-
- Keysym::comma => ",".to_owned(),
- Keysym::period => ".".to_owned(),
- Keysym::less => "<".to_owned(),
- Keysym::greater => ">".to_owned(),
- Keysym::slash => "/".to_owned(),
- Keysym::question => "?".to_owned(),
-
- Keysym::semicolon => ";".to_owned(),
- Keysym::colon => ":".to_owned(),
- Keysym::apostrophe => "'".to_owned(),
- Keysym::quotedbl => "\"".to_owned(),
-
- Keysym::bracketleft => "[".to_owned(),
- Keysym::braceleft => "{".to_owned(),
- Keysym::bracketright => "]".to_owned(),
- Keysym::braceright => "}".to_owned(),
- Keysym::backslash => "\\".to_owned(),
- Keysym::bar => "|".to_owned(),
-
- Keysym::grave => "`".to_owned(),
- Keysym::asciitilde => "~".to_owned(),
- Keysym::exclam => "!".to_owned(),
- Keysym::at => "@".to_owned(),
- Keysym::numbersign => "#".to_owned(),
- Keysym::dollar => "$".to_owned(),
- Keysym::percent => "%".to_owned(),
- Keysym::asciicircum => "^".to_owned(),
- Keysym::ampersand => "&".to_owned(),
- Keysym::asterisk => "*".to_owned(),
- Keysym::parenleft => "(".to_owned(),
- Keysym::parenright => ")".to_owned(),
- Keysym::minus => "-".to_owned(),
- Keysym::underscore => "_".to_owned(),
- Keysym::equal => "=".to_owned(),
- Keysym::plus => "+".to_owned(),
- Keysym::space => "space".to_owned(),
- Keysym::BackSpace => "backspace".to_owned(),
- Keysym::Tab => "tab".to_owned(),
- Keysym::Delete => "delete".to_owned(),
- Keysym::Escape => "escape".to_owned(),
-
- Keysym::Left => "left".to_owned(),
- Keysym::Right => "right".to_owned(),
- Keysym::Up => "up".to_owned(),
- Keysym::Down => "down".to_owned(),
- Keysym::Home => "home".to_owned(),
- Keysym::End => "end".to_owned(),
- Keysym::Insert => "insert".to_owned(),
-
- _ => {
- let name = xkb::keysym_get_name(key_sym).to_lowercase();
- if key_sym.is_keypad_key() {
- name.replace("kp_", "")
- } else if let Some(key) = key_utf8.chars().next()
- && key_utf8.len() == 1
- && key.is_ascii()
+pub(super) fn keystroke_from_xkb(
+ state: &State,
+ mut modifiers: gpui::Modifiers,
+ keycode: Keycode,
+) -> gpui::Keystroke {
+ let key_utf32 = state.key_get_utf32(keycode);
+ let key_utf8 = state.key_get_utf8(keycode);
+ let key_sym = state.key_get_one_sym(keycode);
+
+ let key = match key_sym {
+ Keysym::Return => "enter".to_owned(),
+ Keysym::Prior => "pageup".to_owned(),
+ Keysym::Next => "pagedown".to_owned(),
+ Keysym::ISO_Left_Tab => "tab".to_owned(),
+ Keysym::KP_Prior => "pageup".to_owned(),
+ Keysym::KP_Next => "pagedown".to_owned(),
+ Keysym::XF86_Back => "back".to_owned(),
+ Keysym::XF86_Forward => "forward".to_owned(),
+ Keysym::XF86_Cut => "cut".to_owned(),
+ Keysym::XF86_Copy => "copy".to_owned(),
+ Keysym::XF86_Paste => "paste".to_owned(),
+ Keysym::XF86_New => "new".to_owned(),
+ Keysym::XF86_Open => "open".to_owned(),
+ Keysym::XF86_Save => "save".to_owned(),
+
+ Keysym::comma => ",".to_owned(),
+ Keysym::period => ".".to_owned(),
+ Keysym::less => "<".to_owned(),
+ Keysym::greater => ">".to_owned(),
+ Keysym::slash => "/".to_owned(),
+ Keysym::question => "?".to_owned(),
+
+ Keysym::semicolon => ";".to_owned(),
+ Keysym::colon => ":".to_owned(),
+ Keysym::apostrophe => "'".to_owned(),
+ Keysym::quotedbl => "\"".to_owned(),
+
+ Keysym::bracketleft => "[".to_owned(),
+ Keysym::braceleft => "{".to_owned(),
+ Keysym::bracketright => "]".to_owned(),
+ Keysym::braceright => "}".to_owned(),
+ Keysym::backslash => "\\".to_owned(),
+ Keysym::bar => "|".to_owned(),
+
+ Keysym::grave => "`".to_owned(),
+ Keysym::asciitilde => "~".to_owned(),
+ Keysym::exclam => "!".to_owned(),
+ Keysym::at => "@".to_owned(),
+ Keysym::numbersign => "#".to_owned(),
+ Keysym::dollar => "$".to_owned(),
+ Keysym::percent => "%".to_owned(),
+ Keysym::asciicircum => "^".to_owned(),
+ Keysym::ampersand => "&".to_owned(),
+ Keysym::asterisk => "*".to_owned(),
+ Keysym::parenleft => "(".to_owned(),
+ Keysym::parenright => ")".to_owned(),
+ Keysym::minus => "-".to_owned(),
+ Keysym::underscore => "_".to_owned(),
+ Keysym::equal => "=".to_owned(),
+ Keysym::plus => "+".to_owned(),
+ Keysym::space => "space".to_owned(),
+ Keysym::BackSpace => "backspace".to_owned(),
+ Keysym::Tab => "tab".to_owned(),
+ Keysym::Delete => "delete".to_owned(),
+ Keysym::Escape => "escape".to_owned(),
+
+ Keysym::Left => "left".to_owned(),
+ Keysym::Right => "right".to_owned(),
+ Keysym::Up => "up".to_owned(),
+ Keysym::Down => "down".to_owned(),
+ Keysym::Home => "home".to_owned(),
+ Keysym::End => "end".to_owned(),
+ Keysym::Insert => "insert".to_owned(),
+
+ _ => {
+ let name = xkb::keysym_get_name(key_sym).to_lowercase();
+ if key_sym.is_keypad_key() {
+ name.replace("kp_", "")
+ } else if let Some(key) = key_utf8.chars().next()
+ && key_utf8.len() == 1
+ && key.is_ascii()
+ {
+ if key.is_ascii_graphic() {
+ key_utf8.to_lowercase()
+ // map ctrl-a to `a`
+ // ctrl-0..9 may emit control codes like ctrl-[, but
+ // we don't want to map them to `[`
+ } else if key_utf32 <= 0x1f
+ && !name.chars().next().is_some_and(|c| c.is_ascii_digit())
{
- if key.is_ascii_graphic() {
- key_utf8.to_lowercase()
- // map ctrl-a to `a`
- // ctrl-0..9 may emit control codes like ctrl-[, but
- // we don't want to map them to `[`
- } else if key_utf32 <= 0x1f
- && !name.chars().next().is_some_and(|c| c.is_ascii_digit())
- {
- ((key_utf32 as u8 + 0x40) as char)
- .to_ascii_lowercase()
- .to_string()
- } else {
- name
- }
- } else if let Some(key_en) = guess_ascii(keycode, modifiers.shift) {
- String::from(key_en)
+ ((key_utf32 as u8 + 0x40) as char)
+ .to_ascii_lowercase()
+ .to_string()
} else {
name
}
- }
- };
-
- if modifiers.shift {
- // we only include the shift for upper-case letters by convention,
- // so don't include for numbers and symbols, but do include for
- // tab/enter, etc.
- if key.chars().count() == 1 && key.to_lowercase() == key.to_uppercase() {
- modifiers.shift = false;
+ } else if let Some(key_en) = guess_ascii(keycode, modifiers.shift) {
+ String::from(key_en)
+ } else {
+ name
}
}
+ };
- // Ignore control characters (and DEL) for the purposes of key_char
- let key_char =
- (key_utf32 >= 32 && key_utf32 != 127 && !key_utf8.is_empty()).then_some(key_utf8);
-
- Self {
- modifiers,
- key,
- key_char,
+ if modifiers.shift {
+ // we only include the shift for upper-case letters by convention,
+ // so don't include for numbers and symbols, but do include for
+ // tab/enter, etc.
+ if key.chars().count() == 1 && key.to_lowercase() == key.to_uppercase() {
+ modifiers.shift = false;
}
}
- /**
- * Returns which symbol the dead key represents
- * <https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#dead_keycodes_for_linux>
- */
- pub fn underlying_dead_key(keysym: Keysym) -> Option<String> {
- match keysym {
- Keysym::dead_grave => Some("`".to_owned()),
- Keysym::dead_acute => Some("´".to_owned()),
- Keysym::dead_circumflex => Some("^".to_owned()),
- Keysym::dead_tilde => Some("~".to_owned()),
- Keysym::dead_macron => Some("¯".to_owned()),
- Keysym::dead_breve => Some("˘".to_owned()),
- Keysym::dead_abovedot => Some("˙".to_owned()),
- Keysym::dead_diaeresis => Some("¨".to_owned()),
- Keysym::dead_abovering => Some("˚".to_owned()),
- Keysym::dead_doubleacute => Some("˝".to_owned()),
- Keysym::dead_caron => Some("ˇ".to_owned()),
- Keysym::dead_cedilla => Some("¸".to_owned()),
- Keysym::dead_ogonek => Some("˛".to_owned()),
- Keysym::dead_iota => Some("ͅ".to_owned()),
- Keysym::dead_voiced_sound => Some("゙".to_owned()),
- Keysym::dead_semivoiced_sound => Some("゚".to_owned()),
- Keysym::dead_belowdot => Some("̣̣".to_owned()),
- Keysym::dead_hook => Some("̡".to_owned()),
- Keysym::dead_horn => Some("̛".to_owned()),
- Keysym::dead_stroke => Some("̶̶".to_owned()),
- Keysym::dead_abovecomma => Some("̓̓".to_owned()),
- Keysym::dead_abovereversedcomma => Some("ʽ".to_owned()),
- Keysym::dead_doublegrave => Some("̏".to_owned()),
- Keysym::dead_belowring => Some("˳".to_owned()),
- Keysym::dead_belowmacron => Some("̱".to_owned()),
- Keysym::dead_belowcircumflex => Some("ꞈ".to_owned()),
- Keysym::dead_belowtilde => Some("̰".to_owned()),
- Keysym::dead_belowbreve => Some("̮".to_owned()),
- Keysym::dead_belowdiaeresis => Some("̤".to_owned()),
- Keysym::dead_invertedbreve => Some("̯".to_owned()),
- Keysym::dead_belowcomma => Some("̦".to_owned()),
- Keysym::dead_currency => None,
- Keysym::dead_lowline => None,
- Keysym::dead_aboveverticalline => None,
- Keysym::dead_belowverticalline => None,
- Keysym::dead_longsolidusoverlay => None,
- Keysym::dead_a => None,
- Keysym::dead_A => None,
- Keysym::dead_e => None,
- Keysym::dead_E => None,
- Keysym::dead_i => None,
- Keysym::dead_I => None,
- Keysym::dead_o => None,
- Keysym::dead_O => None,
- Keysym::dead_u => None,
- Keysym::dead_U => None,
- Keysym::dead_small_schwa => Some("ə".to_owned()),
- Keysym::dead_capital_schwa => Some("Ə".to_owned()),
- Keysym::dead_greek => None,
- _ => None,
- }
+ // Ignore control characters (and DEL) for the purposes of key_char
+ let key_char =
+ (key_utf32 >= 32 && key_utf32 != 127 && !key_utf8.is_empty()).then_some(key_utf8);
+
+ gpui::Keystroke {
+ modifiers,
+ key,
+ key_char,
}
}
+/**
+ * Returns which symbol the dead key represents
+ * <https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#dead_keycodes_for_linux>
+ */
#[cfg(any(feature = "wayland", feature = "x11"))]
-impl crate::Modifiers {
- pub(super) fn from_xkb(keymap_state: &State) -> Self {
- let shift = keymap_state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE);
- let alt = keymap_state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE);
- let control =
- keymap_state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE);
- let platform =
- keymap_state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE);
- Self {
- shift,
- alt,
- control,
- platform,
- function: false,
- }
+pub fn keystroke_underlying_dead_key(keysym: Keysym) -> Option<String> {
+ match keysym {
+ Keysym::dead_grave => Some("`".to_owned()),
+ Keysym::dead_acute => Some("´".to_owned()),
+ Keysym::dead_circumflex => Some("^".to_owned()),
+ Keysym::dead_tilde => Some("~".to_owned()),
+ Keysym::dead_macron => Some("¯".to_owned()),
+ Keysym::dead_breve => Some("˘".to_owned()),
+ Keysym::dead_abovedot => Some("˙".to_owned()),
+ Keysym::dead_diaeresis => Some("¨".to_owned()),
+ Keysym::dead_abovering => Some("˚".to_owned()),
+ Keysym::dead_doubleacute => Some("˝".to_owned()),
+ Keysym::dead_caron => Some("ˇ".to_owned()),
+ Keysym::dead_cedilla => Some("¸".to_owned()),
+ Keysym::dead_ogonek => Some("˛".to_owned()),
+ Keysym::dead_iota => Some("ͅ".to_owned()),
+ Keysym::dead_voiced_sound => Some("゙".to_owned()),
+ Keysym::dead_semivoiced_sound => Some("゚".to_owned()),
+ Keysym::dead_belowdot => Some("̣̣".to_owned()),
+ Keysym::dead_hook => Some("̡".to_owned()),
+ Keysym::dead_horn => Some("̛".to_owned()),
+ Keysym::dead_stroke => Some("̶̶".to_owned()),
+ Keysym::dead_abovecomma => Some("̓̓".to_owned()),
+ Keysym::dead_abovereversedcomma => Some("ʽ".to_owned()),
+ Keysym::dead_doublegrave => Some("̏".to_owned()),
+ Keysym::dead_belowring => Some("˳".to_owned()),
+ Keysym::dead_belowmacron => Some("̱".to_owned()),
+ Keysym::dead_belowcircumflex => Some("ꞈ".to_owned()),
+ Keysym::dead_belowtilde => Some("̰".to_owned()),
+ Keysym::dead_belowbreve => Some("̮".to_owned()),
+ Keysym::dead_belowdiaeresis => Some("̤".to_owned()),
+ Keysym::dead_invertedbreve => Some("̯".to_owned()),
+ Keysym::dead_belowcomma => Some("̦".to_owned()),
+ Keysym::dead_currency => None,
+ Keysym::dead_lowline => None,
+ Keysym::dead_aboveverticalline => None,
+ Keysym::dead_belowverticalline => None,
+ Keysym::dead_longsolidusoverlay => None,
+ Keysym::dead_a => None,
+ Keysym::dead_A => None,
+ Keysym::dead_e => None,
+ Keysym::dead_E => None,
+ Keysym::dead_i => None,
+ Keysym::dead_I => None,
+ Keysym::dead_o => None,
+ Keysym::dead_O => None,
+ Keysym::dead_u => None,
+ Keysym::dead_U => None,
+ Keysym::dead_small_schwa => Some("ə".to_owned()),
+ Keysym::dead_capital_schwa => Some("Ə".to_owned()),
+ Keysym::dead_greek => None,
+ _ => None,
}
}
-
#[cfg(any(feature = "wayland", feature = "x11"))]
-impl crate::Capslock {
- pub(super) fn from_xkb(keymap_state: &State) -> Self {
- let on = keymap_state.mod_name_is_active(xkb::MOD_NAME_CAPS, xkb::STATE_MODS_EFFECTIVE);
- Self { on }
+pub(super) fn modifiers_from_xkb(keymap_state: &State) -> gpui::Modifiers {
+ let shift = keymap_state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE);
+ let alt = keymap_state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE);
+ let control = keymap_state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE);
+ let platform = keymap_state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE);
+ gpui::Modifiers {
+ shift,
+ alt,
+ control,
+ platform,
+ function: false,
}
}
+#[cfg(any(feature = "wayland", feature = "x11"))]
+pub(super) fn capslock_from_xkb(keymap_state: &State) -> gpui::Capslock {
+ let on = keymap_state.mod_name_is_active(xkb::MOD_NAME_CAPS, xkb::STATE_MODS_EFFECTIVE);
+ gpui::Capslock { on }
+}
+
#[cfg(test)]
mod tests {
use super::*;
- use crate::{Point, px};
+ use gpui::{Point, px};
#[test]
fn test_is_within_click_distance() {
@@ -1,22 +1,17 @@
-use crate::{
- Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontRun, FontStyle, FontWeight,
- GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RenderGlyphParams, SUBPIXEL_VARIANTS_X,
- SUBPIXEL_VARIANTS_Y, ShapedGlyph, ShapedRun, SharedString, Size, TextRenderingMode, point,
- size,
-};
use anyhow::{Context as _, Ok, Result};
use collections::HashMap;
use cosmic_text::{
Attrs, AttrsList, Family, Font as CosmicTextFont, FontFeatures as CosmicFontFeatures,
FontSystem, ShapeBuffer, ShapeLine,
};
+use gpui::{
+ Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontRun, GlyphId, LineLayout,
+ Pixels, PlatformTextSystem, RenderGlyphParams, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y,
+ ShapedGlyph, ShapedRun, SharedString, Size, TextRenderingMode, point, size,
+};
use itertools::Itertools;
use parking_lot::RwLock;
-use pathfinder_geometry::{
- rect::{RectF, RectI},
- vector::{Vector2F, Vector2I},
-};
use smallvec::SmallVec;
use std::{borrow::Cow, sync::Arc};
use swash::{
@@ -58,7 +53,7 @@ struct LoadedFont {
impl CosmicTextSystem {
pub(crate) fn new() -> Self {
// todo(linux) make font loading non-blocking
- let mut font_system = FontSystem::new();
+ let font_system = FontSystem::new();
Self(RwLock::new(CosmicTextSystemState {
font_system,
@@ -227,7 +222,7 @@ impl CosmicTextSystemState {
features: &FontFeatures,
) -> Result<SmallVec<[FontId; 4]>> {
// TODO: Determine the proper system UI font.
- let name = crate::text_system::font_name_with_fallbacks(name, "IBM Plex Sans");
+ let name = gpui::font_name_with_fallbacks(name, "IBM Plex Sans");
let families = self
.font_system
@@ -261,7 +256,7 @@ impl CosmicTextSystemState {
loaded_font_ids.push(font_id);
self.loaded_fonts.push(LoadedFont {
font,
- features: features.try_into()?,
+ features: cosmic_font_features(features)?,
is_known_emoji_font: check_is_known_emoji_font(&postscript_name),
});
}
@@ -324,7 +319,7 @@ impl CosmicTextSystemState {
) -> Result<swash::scale::image::Image> {
let loaded_font = &self.loaded_fonts[params.font_id.0];
let font_ref = loaded_font.font.as_swash();
- let pixel_size = params.font_size.0;
+ let pixel_size = f32::from(params.font_size);
let subpixel_offset = Vector::new(
params.subpixel_variant.x as f32 / SUBPIXEL_VARIANTS_X as f32 / params.scale_factor,
@@ -428,7 +423,7 @@ impl CosmicTextSystemState {
let mut layout_lines = Vec::with_capacity(1);
line.layout_to_buffer(
&mut self.scratch,
- font_size.0,
+ f32::from(font_size),
None, // We do our own wrapping
cosmic_text::Wrap::None,
None,
@@ -484,93 +479,28 @@ impl CosmicTextSystemState {
}
}
-impl TryFrom<&FontFeatures> for CosmicFontFeatures {
- type Error = anyhow::Error;
-
- fn try_from(features: &FontFeatures) -> Result<Self> {
- let mut result = CosmicFontFeatures::new();
- for feature in features.0.iter() {
- let name_bytes: [u8; 4] = feature
- .0
- .as_bytes()
- .try_into()
- .context("Incorrect feature flag format")?;
-
- let tag = cosmic_text::FeatureTag::new(&name_bytes);
-
- result.set(tag, feature.1);
- }
- Ok(result)
- }
-}
-
-impl From<RectF> for Bounds<f32> {
- fn from(rect: RectF) -> Self {
- Bounds {
- origin: point(rect.origin_x(), rect.origin_y()),
- size: size(rect.width(), rect.height()),
- }
- }
-}
-
-impl From<RectI> for Bounds<DevicePixels> {
- fn from(rect: RectI) -> Self {
- Bounds {
- origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())),
- size: size(DevicePixels(rect.width()), DevicePixels(rect.height())),
- }
- }
-}
-
-impl From<Vector2I> for Size<DevicePixels> {
- fn from(value: Vector2I) -> Self {
- size(value.x().into(), value.y().into())
- }
-}
-
-impl From<RectI> for Bounds<i32> {
- fn from(rect: RectI) -> Self {
- Bounds {
- origin: point(rect.origin_x(), rect.origin_y()),
- size: size(rect.width(), rect.height()),
- }
- }
-}
-
-impl From<Point<u32>> for Vector2I {
- fn from(size: Point<u32>) -> Self {
- Vector2I::new(size.x as i32, size.y as i32)
- }
-}
-
-impl From<Vector2F> for Size<f32> {
- fn from(vec: Vector2F) -> Self {
- size(vec.x(), vec.y())
- }
-}
+fn cosmic_font_features(features: &FontFeatures) -> Result<CosmicFontFeatures> {
+ let mut result = CosmicFontFeatures::new();
+ for feature in features.0.iter() {
+ let name_bytes: [u8; 4] = feature
+ .0
+ .as_bytes()
+ .try_into()
+ .context("Incorrect feature flag format")?;
-impl From<FontWeight> for cosmic_text::Weight {
- fn from(value: FontWeight) -> Self {
- cosmic_text::Weight(value.0 as u16)
- }
-}
+ let tag = cosmic_text::FeatureTag::new(&name_bytes);
-impl From<FontStyle> for cosmic_text::Style {
- fn from(style: FontStyle) -> Self {
- match style {
- FontStyle::Normal => cosmic_text::Style::Normal,
- FontStyle::Italic => cosmic_text::Style::Italic,
- FontStyle::Oblique => cosmic_text::Style::Oblique,
- }
+ result.set(tag, feature.1);
}
+ Ok(result)
}
-fn font_into_properties(font: &crate::Font) -> font_kit::properties::Properties {
+fn font_into_properties(font: &gpui::Font) -> font_kit::properties::Properties {
font_kit::properties::Properties {
style: match font.style {
- crate::FontStyle::Normal => font_kit::properties::Style::Normal,
- crate::FontStyle::Italic => font_kit::properties::Style::Italic,
- crate::FontStyle::Oblique => font_kit::properties::Style::Oblique,
+ gpui::FontStyle::Normal => font_kit::properties::Style::Normal,
+ gpui::FontStyle::Italic => font_kit::properties::Style::Italic,
+ gpui::FontStyle::Oblique => font_kit::properties::Style::Oblique,
},
weight: font_kit::properties::Weight(font.weight.0),
stretch: Default::default(),
@@ -0,0 +1,47 @@
+mod client;
+mod clipboard;
+mod cursor;
+mod display;
+mod serial;
+mod window;
+
+/// Contains Types for configuring layer_shell surfaces.
+pub mod layer_shell;
+
+pub(crate) use client::*;
+
+use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
+
+use gpui::CursorStyle;
+
+pub(super) fn to_shape(style: CursorStyle) -> Shape {
+ match style {
+ CursorStyle::Arrow => Shape::Default,
+ CursorStyle::IBeam => Shape::Text,
+ CursorStyle::Crosshair => Shape::Crosshair,
+ CursorStyle::ClosedHand => Shape::Grabbing,
+ CursorStyle::OpenHand => Shape::Grab,
+ CursorStyle::PointingHand => Shape::Pointer,
+ CursorStyle::ResizeLeft => Shape::WResize,
+ CursorStyle::ResizeRight => Shape::EResize,
+ CursorStyle::ResizeLeftRight => Shape::EwResize,
+ CursorStyle::ResizeUp => Shape::NResize,
+ CursorStyle::ResizeDown => Shape::SResize,
+ CursorStyle::ResizeUpDown => Shape::NsResize,
+ CursorStyle::ResizeUpLeftDownRight => Shape::NwseResize,
+ CursorStyle::ResizeUpRightDownLeft => Shape::NeswResize,
+ CursorStyle::ResizeColumn => Shape::ColResize,
+ CursorStyle::ResizeRow => Shape::RowResize,
+ CursorStyle::IBeamCursorForVerticalLayout => Shape::VerticalText,
+ CursorStyle::OperationNotAllowed => Shape::NotAllowed,
+ CursorStyle::DragLink => Shape::Alias,
+ CursorStyle::DragCopy => Shape::Copy,
+ CursorStyle::ContextualMenu => Shape::ContextMenu,
+ CursorStyle::None => {
+ #[cfg(debug_assertions)]
+ panic!("CursorStyle::None should be handled separately in the client");
+ #[cfg(not(debug_assertions))]
+ Shape::Default
+ }
+ }
+}
@@ -73,32 +73,29 @@ use super::{
window::{ImeInput, WaylandWindowStatePtr},
};
-use crate::{
- AnyWindowHandle, Bounds, Capslock, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId,
- FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon,
- LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
- MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay,
- PlatformInput, PlatformKeyboardLayout, Point, ResultExt as _, SCROLL_LINES, ScrollDelta,
- ScrollWheelEvent, Size, TouchPhase, WindowParams, point, profiler, px, size,
-};
-use crate::{
- SharedString,
- platform::linux::{
- LinuxClient, get_xkb_compose_state, is_within_click_distance, open_uri_internal, read_fd,
- reveal_path_internal,
- wayland::{
- clipboard::{Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPES},
- cursor::Cursor,
- serial::{SerialKind, SerialTracker},
- window::WaylandWindow,
- },
- xdg_desktop_portal::{Event as XDPEvent, XDPEventSource},
+use crate::linux::{
+ DOUBLE_CLICK_INTERVAL, LinuxClient, LinuxCommon, LinuxKeyboardLayout, ResultExt as _,
+ SCROLL_LINES, capslock_from_xkb, cursor_style_to_icon_names, get_xkb_compose_state,
+ is_within_click_distance, keystroke_from_xkb, keystroke_underlying_dead_key,
+ modifiers_from_xkb, open_uri_internal, read_fd, reveal_path_internal,
+ wayland::{
+ clipboard::{Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPES},
+ cursor::Cursor,
+ serial::{SerialKind, SerialTracker},
+ to_shape,
+ window::WaylandWindow,
},
+ xdg_desktop_portal::{Event as XDPEvent, XDPEventSource},
};
-use crate::{
- TaskTiming,
- platform::{PlatformWindow, wgpu::WgpuContext},
+use gpui::{
+ AnyWindowHandle, Bounds, Capslock, CursorStyle, DevicePixels, DisplayId, FileDropEvent,
+ ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
+ MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection,
+ Pixels, PlatformDisplay, PlatformInput, PlatformKeyboardLayout, PlatformWindow, Point,
+ ScrollDelta, ScrollWheelEvent, SharedString, Size, TaskTiming, TouchPhase, WindowParams, point,
+ profiler, px, size,
};
+use gpui_wgpu::WgpuContext;
/// Used to convert evdev scancode to xkb scancode
const MIN_KEYCODE: u32 = 8;
@@ -303,7 +300,7 @@ impl WaylandClientStatePtr {
pub fn enable_ime(&self) {
let client = self.get_client();
let mut state = client.borrow_mut();
- let Some(mut text_input) = state.text_input.take() else {
+ let Some(text_input) = state.text_input.take() else {
return;
};
@@ -313,10 +310,10 @@ impl WaylandClientStatePtr {
drop(state);
if let Some(area) = window.get_ime_area() {
text_input.set_cursor_rectangle(
- area.origin.x.0 as i32,
- area.origin.y.0 as i32,
- area.size.width.0 as i32,
- area.size.height.0 as i32,
+ f32::from(area.origin.x) as i32,
+ f32::from(area.origin.y) as i32,
+ f32::from(area.size.width) as i32,
+ f32::from(area.size.height) as i32,
);
}
state = client.borrow_mut();
@@ -337,17 +334,17 @@ impl WaylandClientStatePtr {
pub fn update_ime_position(&self, bounds: Bounds<Pixels>) {
let client = self.get_client();
- let mut state = client.borrow_mut();
+ let state = client.borrow_mut();
if state.composing || state.text_input.is_none() || state.pre_edit_text.is_some() {
return;
}
let text_input = state.text_input.as_ref().unwrap();
text_input.set_cursor_rectangle(
- bounds.origin.x.0 as i32,
- bounds.origin.y.0 as i32,
- bounds.size.width.0 as i32,
- bounds.size.height.0 as i32,
+ bounds.origin.x.as_f32() as i32,
+ bounds.origin.y.as_f32() as i32,
+ bounds.size.width.as_f32() as i32,
+ bounds.size.height.as_f32() as i32,
);
text_input.commit();
}
@@ -382,7 +379,7 @@ impl WaylandClientStatePtr {
}
pub fn drop_window(&self, surface_id: &ObjectId) {
- let mut client = self.get_client();
+ let client = self.get_client();
let mut state = client.borrow_mut();
let closed_window = state.windows.remove(surface_id).unwrap();
if let Some(window) = state.mouse_focused_window.take()
@@ -456,8 +453,7 @@ impl WaylandClient {
pub(crate) fn new() -> Self {
let conn = Connection::connect_to_env().unwrap();
- let (globals, mut event_queue) =
- registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
+ let (globals, event_queue) = registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
let qh = event_queue.handle();
let mut seat: Option<wl_seat::WlSeat> = None;
@@ -540,7 +536,7 @@ impl WaylandClient {
.as_ref()
.map(|primary_selection_manager| primary_selection_manager.get_device(&seat, &qh, ()));
- let mut cursor = Cursor::new(&conn, &globals, 24);
+ let cursor = Cursor::new(&conn, &globals, 24);
handle
.insert_source(XDPEventSource::new(&common.background_executor), {
@@ -572,7 +568,7 @@ impl WaylandClient {
})
.unwrap();
- let mut state = Rc::new(RefCell::new(WaylandClientState {
+ let state = Rc::new(RefCell::new(WaylandClientState {
serial_tracker: SerialTracker::new(),
globals,
gpu_context,
@@ -673,7 +669,7 @@ impl LinuxClient for WaylandClient {
.outputs
.iter()
.find_map(|(object_id, output)| {
- (object_id.protocol_id() == id.0).then(|| {
+ (object_id.protocol_id() == u32::from(id)).then(|| {
Rc::new(WaylandDisplay {
id: object_id.clone(),
name: output.name.clone(),
@@ -695,7 +691,7 @@ impl LinuxClient for WaylandClient {
#[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
- ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>>
+ ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>>
{
// TODO: Get screen capture working on wayland. Be sure to try window resizing as that may
// be tricky.
@@ -754,7 +750,7 @@ impl LinuxClient for WaylandClient {
.expect("window is focused by pointer");
wl_pointer.set_cursor(serial, None, 0, 0);
} else if let Some(cursor_shape_device) = &state.cursor_shape_device {
- cursor_shape_device.set_shape(serial, style.to_shape());
+ cursor_shape_device.set_shape(serial, to_shape(style));
} else if let Some(focused_window) = &state.mouse_focused_window {
// cursor-shape-v1 isn't supported, set the cursor using a surface.
let wl_pointer = state
@@ -762,9 +758,12 @@ impl LinuxClient for WaylandClient {
.clone()
.expect("window is focused by pointer");
let scale = focused_window.primary_output_scale();
- state
- .cursor
- .set_icon(&wl_pointer, serial, style.to_icon_names(), scale);
+ state.cursor.set_icon(
+ &wl_pointer,
+ serial,
+ cursor_style_to_icon_names(style),
+ scale,
+ );
}
}
}
@@ -826,7 +825,7 @@ impl LinuxClient for WaylandClient {
.log_err();
}
- fn write_to_primary(&self, item: crate::ClipboardItem) {
+ fn write_to_primary(&self, item: gpui::ClipboardItem) {
let mut state = self.0.borrow_mut();
let (Some(primary_selection_manager), Some(primary_selection)) = (
state.globals.primary_selection_manager.clone(),
@@ -846,7 +845,7 @@ impl LinuxClient for WaylandClient {
}
}
- fn write_to_clipboard(&self, item: crate::ClipboardItem) {
+ fn write_to_clipboard(&self, item: gpui::ClipboardItem) {
let mut state = self.0.borrow_mut();
let (Some(data_device_manager), Some(data_device)) = (
state.globals.data_device_manager.clone(),
@@ -866,11 +865,11 @@ impl LinuxClient for WaylandClient {
}
}
- fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
+ fn read_from_primary(&self) -> Option<gpui::ClipboardItem> {
self.0.borrow_mut().clipboard.read_primary()
}
- fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
+ fn read_from_clipboard(&self) -> Option<gpui::ClipboardItem> {
self.0.borrow_mut().clipboard.read()
}
@@ -914,7 +913,7 @@ impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStat
_: &Connection,
qh: &QueueHandle<Self>,
) {
- let mut client = this.get_client();
+ let client = this.get_client();
let mut state = client.borrow_mut();
match event {
@@ -1002,7 +1001,7 @@ impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
}
pub(crate) fn get_window(
- mut state: &mut RefMut<WaylandClientState>,
+ state: &mut RefMut<WaylandClientState>,
surface_id: &ObjectId,
) -> Option<WaylandWindowStatePtr> {
state.windows.get(surface_id).cloned()
@@ -1017,7 +1016,7 @@ impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut client = this.get_client();
+ let client = this.get_client();
let mut state = client.borrow_mut();
let Some(window) = get_window(&mut state, &surface.id()) else {
@@ -1040,10 +1039,10 @@ impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut client = this.get_client();
+ let client = this.get_client();
let mut state = client.borrow_mut();
- let Some(mut in_progress_output) = state.in_progress_outputs.get_mut(&output.id()) else {
+ let Some(in_progress_output) = state.in_progress_outputs.get_mut(&output.id()) else {
return;
};
@@ -1257,7 +1256,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut client = this.get_client();
+ let client = this.get_client();
let mut state = client.borrow_mut();
match event {
wl_keyboard::Event::RepeatInfo { rate, delay } => {
@@ -1332,9 +1331,9 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
let old_layout =
keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
- state.modifiers = Modifiers::from_xkb(keymap_state);
+ state.modifiers = modifiers_from_xkb(keymap_state);
let keymap_state = state.keymap_state.as_mut().unwrap();
- state.capslock = Capslock::from_xkb(keymap_state);
+ state.capslock = capslock_from_xkb(keymap_state);
let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
modifiers: state.modifiers,
@@ -1370,14 +1369,14 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
match key_state {
wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
let mut keystroke =
- Keystroke::from_xkb(keymap_state, state.modifiers, keycode);
+ keystroke_from_xkb(keymap_state, state.modifiers, keycode);
if let Some(mut compose) = state.compose_state.take() {
compose.feed(keysym);
match compose.status() {
xkb::Status::Composing => {
keystroke.key_char = None;
state.pre_edit_text =
- compose.utf8().or(Keystroke::underlying_dead_key(keysym));
+ compose.utf8().or(keystroke_underlying_dead_key(keysym));
let pre_edit =
state.pre_edit_text.clone().unwrap_or(String::default());
drop(state);
@@ -1394,7 +1393,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
}
xkb::Status::Cancelled => {
let pre_edit = state.pre_edit_text.take();
- let new_pre_edit = Keystroke::underlying_dead_key(keysym);
+ let new_pre_edit = keystroke_underlying_dead_key(keysym);
state.pre_edit_text = new_pre_edit.clone();
drop(state);
if let Some(pre_edit) = pre_edit {
@@ -1432,8 +1431,8 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
prefer_character_input: false,
});
move |event_timestamp, _metadata, this| {
- let mut client = this.get_client();
- let mut state = client.borrow_mut();
+ let client = this.get_client();
+ let state = client.borrow();
let is_repeating = id == state.repeat.current_id
&& state.repeat.current_keycode.is_some()
&& state.keyboard_focused_window.is_some();
@@ -1459,7 +1458,7 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
}
wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
let input = PlatformInput::KeyUp(KeyUpEvent {
- keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
+ keystroke: keystroke_from_xkb(keymap_state, state.modifiers, keycode),
});
if state.repeat.current_keycode == Some(keycode) {
@@ -1538,10 +1537,10 @@ impl Dispatch<zwp_text_input_v3::ZwpTextInputV3, ()> for WaylandClientStatePtr {
window.handle_ime(ImeInput::SetMarkedText(text));
if let Some(area) = window.get_ime_area() {
text_input.set_cursor_rectangle(
- area.origin.x.0 as i32,
- area.origin.y.0 as i32,
- area.size.width.0 as i32,
- area.size.height.0 as i32,
+ f32::from(area.origin.x) as i32,
+ f32::from(area.origin.y) as i32,
+ f32::from(area.size.width) as i32,
+ f32::from(area.size.height) as i32,
);
if last_serial == serial {
text_input.commit();
@@ -1587,7 +1586,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
_: &Connection,
_: &QueueHandle<Self>,
) {
- let mut client = this.get_client();
+ let client = this.get_client();
let mut state = client.borrow_mut();
match event {
@@ -1616,12 +1615,15 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
.expect("window is focused by pointer");
wl_pointer.set_cursor(serial, None, 0, 0);
} else if let Some(cursor_shape_device) = &state.cursor_shape_device {
- cursor_shape_device.set_shape(serial, style.to_shape());
+ cursor_shape_device.set_shape(serial, to_shape(style));
} else {
let scale = window.primary_output_scale();
- state
- .cursor
- .set_icon(wl_pointer, serial, style.to_icon_names(), scale);
+ state.cursor.set_icon(
+ wl_pointer,
+ serial,
+ cursor_style_to_icon_names(style),
+ scale,
+ );
}
}
drop(state);
@@ -1662,7 +1664,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
state.cursor_style = Some(default_style);
if let Some(cursor_shape_device) = &state.cursor_shape_device {
- cursor_shape_device.set_shape(serial, default_style.to_shape());
+ cursor_shape_device.set_shape(serial, to_shape(default_style));
} else {
// cursor-shape-v1 isn't supported, set the cursor using a surface.
let wl_pointer = state
@@ -1673,7 +1675,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
state.cursor.set_icon(
&wl_pointer,
serial,
- default_style.to_icon_names(),
+ cursor_style_to_icon_names(default_style),
scale,
);
}
@@ -2043,7 +2045,7 @@ impl Dispatch<wl_data_device::WlDataDevice, ()> for WaylandClientStatePtr {
let input = PlatformInput::FileDrop(FileDropEvent::Entered {
position,
- paths: crate::ExternalPaths(paths),
+ paths: gpui::ExternalPaths(paths),
});
let client = this.get_client();
@@ -2151,7 +2153,7 @@ impl Dispatch<wl_data_source::WlDataSource, ()> for WaylandClientStatePtr {
_: &QueueHandle<Self>,
) {
let client = this.get_client();
- let mut state = client.borrow_mut();
+ let state = client.borrow_mut();
match event {
wl_data_source::Event::Send { mime_type, fd } => {
@@ -2237,7 +2239,7 @@ impl Dispatch<zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1, ()>
_: &QueueHandle<Self>,
) {
let client = this.get_client();
- let mut state = client.borrow_mut();
+ let state = client.borrow_mut();
match event {
zwp_primary_selection_source_v1::Event::Send { mime_type, fd } => {
@@ -10,10 +10,8 @@ use strum::IntoEnumIterator;
use wayland_client::{Connection, protocol::wl_data_offer::WlDataOffer};
use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_offer_v1::ZwpPrimarySelectionOfferV1;
-use crate::{
- ClipboardEntry, ClipboardItem, Image, ImageFormat, WaylandClientStatePtr, hash,
- platform::linux::platform::read_fd,
-};
+use crate::linux::{WaylandClientStatePtr, platform::read_fd};
+use gpui::{ClipboardEntry, ClipboardItem, Image, ImageFormat, hash};
/// Text mime types that we'll offer to other programs.
pub(crate) const TEXT_MIME_TYPES: [&str; 3] =
@@ -241,7 +239,7 @@ impl Clipboard {
calloop::Mode::Level,
),
move |_, file, _| {
- let mut file = unsafe { file.get_mut() };
+ let file = unsafe { file.get_mut() };
loop {
match file.write(&bytes[written..]) {
Ok(n) if written + n == bytes.len() => {
@@ -1,5 +1,5 @@
-use crate::Globals;
-use crate::platform::linux::{DEFAULT_CURSOR_ICON_NAME, log_cursor_icon_warning};
+use crate::linux::Globals;
+use crate::linux::{DEFAULT_CURSOR_ICON_NAME, log_cursor_icon_warning};
use anyhow::{Context as _, anyhow};
use util::ResultExt;
@@ -95,7 +95,7 @@ impl Cursor {
&mut self,
wl_pointer: &WlPointer,
serial_id: u32,
- mut cursor_icon_names: &[&str],
+ cursor_icon_names: &[&str],
scale: i32,
) {
self.set_scaled_size(self.size * scale as u32);
@@ -104,9 +104,9 @@ impl Cursor {
log::warn!("Wayland: Unable to load cursor themes");
return;
};
- let mut theme = &mut loaded_theme.theme;
+ let theme = &mut loaded_theme.theme;
- let mut buffer: &CursorImageBuffer;
+ let buffer: &CursorImageBuffer;
'outer: {
for cursor_icon_name in cursor_icon_names {
if let Some(cursor) = theme.get_cursor(cursor_icon_name) {
@@ -7,7 +7,7 @@ use anyhow::Context as _;
use uuid::Uuid;
use wayland_backend::client::ObjectId;
-use crate::{Bounds, DisplayId, Pixels, PlatformDisplay};
+use gpui::{Bounds, DisplayId, Pixels, PlatformDisplay};
#[derive(Debug, Clone)]
pub(crate) struct WaylandDisplay {
@@ -25,7 +25,7 @@ impl Hash for WaylandDisplay {
impl PlatformDisplay for WaylandDisplay {
fn id(&self) -> DisplayId {
- DisplayId(self.id.protocol_id())
+ DisplayId::new(self.id.protocol_id())
}
fn uuid(&self) -> anyhow::Result<Uuid> {
@@ -0,0 +1,26 @@
+pub use gpui::layer_shell::*;
+
+use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
+
+pub(crate) fn wayland_layer(layer: Layer) -> zwlr_layer_shell_v1::Layer {
+ match layer {
+ Layer::Background => zwlr_layer_shell_v1::Layer::Background,
+ Layer::Bottom => zwlr_layer_shell_v1::Layer::Bottom,
+ Layer::Top => zwlr_layer_shell_v1::Layer::Top,
+ Layer::Overlay => zwlr_layer_shell_v1::Layer::Overlay,
+ }
+}
+
+pub(crate) fn wayland_anchor(anchor: Anchor) -> zwlr_layer_surface_v1::Anchor {
+ zwlr_layer_surface_v1::Anchor::from_bits_truncate(anchor.bits())
+}
+
+pub(crate) fn wayland_keyboard_interactivity(
+ value: KeyboardInteractivity,
+) -> zwlr_layer_surface_v1::KeyboardInteractivity {
+ match value {
+ KeyboardInteractivity::None => zwlr_layer_surface_v1::KeyboardInteractivity::None,
+ KeyboardInteractivity::Exclusive => zwlr_layer_surface_v1::KeyboardInteractivity::Exclusive,
+ KeyboardInteractivity::OnDemand => zwlr_layer_surface_v1::KeyboardInteractivity::OnDemand,
+ }
+}
@@ -24,27 +24,22 @@ use wayland_protocols::{
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
use wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1;
-use crate::{
- AnyWindowHandle, Bounds, Decorations, DevicePixels, Globals, GpuSpecs, Modifiers, Output,
- Pixels, PlatformDisplay, PlatformInput, Point, PromptButton, PromptLevel, RequestFrameOptions,
- ResizeEdge, Size, Tiling, WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance,
- WindowBounds, WindowControlArea, WindowControls, WindowDecorations, WindowParams, get_window,
- layer_shell::LayerShellNotSupportedError, px, size,
+use crate::linux::wayland::{display::WaylandDisplay, serial::SerialKind};
+use crate::linux::{Globals, Output, WaylandClientStatePtr, get_window};
+use gpui::{
+ AnyWindowHandle, Bounds, Capslock, Decorations, DevicePixels, GpuSpecs, Modifiers, Pixels,
+ PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
+ PromptButton, PromptLevel, RequestFrameOptions, ResizeEdge, Scene, Size, Tiling,
+ WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls,
+ WindowDecorations, WindowKind, WindowParams, layer_shell::LayerShellNotSupportedError, px,
+ size,
};
-use crate::{
- Capslock,
- platform::{
- PlatformAtlas, PlatformInputHandler, PlatformWindow,
- linux::wayland::{display::WaylandDisplay, serial::SerialKind},
- wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig},
- },
-};
-use crate::{WindowKind, scene::Scene};
+use gpui_wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig};
#[derive(Default)]
pub(crate) struct Callbacks {
request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
- input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,
+ input: Option<Box<dyn FnMut(gpui::PlatformInput) -> gpui::DispatchEventResult>>,
active_status_change: Option<Box<dyn FnMut(bool)>>,
hover_status_change: Option<Box<dyn FnMut(bool)>>,
resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
@@ -144,34 +139,37 @@ impl WaylandSurfaceState {
let layer_surface = layer_shell.get_layer_surface(
&surface,
None,
- options.layer.into(),
+ super::layer_shell::wayland_layer(options.layer),
options.namespace.clone(),
&globals.qh,
surface.id(),
);
- let width = params.bounds.size.width.0;
- let height = params.bounds.size.height.0;
+ let width = f32::from(params.bounds.size.width);
+ let height = f32::from(params.bounds.size.height);
layer_surface.set_size(width as u32, height as u32);
- layer_surface.set_anchor(options.anchor.into());
- layer_surface.set_keyboard_interactivity(options.keyboard_interactivity.into());
+ layer_surface.set_anchor(super::layer_shell::wayland_anchor(options.anchor));
+ layer_surface.set_keyboard_interactivity(
+ super::layer_shell::wayland_keyboard_interactivity(options.keyboard_interactivity),
+ );
if let Some(margin) = options.margin {
layer_surface.set_margin(
- margin.0.0 as i32,
- margin.1.0 as i32,
- margin.2.0 as i32,
- margin.3.0 as i32,
+ f32::from(margin.0) as i32,
+ f32::from(margin.1) as i32,
+ f32::from(margin.2) as i32,
+ f32::from(margin.3) as i32,
)
}
if let Some(exclusive_zone) = options.exclusive_zone {
- layer_surface.set_exclusive_zone(exclusive_zone.0 as i32);
+ layer_surface.set_exclusive_zone(f32::from(exclusive_zone) as i32);
}
if let Some(exclusive_edge) = options.exclusive_edge {
- layer_surface.set_exclusive_edge(exclusive_edge.into());
+ layer_surface
+ .set_exclusive_edge(super::layer_shell::wayland_anchor(exclusive_edge));
}
return Ok(WaylandSurfaceState::LayerShell(WaylandLayerSurfaceState {
@@ -208,7 +206,7 @@ impl WaylandSurfaceState {
};
if let Some(size) = params.window_min_size {
- toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);
+ toplevel.set_min_size(f32::from(size.width) as i32, f32::from(size.height) as i32);
}
// Attempt to set up window decorations based on the requested configuration
@@ -335,8 +333,8 @@ impl WaylandWindowState {
};
let config = WgpuSurfaceConfig {
size: Size {
- width: DevicePixels(options.bounds.size.width.0 as i32),
- height: DevicePixels(options.bounds.size.height.0 as i32),
+ width: DevicePixels(f32::from(options.bounds.size.width) as i32),
+ height: DevicePixels(f32::from(options.bounds.size.height) as i32),
},
transparent: true,
};
@@ -618,7 +616,7 @@ impl WaylandWindowStatePtr {
state.inset(),
state.tiling,
)
- .map(|v| v.0 as i32)
+ .map(|v| f32::from(v) as i32)
.map_size(|v| if v <= 0 { 1 } else { v });
state.surface_state.set_geometry(
@@ -642,7 +640,7 @@ impl WaylandWindowStatePtr {
match mode {
WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
self.state.borrow_mut().decorations = WindowDecorations::Server;
- if let Some(mut appearance_changed) =
+ if let Some(appearance_changed) =
self.callbacks.borrow_mut().appearance_changed.as_mut()
{
appearance_changed();
@@ -651,7 +649,7 @@ impl WaylandWindowStatePtr {
WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
self.state.borrow_mut().decorations = WindowDecorations::Client;
// Update background to be transparent
- if let Some(mut appearance_changed) =
+ if let Some(appearance_changed) =
self.callbacks.borrow_mut().appearance_changed.as_mut()
{
appearance_changed();
@@ -680,7 +678,7 @@ impl WaylandWindowStatePtr {
height,
states,
} => {
- let mut size = if width == 0 || height == 0 {
+ let size = if width == 0 || height == 0 {
None
} else {
Some(size(px(width as f32), px(height as f32)))
@@ -787,7 +785,7 @@ impl WaylandWindowStatePtr {
height,
serial,
} => {
- let mut size = if width == 0 || height == 0 {
+ let size = if width == 0 || height == 0 {
None
} else {
Some(size(px(width as f32), px(height as f32)))
@@ -933,7 +931,8 @@ impl WaylandWindowStatePtr {
{
let state = self.state.borrow();
if let Some(viewport) = &state.viewport {
- viewport.set_destination(size.width.0 as i32, size.height.0 as i32);
+ viewport
+ .set_destination(f32::from(size.width) as i32, f32::from(size.height) as i32);
}
}
}
@@ -1108,7 +1107,7 @@ impl PlatformWindow for WaylandWindow {
state.inset(),
state.tiling,
)
- .map(|v| v.0 as i32)
+ .map(|v| f32::from(v) as i32)
.map_size(|v| if v <= 0 { 1 } else { v });
state.surface_state.set_geometry(
@@ -1252,7 +1251,7 @@ impl PlatformWindow for WaylandWindow {
}
fn toggle_fullscreen(&self) {
- let mut state = self.borrow();
+ let state = self.borrow();
if let Some(toplevel) = state.surface_state.toplevel() {
if !state.fullscreen {
toplevel.set_fullscreen(None);
@@ -1270,7 +1269,7 @@ impl PlatformWindow for WaylandWindow {
self.0.callbacks.borrow_mut().request_frame = Some(callback);
}
- fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
+ fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> gpui::DispatchEventResult>) {
self.0.callbacks.borrow_mut().input = Some(callback);
}
@@ -1327,8 +1326,8 @@ impl PlatformWindow for WaylandWindow {
toplevel.show_window_menu(
&state.globals.seat,
serial,
- position.x.0 as i32,
- position.y.0 as i32,
+ f32::from(position.x) as i32,
+ f32::from(position.y) as i32,
);
}
}
@@ -1341,7 +1340,7 @@ impl PlatformWindow for WaylandWindow {
}
}
- fn start_window_resize(&self, edge: crate::ResizeEdge) {
+ fn start_window_resize(&self, edge: gpui::ResizeEdge) {
let state = self.borrow();
if let Some(toplevel) = state.surface_state.toplevel() {
toplevel.resize(
@@ -1408,8 +1407,8 @@ fn update_window(mut state: RefMut<WaylandWindowState>) {
let opaque = !state.is_transparent();
state.renderer.update_transparency(!opaque);
- let mut opaque_area = state.window_bounds.map(|v| v.0 as i32);
- opaque_area.inset(state.inset().0 as i32);
+ let opaque_area = state.window_bounds.map(|v| f32::from(v) as i32);
+ opaque_area.inset(f32::from(state.inset()) as i32);
let region = state
.globals
@@ -1454,7 +1453,11 @@ fn update_window(mut state: RefMut<WaylandWindowState>) {
region.destroy();
}
-impl WindowDecorations {
+pub(crate) trait WindowDecorationsExt {
+ fn to_xdg(self) -> zxdg_toplevel_decoration_v1::Mode;
+}
+
+impl WindowDecorationsExt for WindowDecorations {
fn to_xdg(self) -> zxdg_toplevel_decoration_v1::Mode {
match self {
WindowDecorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
@@ -1463,7 +1466,11 @@ impl WindowDecorations {
}
}
-impl ResizeEdge {
+pub(crate) trait ResizeEdgeWaylandExt {
+ fn to_xdg(self) -> xdg_toplevel::ResizeEdge;
+}
+
+impl ResizeEdgeWaylandExt for ResizeEdge {
fn to_xdg(self) -> xdg_toplevel::ResizeEdge {
match self {
ResizeEdge::Top => xdg_toplevel::ResizeEdge::Top,
@@ -1,4 +1,3 @@
-use crate::{Capslock, ResultExt as _, TaskTiming, profiler, xcb_flush};
use anyhow::{Context as _, anyhow};
use ashpd::WindowIdentifier;
use calloop::{
@@ -7,6 +6,7 @@ use calloop::{
};
use collections::HashMap;
use core::str;
+use gpui::{Capslock, TaskTiming, profiler};
use http_client::Url;
use log::Level;
use smallvec::SmallVec;
@@ -45,26 +45,27 @@ use super::{
XimHandler, button_or_scroll_from_event_detail, check_reply,
clipboard::{self, Clipboard},
get_reply, get_valuator_axis_index, handle_connection_error, modifiers_from_state,
- pressed_button_from_mask,
+ pressed_button_from_mask, xcb_flush,
};
-use crate::platform::{
- LinuxCommon, PlatformWindow,
- linux::{
- DEFAULT_CURSOR_ICON_NAME, LinuxClient, get_xkb_compose_state, is_within_click_distance,
- log_cursor_icon_warning, open_uri_internal,
- platform::{DOUBLE_CLICK_INTERVAL, SCROLL_LINES},
- reveal_path_internal,
- xdg_desktop_portal::{Event as XDPEvent, XDPEventSource},
- },
- wgpu::WgpuContext,
+use crate::linux::{
+ DEFAULT_CURSOR_ICON_NAME, LinuxClient, ResultExt as _, capslock_from_xkb,
+ cursor_style_to_icon_names, get_xkb_compose_state, is_within_click_distance,
+ keystroke_from_xkb, keystroke_underlying_dead_key, log_cursor_icon_warning, modifiers_from_xkb,
+ open_uri_internal,
+ platform::{DOUBLE_CLICK_INTERVAL, SCROLL_LINES},
+ reveal_path_internal,
+ xdg_desktop_portal::{Event as XDPEvent, XDPEventSource},
};
-use crate::{
+use crate::linux::{LinuxCommon, LinuxKeyboardLayout, X11Window, modifiers_from_xinput_info};
+
+use gpui::{
AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke,
- LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform,
- PlatformDisplay, PlatformInput, PlatformKeyboardLayout, Point, RequestFrameOptions,
- ScrollDelta, Size, TouchPhase, WindowParams, X11Window, modifiers_from_xinput_info, point, px,
+ Modifiers, ModifiersChangedEvent, MouseButton, Pixels, PlatformDisplay, PlatformInput,
+ PlatformKeyboardLayout, PlatformWindow, Point, RequestFrameOptions, ScrollDelta, Size,
+ TouchPhase, WindowParams, point, px,
};
+use gpui_wgpu::WgpuContext;
/// Value for DeviceId parameters which selects all devices.
pub(crate) const XINPUT_ALL_DEVICES: xinput::DeviceId = 0;
@@ -684,7 +685,7 @@ impl X11Client {
return;
}
- let Some((mut ximc, mut xim_handler)) = state.take_xim() else {
+ let Some((mut ximc, xim_handler)) = state.take_xim() else {
return;
};
let mut ic_attributes = ximc
@@ -815,7 +816,7 @@ impl X11Client {
state.xcb_connection.query_pointer(event.window),
) {
state.xdnd_state.position =
- Point::new(Pixels(pos.win_x as f32), Pixels(pos.win_y as f32));
+ Point::new(px(pos.win_x as f32), px(pos.win_y as f32));
}
if !state.xdnd_state.retrieved {
check_reply(
@@ -857,7 +858,7 @@ impl X11Client {
}
Event::SelectionNotify(event) => {
let window = self.get_window(event.requestor)?;
- let mut state = self.0.borrow_mut();
+ let state = self.0.borrow_mut();
let reply = get_reply(
|| "Failed to get XDND_DATA",
state.xcb_connection.get_property(
@@ -881,7 +882,7 @@ impl X11Client {
.collect();
let input = PlatformInput::FileDrop(FileDropEvent::Entered {
position: state.xdnd_state.position,
- paths: crate::ExternalPaths(paths),
+ paths: gpui::ExternalPaths(paths),
});
drop(state);
window.handle_input(input);
@@ -969,8 +970,8 @@ impl X11Client {
event.latched_group as u32,
event.locked_group.into(),
);
- let modifiers = Modifiers::from_xkb(&state.xkb);
- let capslock = Capslock::from_xkb(&state.xkb);
+ let modifiers = modifiers_from_xkb(&state.xkb);
+ let capslock = capslock_from_xkb(&state.xkb);
if state.last_modifiers_changed_event == modifiers
&& state.last_capslock_changed_event == capslock
{
@@ -1011,7 +1012,7 @@ impl X11Client {
let keystroke = {
let code = event.detail.into();
- let mut keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
+ let mut keystroke = keystroke_from_xkb(&state.xkb, modifiers, code);
let keysym = state.xkb.key_get_one_sym(code);
if keysym.is_modifier_key() {
@@ -1035,7 +1036,7 @@ impl X11Client {
keystroke.key_char = None;
state.pre_edit_text = compose_state
.utf8()
- .or(crate::Keystroke::underlying_dead_key(keysym));
+ .or(keystroke_underlying_dead_key(keysym));
let pre_edit =
state.pre_edit_text.clone().unwrap_or(String::default());
drop(state);
@@ -1048,7 +1049,7 @@ impl X11Client {
if let Some(pre_edit) = pre_edit {
window.handle_ime_commit(pre_edit);
}
- if let Some(current_key) = Keystroke::underlying_dead_key(keysym) {
+ if let Some(current_key) = keystroke_underlying_dead_key(keysym) {
window.handle_ime_preedit(current_key);
}
state = self.0.borrow_mut();
@@ -1061,7 +1062,7 @@ impl X11Client {
keystroke
};
drop(state);
- window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
+ window.handle_input(PlatformInput::KeyDown(gpui::KeyDownEvent {
keystroke,
is_held: false,
prefer_character_input: false,
@@ -1081,7 +1082,7 @@ impl X11Client {
let keystroke = {
let code = event.detail.into();
- let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
+ let keystroke = keystroke_from_xkb(&state.xkb, modifiers, code);
let keysym = state.xkb.key_get_one_sym(code);
if keysym.is_modifier_key() {
@@ -1094,7 +1095,7 @@ impl X11Client {
keystroke
};
drop(state);
- window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
+ window.handle_input(PlatformInput::KeyUp(gpui::KeyUpEvent { keystroke }));
}
Event::XinputButtonPress(event) => {
let window = self.get_window(event.event)?;
@@ -1141,7 +1142,7 @@ impl X11Client {
let current_count = state.current_count;
drop(state);
- window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
+ window.handle_input(PlatformInput::MouseDown(gpui::MouseDownEvent {
button,
position,
modifiers,
@@ -1187,7 +1188,7 @@ impl X11Client {
Some(ButtonOrScroll::Button(button)) => {
let click_count = state.current_count;
drop(state);
- window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
+ window.handle_input(PlatformInput::MouseUp(gpui::MouseUpEvent {
button,
position,
modifiers,
@@ -1238,7 +1239,7 @@ impl X11Client {
drop(state);
if event.valuator_mask[0] & 3 != 0 {
- window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
+ window.handle_input(PlatformInput::MouseMove(gpui::MouseMoveEvent {
position,
pressed_button,
modifiers,
@@ -1246,7 +1247,7 @@ impl X11Client {
}
state = self.0.borrow_mut();
- if let Some(mut pointer) = state.pointer_device_states.get_mut(&event.sourceid) {
+ if let Some(pointer) = state.pointer_device_states.get_mut(&event.sourceid) {
let scroll_delta = get_scroll_delta_and_update_state(pointer, &event);
drop(state);
if let Some(scroll_delta) = scroll_delta {
@@ -1280,7 +1281,7 @@ impl X11Client {
drop(state);
let window = self.get_window(event.event)?;
- window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
+ window.handle_input(PlatformInput::MouseExited(gpui::MouseExitEvent {
pressed_button,
position,
modifiers,
@@ -1305,7 +1306,7 @@ impl X11Client {
}
Event::XinputDeviceChanged(event) => {
let mut state = self.0.borrow_mut();
- if let Some(mut pointer) = state.pointer_device_states.get_mut(&event.sourceid) {
+ if let Some(pointer) = state.pointer_device_states.get_mut(&event.sourceid) {
reset_pointer_device_scroll_positions(pointer);
}
}
@@ -1333,7 +1334,7 @@ impl X11Client {
match event {
Event::KeyPress(event) | Event::KeyRelease(event) => {
let mut state = self.0.borrow_mut();
- state.pre_key_char_down = Some(Keystroke::from_xkb(
+ state.pre_key_char_down = Some(keystroke_from_xkb(
&state.xkb,
state.modifiers,
event.detail.into(),
@@ -1379,7 +1380,7 @@ impl X11Client {
};
let mut state = self.0.borrow_mut();
- let (mut ximc, mut xim_handler) = state.take_xim()?;
+ let (mut ximc, xim_handler) = state.take_xim()?;
state.composing = !text.is_empty();
drop(state);
window.handle_ime_preedit(text);
@@ -1473,7 +1474,12 @@ impl LinuxClient for X11Client {
let state = self.0.borrow();
Some(Rc::new(
- X11Display::new(&state.xcb_connection, state.scale_factor, id.0 as usize).ok()?,
+ X11Display::new(
+ &state.xcb_connection,
+ state.scale_factor,
+ u32::from(id) as usize,
+ )
+ .ok()?,
))
}
@@ -1485,11 +1491,9 @@ impl LinuxClient for X11Client {
#[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
- ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>>
+ ) -> futures::channel::oneshot::Receiver<anyhow::Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>>
{
- crate::platform::scap_screen_capture::scap_screen_sources(
- &self.0.borrow().common.foreground_executor,
- )
+ gpui::scap_screen_capture::scap_screen_sources(&self.0.borrow().common.foreground_executor)
}
fn open_window(
@@ -1589,15 +1593,23 @@ impl LinuxClient for X11Client {
fn open_uri(&self, uri: &str) {
#[cfg(any(feature = "wayland", feature = "x11"))]
- open_uri_internal(self.background_executor(), uri, None);
+ open_uri_internal(
+ self.with_common(|c| c.background_executor.clone()),
+ uri,
+ None,
+ );
}
fn reveal_path(&self, path: PathBuf) {
#[cfg(any(feature = "x11", feature = "wayland"))]
- reveal_path_internal(self.background_executor(), path, None);
+ reveal_path_internal(
+ self.with_common(|c| c.background_executor.clone()),
+ path,
+ None,
+ );
}
- fn write_to_primary(&self, item: crate::ClipboardItem) {
+ fn write_to_primary(&self, item: gpui::ClipboardItem) {
let state = self.0.borrow_mut();
state
.clipboard
@@ -1610,7 +1622,7 @@ impl LinuxClient for X11Client {
.log_with_level(log::Level::Debug);
}
- fn write_to_clipboard(&self, item: crate::ClipboardItem) {
+ fn write_to_clipboard(&self, item: gpui::ClipboardItem) {
let mut state = self.0.borrow_mut();
state
.clipboard
@@ -1624,7 +1636,7 @@ impl LinuxClient for X11Client {
state.clipboard_item.replace(item);
}
- fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
+ fn read_from_primary(&self) -> Option<gpui::ClipboardItem> {
let state = self.0.borrow_mut();
state
.clipboard
@@ -1633,7 +1645,7 @@ impl LinuxClient for X11Client {
.log_with_level(log::Level::Debug)
}
- fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
+ fn read_from_clipboard(&self) -> Option<gpui::ClipboardItem> {
let state = self.0.borrow_mut();
// if the last copy was from this app, return our cached item
// which has metadata attached.
@@ -1876,7 +1888,7 @@ impl X11ClientState {
return *cursor;
}
- let mut result;
+ let result;
match style {
CursorStyle::None => match create_invisible_cursor(&self.xcb_connection) {
Ok(loaded_cursor) => result = Ok(loaded_cursor),
@@ -1884,7 +1896,7 @@ impl X11ClientState {
},
_ => 'outer: {
let mut errors = String::new();
- let cursor_icon_names = style.to_icon_names();
+ let cursor_icon_names = cursor_style_to_icon_names(style);
for cursor_icon_name in cursor_icon_names {
match self
.cursor_handle
@@ -2262,7 +2274,7 @@ fn make_scroll_wheel_event(
position: Point<Pixels>,
scroll_delta: Point<f32>,
modifiers: Modifiers,
-) -> crate::ScrollWheelEvent {
+) -> gpui::ScrollWheelEvent {
// When shift is held down, vertical scrolling turns into horizontal scrolling.
let delta = if modifiers.shift {
Point {
@@ -2272,7 +2284,7 @@ fn make_scroll_wheel_event(
} else {
scroll_delta
};
- crate::ScrollWheelEvent {
+ gpui::ScrollWheelEvent {
position,
delta: ScrollDelta::Lines(delta),
modifiers,
@@ -2282,7 +2294,7 @@ fn make_scroll_wheel_event(
fn create_invisible_cursor(
connection: &XCBConnection,
-) -> anyhow::Result<crate::platform::linux::x11::client::xproto::Cursor> {
+) -> anyhow::Result<crate::linux::x11::client::xproto::Cursor> {
let empty_pixmap = connection.generate_id()?;
let root = connection.setup().roots[0].root;
connection.create_pixmap(1, empty_pixmap, root, 1, 1)?;
@@ -47,7 +47,7 @@ use x11rb::{
wrapper::ConnectionExt as _,
};
-use crate::{ClipboardItem, Image, ImageFormat, hash};
+use gpui::{ClipboardItem, Image, ImageFormat, hash};
type Result<T, E = Error> = std::result::Result<T, E>;
@@ -2,7 +2,7 @@ use anyhow::Context as _;
use uuid::Uuid;
use x11rb::{connection::Connection as _, xcb_ffi::XCBConnection};
-use crate::{Bounds, DisplayId, Pixels, PlatformDisplay, Size, px};
+use gpui::{Bounds, DisplayId, Pixels, PlatformDisplay, Size, px};
#[derive(Debug)]
pub(crate) struct X11Display {
@@ -38,7 +38,7 @@ impl X11Display {
impl PlatformDisplay for X11Display {
fn id(&self) -> DisplayId {
- DisplayId(self.x_screen_index as u32)
+ DisplayId::new(self.x_screen_index as u32)
}
fn uuid(&self) -> anyhow::Result<Uuid> {
@@ -3,7 +3,7 @@ use x11rb::protocol::{
xproto::{self, ModMask},
};
-use crate::{Modifiers, MouseButton, NavigationDirection};
+use gpui::{Modifiers, MouseButton, NavigationDirection};
pub(crate) enum ButtonOrScroll {
Button(MouseButton),
@@ -1,14 +1,15 @@
use anyhow::{Context as _, anyhow};
use x11rb::connection::RequestConnection;
-use crate::platform::wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig};
-use crate::{
+use crate::linux::X11ClientStatePtr;
+use gpui::{
AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, GpuSpecs, Modifiers,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
Point, PromptButton, PromptLevel, RequestFrameOptions, ResizeEdge, ScaledPixels, Scene, Size,
Tiling, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea,
- WindowDecorations, WindowKind, WindowParams, X11ClientStatePtr, px,
+ WindowDecorations, WindowKind, WindowParams, px,
};
+use gpui_wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig};
use collections::FxHashSet;
use raw_window_handle as rwh;
@@ -95,18 +96,16 @@ fn query_render_extent(
})
}
-impl ResizeEdge {
- fn to_moveresize(self) -> u32 {
- match self {
- ResizeEdge::TopLeft => 0,
- ResizeEdge::Top => 1,
- ResizeEdge::TopRight => 2,
- ResizeEdge::Right => 3,
- ResizeEdge::BottomRight => 4,
- ResizeEdge::Bottom => 5,
- ResizeEdge::BottomLeft => 6,
- ResizeEdge::Left => 7,
- }
+fn resize_edge_to_moveresize(edge: ResizeEdge) -> u32 {
+ match edge {
+ ResizeEdge::TopLeft => 0,
+ ResizeEdge::Top => 1,
+ ResizeEdge::TopRight => 2,
+ ResizeEdge::Right => 3,
+ ResizeEdge::BottomRight => 4,
+ ResizeEdge::Bottom => 5,
+ ResizeEdge::BottomLeft => 6,
+ ResizeEdge::Left => 7,
}
}
@@ -242,7 +241,7 @@ unsafe impl Sync for RawWindow {}
#[derive(Default)]
pub struct Callbacks {
request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
- input: Option<Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>>,
+ input: Option<Box<dyn FnMut(PlatformInput) -> gpui::DispatchEventResult>>,
active_status_change: Option<Box<dyn FnMut(bool)>>,
hovered_status_change: Option<Box<dyn FnMut(bool)>>,
resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
@@ -405,7 +404,7 @@ impl X11WindowState {
) -> anyhow::Result<Self> {
let x_screen_index = params
.display_id
- .map_or(x_main_screen_index, |did| did.0 as usize);
+ .map_or(x_main_screen_index, |did| u32::from(did) as usize);
let visual_set = find_visuals(xcb, x_screen_index);
@@ -500,7 +499,7 @@ impl X11WindowState {
if let Some(size) = params.window_min_size {
let mut size_hints = WmSizeHints::new();
- let min_size = (size.width.0 as i32, size.height.0 as i32);
+ let min_size = (f32::from(size.width) as i32, f32::from(size.height) as i32);
size_hints.min_size = Some(min_size);
check_reply(
|| {
@@ -874,8 +873,8 @@ impl X11Window {
self.0.xcb.translate_coordinates(
self.0.x_window,
state.x_root_window,
- (position.x.0 * state.scale_factor) as i16,
- (position.y.0 * state.scale_factor) as i16,
+ (f32::from(position.x) * state.scale_factor) as i16,
+ (f32::from(position.y) * state.scale_factor) as i16,
),
)
}
@@ -932,7 +931,7 @@ impl X11WindowStatePtr {
}
pub fn property_notify(&self, event: xproto::PropertyNotifyEvent) -> anyhow::Result<()> {
- let mut state = self.state.borrow_mut();
+ let state = self.state.borrow_mut();
if event.atom == state.atoms._NET_WM_STATE {
self.set_wm_properties(state)?;
} else if event.atom == state.atoms._GTK_EDGE_CONSTRAINTS {
@@ -1247,10 +1246,10 @@ impl PlatformWindow for X11Window {
let [left, right, top, bottom] = state.last_insets;
let [left, right, top, bottom] = [
- Pixels((left as f32) / state.scale_factor),
- Pixels((right as f32) / state.scale_factor),
- Pixels((top as f32) / state.scale_factor),
- Pixels((bottom as f32) / state.scale_factor),
+ px((left as f32) / state.scale_factor),
+ px((right as f32) / state.scale_factor),
+ px((top as f32) / state.scale_factor),
+ px((bottom as f32) / state.scale_factor),
];
bounds.origin.x += left;
@@ -1327,7 +1326,7 @@ impl PlatformWindow for X11Window {
.unwrap_or_default()
}
- fn capslock(&self) -> crate::Capslock {
+ fn capslock(&self) -> gpui::Capslock {
self.0
.state
.borrow()
@@ -1522,7 +1521,7 @@ impl PlatformWindow for X11Window {
self.0.callbacks.borrow_mut().request_frame = Some(callback);
}
- fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
+ fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> gpui::DispatchEventResult>) {
self.0.callbacks.borrow_mut().input = Some(callback);
}
@@ -1609,10 +1608,11 @@ impl PlatformWindow for X11Window {
}
fn start_window_resize(&self, edge: ResizeEdge) {
- self.send_moveresize(edge.to_moveresize()).log_err();
+ self.send_moveresize(resize_edge_to_moveresize(edge))
+ .log_err();
}
- fn window_decorations(&self) -> crate::Decorations {
+ fn window_decorations(&self) -> gpui::Decorations {
let state = self.0.state.borrow();
// Client window decorations require compositor support
@@ -1644,7 +1644,7 @@ impl PlatformWindow for X11Window {
fn set_client_inset(&self, inset: Pixels) {
let mut state = self.0.state.borrow_mut();
- let dp = (inset.0 * state.scale_factor) as u32;
+ let dp = (f32::from(inset) * state.scale_factor) as u32;
let insets = if state.fullscreen {
[0, 0, 0, 0]
@@ -1688,16 +1688,16 @@ impl PlatformWindow for X11Window {
}
}
- fn request_decorations(&self, mut decorations: crate::WindowDecorations) {
+ fn request_decorations(&self, mut decorations: gpui::WindowDecorations) {
let mut state = self.0.state.borrow_mut();
- if matches!(decorations, crate::WindowDecorations::Client)
+ if matches!(decorations, gpui::WindowDecorations::Client)
&& !state.client_side_decorations_supported
{
log::info!(
"x11: no compositor present, falling back to server-side window decorations"
);
- decorations = crate::WindowDecorations::Server;
+ decorations = gpui::WindowDecorations::Server;
}
// https://github.com/rust-windowing/winit/blob/master/src/platform_impl/linux/x11/util/hint.rs#L53-L87
@@ -1745,7 +1745,7 @@ impl PlatformWindow for X11Window {
}
fn update_ime_position(&self, bounds: Bounds<Pixels>) {
- let mut state = self.0.state.borrow_mut();
+ let state = self.0.state.borrow();
let client = state.client.clone();
drop(state);
client.update_ime_position(bounds);
@@ -7,7 +7,7 @@ use calloop::channel::Channel;
use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
use smol::stream::StreamExt;
-use crate::{BackgroundExecutor, WindowAppearance};
+use gpui::{BackgroundExecutor, WindowAppearance};
pub enum Event {
WindowAppearance(WindowAppearance),
@@ -32,9 +32,9 @@ impl XDPEventSource {
let settings = Settings::new().await?;
if let Ok(initial_appearance) = settings.color_scheme().await {
- sender.send(Event::WindowAppearance(WindowAppearance::from_native(
- initial_appearance,
- )))?;
+ sender.send(Event::WindowAppearance(
+ window_appearance_from_color_scheme(initial_appearance),
+ ))?;
}
if let Ok(initial_theme) = settings
.read::<String>("org.gnome.desktop.interface", "cursor-theme")
@@ -91,9 +91,9 @@ impl XDPEventSource {
let mut appearance_changed = settings.receive_color_scheme_changed().await?;
while let Some(scheme) = appearance_changed.next().await {
- sender.send(Event::WindowAppearance(WindowAppearance::from_native(
- scheme,
- )))?;
+ sender.send(Event::WindowAppearance(
+ window_appearance_from_color_scheme(scheme),
+ ))?;
}
anyhow::Ok(())
@@ -155,17 +155,10 @@ impl EventSource for XDPEventSource {
}
}
-impl WindowAppearance {
- fn from_native(cs: ColorScheme) -> WindowAppearance {
- match cs {
- ColorScheme::PreferDark => WindowAppearance::Dark,
- ColorScheme::PreferLight => WindowAppearance::Light,
- ColorScheme::NoPreference => WindowAppearance::Light,
- }
- }
-
- #[cfg_attr(any(target_os = "linux", target_os = "freebsd"), allow(dead_code))]
- fn set_native(&mut self, cs: ColorScheme) {
- *self = Self::from_native(cs);
+fn window_appearance_from_color_scheme(cs: ColorScheme) -> WindowAppearance {
+ match cs {
+ ColorScheme::PreferDark => WindowAppearance::Dark,
+ ColorScheme::PreferLight => WindowAppearance::Light,
+ ColorScheme::NoPreference => WindowAppearance::Light,
}
}
@@ -0,0 +1,62 @@
+[package]
+name = "gpui_macos"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/gpui_macos.rs"
+
+[features]
+default = ["gpui/default"]
+test-support = ["gpui/test-support"]
+runtime_shaders = []
+font-kit = ["dep:font-kit"]
+screen-capture = ["gpui/screen-capture"]
+
+[dependencies]
+gpui.workspace = true
+
+[target.'cfg(target_os = "macos")'.dependencies]
+anyhow.workspace = true
+async-task = "4.7"
+block = "0.1"
+cocoa.workspace = true
+collections.workspace = true
+core-foundation.workspace = true
+core-foundation-sys.workspace = true
+core-graphics = "0.24"
+core-text = "21"
+core-video.workspace = true
+ctor.workspace = true
+derive_more.workspace = true
+etagere = "0.2"
+# WARNING: If you change this, you must also publish a new version of zed-font-kit to crates.io
+font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "110523127440aefb11ce0cf280ae7c5071337ec5", package = "zed-font-kit", version = "0.14.1-zed", optional = true }
+foreign-types = "0.5"
+futures.workspace = true
+image.workspace = true
+itertools.workspace = true
+libc.workspace = true
+log.workspace = true
+mach2.workspace = true
+media.workspace = true
+metal.workspace = true
+objc.workspace = true
+parking_lot.workspace = true
+pathfinder_geometry = "0.5"
+raw-window-handle = "0.6"
+semver.workspace = true
+smallvec.workspace = true
+strum.workspace = true
+util.workspace = true
+uuid.workspace = true
+
+[target.'cfg(target_os = "macos")'.build-dependencies]
+bindgen = "0.71"
+cbindgen = { version = "0.28.0", default-features = false }
+gpui.workspace = true
@@ -0,0 +1 @@
+../../LICENSE-APACHE
@@ -0,0 +1,209 @@
+#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
+
+fn main() {
+ #[cfg(target_os = "macos")]
+ macos_build::run();
+}
+
+#[cfg(target_os = "macos")]
+mod macos_build {
+ use std::{
+ env,
+ path::{Path, PathBuf},
+ };
+
+ use cbindgen::Config;
+
+ pub fn run() {
+ generate_dispatch_bindings();
+
+ let header_path = generate_shader_bindings();
+
+ #[cfg(feature = "runtime_shaders")]
+ emit_stitched_shaders(&header_path);
+ #[cfg(not(feature = "runtime_shaders"))]
+ compile_metal_shaders(&header_path);
+ }
+
+ fn generate_dispatch_bindings() {
+ println!("cargo:rustc-link-lib=framework=System");
+
+ let bindings = bindgen::Builder::default()
+ .header("src/dispatch.h")
+ .allowlist_var("_dispatch_main_q")
+ .allowlist_var("_dispatch_source_type_data_add")
+ .allowlist_var("DISPATCH_QUEUE_PRIORITY_HIGH")
+ .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
+ .allowlist_var("DISPATCH_QUEUE_PRIORITY_LOW")
+ .allowlist_var("DISPATCH_TIME_NOW")
+ .allowlist_function("dispatch_get_global_queue")
+ .allowlist_function("dispatch_async_f")
+ .allowlist_function("dispatch_after_f")
+ .allowlist_function("dispatch_time")
+ .allowlist_function("dispatch_source_merge_data")
+ .allowlist_function("dispatch_source_create")
+ .allowlist_function("dispatch_source_set_event_handler_f")
+ .allowlist_function("dispatch_resume")
+ .allowlist_function("dispatch_suspend")
+ .allowlist_function("dispatch_source_cancel")
+ .allowlist_function("dispatch_set_context")
+ .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
+ .layout_tests(false)
+ .generate()
+ .expect("unable to generate bindings");
+
+ let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
+ bindings
+ .write_to_file(out_path.join("dispatch_sys.rs"))
+ .expect("couldn't write dispatch bindings");
+ }
+
+ fn generate_shader_bindings() -> PathBuf {
+ let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
+
+ let gpui_dir = find_gpui_crate_dir();
+
+ let mut config = Config {
+ include_guard: Some("SCENE_H".into()),
+ language: cbindgen::Language::C,
+ no_includes: true,
+ ..Default::default()
+ };
+ config.export.include.extend([
+ "Bounds".into(),
+ "Corners".into(),
+ "Edges".into(),
+ "Size".into(),
+ "Pixels".into(),
+ "PointF".into(),
+ "Hsla".into(),
+ "ContentMask".into(),
+ "Uniforms".into(),
+ "AtlasTile".into(),
+ "PathRasterizationInputIndex".into(),
+ "PathVertex_ScaledPixels".into(),
+ "PathRasterizationVertex".into(),
+ "ShadowInputIndex".into(),
+ "Shadow".into(),
+ "QuadInputIndex".into(),
+ "Underline".into(),
+ "UnderlineInputIndex".into(),
+ "Quad".into(),
+ "BorderStyle".into(),
+ "SpriteInputIndex".into(),
+ "MonochromeSprite".into(),
+ "PolychromeSprite".into(),
+ "PathSprite".into(),
+ "SurfaceInputIndex".into(),
+ "SurfaceBounds".into(),
+ "TransformationMatrix".into(),
+ ]);
+ config.no_includes = true;
+ config.enumeration.prefix_with_name = true;
+
+ let mut builder = cbindgen::Builder::new();
+
+ let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+
+ // Source files from gpui that define types used in shaders
+ let gpui_src_paths = [
+ gpui_dir.join("src/scene.rs"),
+ gpui_dir.join("src/geometry.rs"),
+ gpui_dir.join("src/color.rs"),
+ gpui_dir.join("src/window.rs"),
+ gpui_dir.join("src/platform.rs"),
+ ];
+
+ // Source files from this crate
+ let local_src_paths = [crate_dir.join("src/metal_renderer.rs")];
+
+ for src_path in gpui_src_paths.iter().chain(local_src_paths.iter()) {
+ println!("cargo:rerun-if-changed={}", src_path.display());
+ builder = builder.with_src(src_path);
+ }
+
+ builder
+ .with_config(config)
+ .generate()
+ .expect("Unable to generate bindings")
+ .write_to_file(&output_path);
+
+ output_path
+ }
+
+ /// Locate the gpui crate directory relative to this crate.
+ fn find_gpui_crate_dir() -> PathBuf {
+ gpui::GPUI_MANIFEST_DIR.into()
+ }
+
+ /// To enable runtime compilation, we need to "stitch" the shaders file with the generated header
+ /// so that it is self-contained.
+ #[cfg(feature = "runtime_shaders")]
+ fn emit_stitched_shaders(header_path: &Path) {
+ fn stitch_header(header: &Path, shader_path: &Path) -> std::io::Result<PathBuf> {
+ let header_contents = std::fs::read_to_string(header)?;
+ let shader_contents = std::fs::read_to_string(shader_path)?;
+ let stitched_contents = format!("{header_contents}\n{shader_contents}");
+ let out_path =
+ PathBuf::from(env::var("OUT_DIR").unwrap()).join("stitched_shaders.metal");
+ std::fs::write(&out_path, stitched_contents)?;
+ Ok(out_path)
+ }
+ let shader_source_path = "./src/shaders.metal";
+ let shader_path = PathBuf::from(shader_source_path);
+ stitch_header(header_path, &shader_path).unwrap();
+ println!("cargo:rerun-if-changed={}", &shader_source_path);
+ }
+
+ #[cfg(not(feature = "runtime_shaders"))]
+ fn compile_metal_shaders(header_path: &Path) {
+ use std::process::{self, Command};
+ let shader_path = "./src/shaders.metal";
+ let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air");
+ let metallib_output_path =
+ PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.metallib");
+ println!("cargo:rerun-if-changed={}", shader_path);
+
+ let output = Command::new("xcrun")
+ .args([
+ "-sdk",
+ "macosx",
+ "metal",
+ "-gline-tables-only",
+ "-mmacosx-version-min=10.15.7",
+ "-MO",
+ "-c",
+ shader_path,
+ "-include",
+ (header_path.to_str().unwrap()),
+ "-o",
+ ])
+ .arg(&air_output_path)
+ .output()
+ .unwrap();
+
+ if !output.status.success() {
+ println!(
+ "cargo::error=metal shader compilation failed:\n{}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+ process::exit(1);
+ }
+
+ let output = Command::new("xcrun")
+ .args(["-sdk", "macosx", "metallib"])
+ .arg(air_output_path)
+ .arg("-o")
+ .arg(metallib_output_path)
+ .output()
+ .unwrap();
+
+ if !output.status.success() {
+ println!(
+ "cargo::error=metallib compilation failed:\n{}",
+ String::from_utf8_lossy(&output.stderr)
+ );
+ process::exit(1);
+ }
+ }
+}
@@ -2,7 +2,7 @@
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
-use crate::{
+use gpui::{
GLOBAL_THREAD_TIMINGS, PlatformDispatcher, Priority, RunnableMeta, RunnableVariant,
THREAD_TIMINGS, TaskTiming, ThreadTaskTimings,
};
@@ -1,5 +1,4 @@
-use super::ns_string;
-use crate::{Bounds, DisplayId, Pixels, PlatformDisplay, point, px, size};
+use crate::ns_string;
use anyhow::Result;
use cocoa::{
appkit::NSScreen,
@@ -8,6 +7,7 @@ use cocoa::{
};
use core_foundation::uuid::{CFUUIDGetUUIDBytes, CFUUIDRef};
use core_graphics::display::{CGDirectDisplayID, CGDisplayBounds, CGGetActiveDisplayList};
+use gpui::{Bounds, DisplayId, Pixels, PlatformDisplay, point, px, size};
use objc::{msg_send, sel, sel_impl};
use uuid::Uuid;
@@ -72,7 +72,7 @@ unsafe extern "C" {
impl PlatformDisplay for MacDisplay {
fn id(&self) -> DisplayId {
- DisplayId(self.0)
+ DisplayId::new(self.0)
}
fn uuid(&self) -> Result<Uuid> {
@@ -1,6 +1,6 @@
use crate::{
dispatch_get_main_queue,
- dispatch_sys::{
+ dispatcher::dispatch_sys::{
_dispatch_source_type_data_add, dispatch_resume, dispatch_set_context,
dispatch_source_cancel, dispatch_source_create, dispatch_source_merge_data,
dispatch_source_set_event_handler_f, dispatch_source_t, dispatch_suspend,
@@ -1,13 +1,13 @@
-use crate::{
+use gpui::{
Capslock, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
MouseDownEvent, MouseExitEvent, MouseMoveEvent, MousePressureEvent, MouseUpEvent,
NavigationDirection, Pixels, PlatformInput, PressureStage, ScrollDelta, ScrollWheelEvent,
- TouchPhase,
- platform::mac::{
- LMGetKbdType, NSStringExt, TISCopyCurrentKeyboardLayoutInputSource,
- TISGetInputSourceProperty, UCKeyTranslate, kTISPropertyUnicodeKeyLayoutData,
- },
- point, px,
+ TouchPhase, point, px,
+};
+
+use crate::{
+ LMGetKbdType, NSStringExt, TISCopyCurrentKeyboardLayoutInputSource, TISGetInputSourceProperty,
+ UCKeyTranslate, kTISPropertyUnicodeKeyLayoutData,
};
use cocoa::{
appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType},
@@ -101,217 +101,215 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers {
}
}
-impl PlatformInput {
- pub(crate) unsafe fn from_native(
- native_event: id,
- window_height: Option<Pixels>,
- ) -> Option<Self> {
- unsafe {
- let event_type = native_event.eventType();
+pub(crate) unsafe fn platform_input_from_native(
+ native_event: id,
+ window_height: Option<Pixels>,
+) -> Option<PlatformInput> {
+ unsafe {
+ let event_type = native_event.eventType();
- // Filter out event types that aren't in the NSEventType enum.
- // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
- match event_type as u64 {
- 0 | 21 | 32 | 33 | 35 | 36 | 37 => {
- return None;
- }
- _ => {}
+ // Filter out event types that aren't in the NSEventType enum.
+ // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details.
+ match event_type as u64 {
+ 0 | 21 | 32 | 33 | 35 | 36 | 37 => {
+ return None;
}
+ _ => {}
+ }
- match event_type {
- NSEventType::NSFlagsChanged => {
- Some(Self::ModifiersChanged(ModifiersChangedEvent {
+ match event_type {
+ NSEventType::NSFlagsChanged => {
+ Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
+ modifiers: read_modifiers(native_event),
+ capslock: Capslock {
+ on: native_event
+ .modifierFlags()
+ .contains(NSEventModifierFlags::NSAlphaShiftKeyMask),
+ },
+ }))
+ }
+ NSEventType::NSKeyDown => Some(PlatformInput::KeyDown(KeyDownEvent {
+ keystroke: parse_keystroke(native_event),
+ is_held: native_event.isARepeat() == YES,
+ prefer_character_input: false,
+ })),
+ NSEventType::NSKeyUp => Some(PlatformInput::KeyUp(KeyUpEvent {
+ keystroke: parse_keystroke(native_event),
+ })),
+ NSEventType::NSLeftMouseDown
+ | NSEventType::NSRightMouseDown
+ | NSEventType::NSOtherMouseDown => {
+ let button = match native_event.buttonNumber() {
+ 0 => MouseButton::Left,
+ 1 => MouseButton::Right,
+ 2 => MouseButton::Middle,
+ 3 => MouseButton::Navigate(NavigationDirection::Back),
+ 4 => MouseButton::Navigate(NavigationDirection::Forward),
+ // Other mouse buttons aren't tracked currently
+ _ => return None,
+ };
+ window_height.map(|window_height| {
+ PlatformInput::MouseDown(MouseDownEvent {
+ button,
+ position: point(
+ px(native_event.locationInWindow().x as f32),
+ // MacOS screen coordinates are relative to bottom left
+ window_height - px(native_event.locationInWindow().y as f32),
+ ),
modifiers: read_modifiers(native_event),
- capslock: Capslock {
- on: native_event
- .modifierFlags()
- .contains(NSEventModifierFlags::NSAlphaShiftKeyMask),
- },
- }))
- }
- NSEventType::NSKeyDown => Some(Self::KeyDown(KeyDownEvent {
- keystroke: parse_keystroke(native_event),
- is_held: native_event.isARepeat() == YES,
- prefer_character_input: false,
- })),
- NSEventType::NSKeyUp => Some(Self::KeyUp(KeyUpEvent {
- keystroke: parse_keystroke(native_event),
- })),
- NSEventType::NSLeftMouseDown
- | NSEventType::NSRightMouseDown
- | NSEventType::NSOtherMouseDown => {
- let button = match native_event.buttonNumber() {
- 0 => MouseButton::Left,
- 1 => MouseButton::Right,
- 2 => MouseButton::Middle,
- 3 => MouseButton::Navigate(NavigationDirection::Back),
- 4 => MouseButton::Navigate(NavigationDirection::Forward),
- // Other mouse buttons aren't tracked currently
- _ => return None,
- };
- window_height.map(|window_height| {
- Self::MouseDown(MouseDownEvent {
- button,
- position: point(
- px(native_event.locationInWindow().x as f32),
- // MacOS screen coordinates are relative to bottom left
- window_height - px(native_event.locationInWindow().y as f32),
- ),
- modifiers: read_modifiers(native_event),
- click_count: native_event.clickCount() as usize,
- first_mouse: false,
- })
+ click_count: native_event.clickCount() as usize,
+ first_mouse: false,
})
- }
- NSEventType::NSLeftMouseUp
- | NSEventType::NSRightMouseUp
- | NSEventType::NSOtherMouseUp => {
- let button = match native_event.buttonNumber() {
- 0 => MouseButton::Left,
- 1 => MouseButton::Right,
- 2 => MouseButton::Middle,
- 3 => MouseButton::Navigate(NavigationDirection::Back),
- 4 => MouseButton::Navigate(NavigationDirection::Forward),
- // Other mouse buttons aren't tracked currently
- _ => return None,
- };
-
- window_height.map(|window_height| {
- Self::MouseUp(MouseUpEvent {
- button,
- position: point(
- px(native_event.locationInWindow().x as f32),
- window_height - px(native_event.locationInWindow().y as f32),
- ),
- modifiers: read_modifiers(native_event),
- click_count: native_event.clickCount() as usize,
- })
- })
- }
- NSEventType::NSEventTypePressure => {
- let stage = native_event.stage();
- let pressure = native_event.pressure();
-
- window_height.map(|window_height| {
- Self::MousePressure(MousePressureEvent {
- stage: match stage {
- 1 => PressureStage::Normal,
- 2 => PressureStage::Force,
- _ => PressureStage::Zero,
- },
- pressure,
- modifiers: read_modifiers(native_event),
- position: point(
- px(native_event.locationInWindow().x as f32),
- window_height - px(native_event.locationInWindow().y as f32),
- ),
- })
+ })
+ }
+ NSEventType::NSLeftMouseUp
+ | NSEventType::NSRightMouseUp
+ | NSEventType::NSOtherMouseUp => {
+ let button = match native_event.buttonNumber() {
+ 0 => MouseButton::Left,
+ 1 => MouseButton::Right,
+ 2 => MouseButton::Middle,
+ 3 => MouseButton::Navigate(NavigationDirection::Back),
+ 4 => MouseButton::Navigate(NavigationDirection::Forward),
+ // Other mouse buttons aren't tracked currently
+ _ => return None,
+ };
+
+ window_height.map(|window_height| {
+ PlatformInput::MouseUp(MouseUpEvent {
+ button,
+ position: point(
+ px(native_event.locationInWindow().x as f32),
+ window_height - px(native_event.locationInWindow().y as f32),
+ ),
+ modifiers: read_modifiers(native_event),
+ click_count: native_event.clickCount() as usize,
})
- }
- // Some mice (like Logitech MX Master) send navigation buttons as swipe events
- NSEventType::NSEventTypeSwipe => {
- let navigation_direction = match native_event.phase() {
- NSEventPhase::NSEventPhaseEnded => match native_event.deltaX() {
- x if x > 0.0 => Some(NavigationDirection::Back),
- x if x < 0.0 => Some(NavigationDirection::Forward),
- _ => return None,
+ })
+ }
+ NSEventType::NSEventTypePressure => {
+ let stage = native_event.stage();
+ let pressure = native_event.pressure();
+
+ window_height.map(|window_height| {
+ PlatformInput::MousePressure(MousePressureEvent {
+ stage: match stage {
+ 1 => PressureStage::Normal,
+ 2 => PressureStage::Force,
+ _ => PressureStage::Zero,
},
- _ => return None,
- };
-
- match navigation_direction {
- Some(direction) => window_height.map(|window_height| {
- Self::MouseDown(MouseDownEvent {
- button: MouseButton::Navigate(direction),
- position: point(
- px(native_event.locationInWindow().x as f32),
- window_height - px(native_event.locationInWindow().y as f32),
- ),
- modifiers: read_modifiers(native_event),
- click_count: 1,
- first_mouse: false,
- })
- }),
- _ => None,
- }
- }
- NSEventType::NSScrollWheel => window_height.map(|window_height| {
- let phase = match native_event.phase() {
- NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
- TouchPhase::Started
- }
- NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
- _ => TouchPhase::Moved,
- };
-
- let raw_data = point(
- native_event.scrollingDeltaX() as f32,
- native_event.scrollingDeltaY() as f32,
- );
-
- let delta = if native_event.hasPreciseScrollingDeltas() == YES {
- ScrollDelta::Pixels(raw_data.map(px))
- } else {
- ScrollDelta::Lines(raw_data)
- };
-
- Self::ScrollWheel(ScrollWheelEvent {
+ pressure,
+ modifiers: read_modifiers(native_event),
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
- delta,
- touch_phase: phase,
- modifiers: read_modifiers(native_event),
})
- }),
- NSEventType::NSLeftMouseDragged
- | NSEventType::NSRightMouseDragged
- | NSEventType::NSOtherMouseDragged => {
- let pressed_button = match native_event.buttonNumber() {
- 0 => MouseButton::Left,
- 1 => MouseButton::Right,
- 2 => MouseButton::Middle,
- 3 => MouseButton::Navigate(NavigationDirection::Back),
- 4 => MouseButton::Navigate(NavigationDirection::Forward),
- // Other mouse buttons aren't tracked currently
+ })
+ }
+ // Some mice (like Logitech MX Master) send navigation buttons as swipe events
+ NSEventType::NSEventTypeSwipe => {
+ let navigation_direction = match native_event.phase() {
+ NSEventPhase::NSEventPhaseEnded => match native_event.deltaX() {
+ x if x > 0.0 => Some(NavigationDirection::Back),
+ x if x < 0.0 => Some(NavigationDirection::Forward),
_ => return None,
- };
-
- window_height.map(|window_height| {
- Self::MouseMove(MouseMoveEvent {
- pressed_button: Some(pressed_button),
+ },
+ _ => return None,
+ };
+
+ match navigation_direction {
+ Some(direction) => window_height.map(|window_height| {
+ PlatformInput::MouseDown(MouseDownEvent {
+ button: MouseButton::Navigate(direction),
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
modifiers: read_modifiers(native_event),
+ click_count: 1,
+ first_mouse: false,
})
- })
+ }),
+ _ => None,
}
- NSEventType::NSMouseMoved => window_height.map(|window_height| {
- Self::MouseMove(MouseMoveEvent {
- position: point(
- px(native_event.locationInWindow().x as f32),
- window_height - px(native_event.locationInWindow().y as f32),
- ),
- pressed_button: None,
- modifiers: read_modifiers(native_event),
- })
- }),
- NSEventType::NSMouseExited => window_height.map(|window_height| {
- Self::MouseExited(MouseExitEvent {
+ }
+ NSEventType::NSScrollWheel => window_height.map(|window_height| {
+ let phase = match native_event.phase() {
+ NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
+ TouchPhase::Started
+ }
+ NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
+ _ => TouchPhase::Moved,
+ };
+
+ let raw_data = point(
+ native_event.scrollingDeltaX() as f32,
+ native_event.scrollingDeltaY() as f32,
+ );
+
+ let delta = if native_event.hasPreciseScrollingDeltas() == YES {
+ ScrollDelta::Pixels(raw_data.map(px))
+ } else {
+ ScrollDelta::Lines(raw_data)
+ };
+
+ PlatformInput::ScrollWheel(ScrollWheelEvent {
+ position: point(
+ px(native_event.locationInWindow().x as f32),
+ window_height - px(native_event.locationInWindow().y as f32),
+ ),
+ delta,
+ touch_phase: phase,
+ modifiers: read_modifiers(native_event),
+ })
+ }),
+ NSEventType::NSLeftMouseDragged
+ | NSEventType::NSRightMouseDragged
+ | NSEventType::NSOtherMouseDragged => {
+ let pressed_button = match native_event.buttonNumber() {
+ 0 => MouseButton::Left,
+ 1 => MouseButton::Right,
+ 2 => MouseButton::Middle,
+ 3 => MouseButton::Navigate(NavigationDirection::Back),
+ 4 => MouseButton::Navigate(NavigationDirection::Forward),
+ // Other mouse buttons aren't tracked currently
+ _ => return None,
+ };
+
+ window_height.map(|window_height| {
+ PlatformInput::MouseMove(MouseMoveEvent {
+ pressed_button: Some(pressed_button),
position: point(
px(native_event.locationInWindow().x as f32),
window_height - px(native_event.locationInWindow().y as f32),
),
-
- pressed_button: None,
modifiers: read_modifiers(native_event),
})
- }),
- _ => None,
+ })
}
+ NSEventType::NSMouseMoved => window_height.map(|window_height| {
+ PlatformInput::MouseMove(MouseMoveEvent {
+ position: point(
+ px(native_event.locationInWindow().x as f32),
+ window_height - px(native_event.locationInWindow().y as f32),
+ ),
+ pressed_button: None,
+ modifiers: read_modifiers(native_event),
+ })
+ }),
+ NSEventType::NSMouseExited => window_height.map(|window_height| {
+ PlatformInput::MouseExited(MouseExitEvent {
+ position: point(
+ px(native_event.locationInWindow().x as f32),
+ window_height - px(native_event.locationInWindow().y as f32),
+ ),
+
+ pressed_button: None,
+ modifiers: read_modifiers(native_event),
+ })
+ }),
+ _ => None,
}
}
}
@@ -320,7 +318,7 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke {
unsafe {
use cocoa::appkit::*;
- let mut characters = native_event
+ let characters = native_event
.charactersIgnoringModifiers()
.to_str()
.to_string();
@@ -1,5 +1,9 @@
-//! Macos screen have a y axis that goings up from the bottom of the screen and
+#![cfg(target_os = "macos")]
+//! macOS platform implementation for GPUI.
+//!
+//! macOS screens have a y axis that goes up from the bottom of the screen and
//! an origin at the bottom left of the main display.
+
mod dispatcher;
mod display;
mod display_link;
@@ -13,7 +17,6 @@ mod screen_capture;
mod metal_atlas;
pub mod metal_renderer;
-use core_video::image_buffer::CVImageBuffer;
use metal_renderer as renderer;
#[cfg(feature = "font-kit")]
@@ -26,10 +29,9 @@ mod platform;
mod window;
mod window_appearance;
-use crate::{DevicePixels, Pixels, Size, px, size};
use cocoa::{
base::{id, nil},
- foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger},
+ foundation::{NSAutoreleasePool, NSNotFound, NSString, NSUInteger},
};
use objc::runtime::{BOOL, NO, YES};
@@ -48,8 +50,7 @@ pub(crate) use window::*;
#[cfg(feature = "font-kit")]
pub(crate) use text_system::*;
-/// A frame of video captured from a screen.
-pub(crate) type PlatformScreenCaptureFrame = CVImageBuffer;
+pub use platform::MacPlatform;
trait BoolExt {
fn to_objc(self) -> BOOL;
@@ -133,26 +134,3 @@ unsafe impl objc::Encode for NSRange {
unsafe fn ns_string(string: &str) -> id {
unsafe { NSString::alloc(nil).init_str(string).autorelease() }
}
-
-impl From<NSSize> for Size<Pixels> {
- fn from(value: NSSize) -> Self {
- Size {
- width: px(value.width as f32),
- height: px(value.height as f32),
- }
- }
-}
-
-impl From<NSRect> for Size<Pixels> {
- fn from(rect: NSRect) -> Self {
- let NSSize { width, height } = rect.size;
- size(width.into(), height.into())
- }
-}
-
-impl From<NSRect> for Size<DevicePixels> {
- fn from(rect: NSRect) -> Self {
- let NSSize { width, height } = rect.size;
- size(DevicePixels(width as i32), DevicePixels(height as i32))
- }
-}
@@ -3,9 +3,9 @@ use std::ffi::{CStr, c_void};
use objc::{msg_send, runtime::Object, sel, sel_impl};
-use crate::{KeybindingKeystroke, Keystroke, PlatformKeyboardLayout, PlatformKeyboardMapper};
+use gpui::{KeybindingKeystroke, Keystroke, PlatformKeyboardLayout, PlatformKeyboardMapper};
-use super::{
+use crate::{
TISCopyCurrentKeyboardLayoutInputSource, TISGetInputSourceProperty, kTISPropertyInputSourceID,
kTISPropertyLocalizedName,
};
@@ -1,11 +1,11 @@
-use crate::{
- AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
- Point, Size, platform::AtlasTextureList,
-};
use anyhow::{Context as _, Result};
use collections::FxHashMap;
use derive_more::{Deref, DerefMut};
use etagere::BucketedAtlasAllocator;
+use gpui::{
+ AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTextureList, AtlasTile, Bounds, DevicePixels,
+ PlatformAtlas, Point, Size,
+};
use metal::Device;
use parking_lot::Mutex;
use std::borrow::Cow;
@@ -164,7 +164,7 @@ impl MetalAtlasState {
index: index.unwrap_or(texture_list.textures.len()) as u32,
kind,
},
- allocator: etagere::BucketedAtlasAllocator::new(size.into()),
+ allocator: etagere::BucketedAtlasAllocator::new(size_to_etagere(size)),
metal_texture: AssertSend(metal_texture),
live_atlas_keys: 0,
};
@@ -183,9 +183,9 @@ impl MetalAtlasState {
fn texture(&self, id: AtlasTextureId) -> &MetalAtlasTexture {
let textures = match id.kind {
- crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
- crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
- crate::AtlasTextureKind::Subpixel => unreachable!(),
+ AtlasTextureKind::Monochrome => &self.monochrome_textures,
+ AtlasTextureKind::Polychrome => &self.polychrome_textures,
+ AtlasTextureKind::Subpixel => unreachable!(),
};
textures[id.index as usize].as_ref().unwrap()
}
@@ -200,12 +200,12 @@ struct MetalAtlasTexture {
impl MetalAtlasTexture {
fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {
- let allocation = self.allocator.allocate(size.into())?;
+ let allocation = self.allocator.allocate(size_to_etagere(size))?;
let tile = AtlasTile {
texture_id: self.id,
tile_id: allocation.id.into(),
bounds: Bounds {
- origin: allocation.rectangle.min.into(),
+ origin: point_from_etagere(allocation.rectangle.min),
size,
},
padding: 0,
@@ -247,36 +247,14 @@ impl MetalAtlasTexture {
}
}
-impl From<Size<DevicePixels>> for etagere::Size {
- fn from(size: Size<DevicePixels>) -> Self {
- etagere::Size::new(size.width.into(), size.height.into())
- }
+fn size_to_etagere(size: Size<DevicePixels>) -> etagere::Size {
+ etagere::Size::new(size.width.into(), size.height.into())
}
-impl From<etagere::Point> for Point<DevicePixels> {
- fn from(value: etagere::Point) -> Self {
- Point {
- x: DevicePixels::from(value.x),
- y: DevicePixels::from(value.y),
- }
- }
-}
-
-impl From<etagere::Size> for Size<DevicePixels> {
- fn from(size: etagere::Size) -> Self {
- Size {
- width: DevicePixels::from(size.width),
- height: DevicePixels::from(size.height),
- }
- }
-}
-
-impl From<etagere::Rectangle> for Bounds<DevicePixels> {
- fn from(rectangle: etagere::Rectangle) -> Self {
- Bounds {
- origin: rectangle.min.into(),
- size: rectangle.size().into(),
- }
+fn point_from_etagere(value: etagere::Point) -> Point<DevicePixels> {
+ Point {
+ x: DevicePixels::from(value.x),
+ y: DevicePixels::from(value.y),
}
}
@@ -1,9 +1,4 @@
-use super::metal_atlas::MetalAtlas;
-use crate::{
- AtlasTextureId, Background, Bounds, ContentMask, DevicePixels, MonochromeSprite, PaintSurface,
- Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
- Surface, Underline, point, size,
-};
+use crate::metal_atlas::MetalAtlas;
use anyhow::Result;
use block::ConcreteBlock;
use cocoa::{
@@ -11,6 +6,11 @@ use cocoa::{
foundation::{NSSize, NSUInteger},
quartzcore::AutoresizingMask,
};
+use gpui::{
+ AtlasTextureId, Background, Bounds, ContentMask, DevicePixels, MonochromeSprite, PaintSurface,
+ Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
+ Surface, Underline, point, size,
+};
#[cfg(any(test, feature = "test-support"))]
use image::RgbaImage;
@@ -30,7 +30,7 @@ use parking_lot::Mutex;
use std::{cell::Cell, ffi::c_void, mem, ptr, sync::Arc};
// Exported to metal
-pub(crate) type PointF = crate::Point<f32>;
+pub(crate) type PointF = gpui::Point<f32>;
#[cfg(not(feature = "runtime_shaders"))]
const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
@@ -40,14 +40,14 @@ const SHADERS_SOURCE_FILE: &str = include_str!(concat!(env!("OUT_DIR"), "/stitch
// https://developer.apple.com/documentation/metal/mtldevice/1433355-supportstexturesamplecount
const PATH_SAMPLE_COUNT: u32 = 4;
-pub type Context = Arc<Mutex<InstanceBufferPool>>;
-pub type Renderer = MetalRenderer;
+pub(crate) type Context = Arc<Mutex<InstanceBufferPool>>;
+pub(crate) type Renderer = MetalRenderer;
-pub unsafe fn new_renderer(
+pub(crate) unsafe fn new_renderer(
context: self::Context,
_native_window: *mut c_void,
_native_view: *mut c_void,
- _bounds: crate::Size<f32>,
+ _bounds: gpui::Size<f32>,
transparent: bool,
) -> Renderer {
MetalRenderer::new(context, transparent)
@@ -349,7 +349,7 @@ impl MetalRenderer {
self.path_intermediate_texture = Some(self.device.new_texture(&texture_descriptor));
if self.path_sample_count > 1 {
- let mut msaa_descriptor = texture_descriptor;
+ let msaa_descriptor = texture_descriptor;
msaa_descriptor.set_texture_type(metal::MTLTextureType::D2Multisample);
msaa_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
msaa_descriptor.set_sample_count(self.path_sample_count as _);
@@ -1,6 +1,5 @@
#![allow(unused, non_upper_case_globals)]
-use crate::{FontFallbacks, FontFeatures};
use cocoa::appkit::CGFloat;
use core_foundation::{
array::{
@@ -25,6 +24,7 @@ use core_text::{
},
};
use font_kit::font::Font as FontKitFont;
+use gpui::{FontFallbacks, FontFeatures};
use std::ptr;
pub fn apply_features_and_fallbacks(
@@ -9,10 +9,8 @@ use cocoa::{
use objc::{msg_send, runtime::Object, sel, sel_impl};
use strum::IntoEnumIterator as _;
-use crate::{
- ClipboardEntry, ClipboardItem, ClipboardString, Image, ImageFormat, asset_cache::hash,
- platform::mac::ns_string,
-};
+use crate::ns_string;
+use gpui::{ClipboardEntry, ClipboardItem, ClipboardString, Image, ImageFormat, hash};
pub struct Pasteboard {
inner: id,
@@ -77,7 +75,7 @@ impl Pasteboard {
}
fn read_image(&self, format: ImageFormat) -> Option<ClipboardItem> {
- let mut ut_type: UTType = format.into();
+ let ut_type: UTType = format.into();
unsafe {
let types: id = self.inner.types();
@@ -304,7 +302,7 @@ impl UTType {
mod tests {
use cocoa::{appkit::NSPasteboardTypeString, foundation::NSData};
- use crate::{ClipboardEntry, ClipboardItem, ClipboardString};
+ use gpui::{ClipboardEntry, ClipboardItem, ClipboardString};
use super::*;
@@ -1,12 +1,6 @@
-use super::{
- BoolExt, MacKeyboardLayout, MacKeyboardMapper, events::key_to_native, ns_string, renderer,
-};
use crate::{
- Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
- KeyContext, Keymap, MacDispatcher, MacDisplay, MacWindow, Menu, MenuItem, OsMenu, OwnedMenu,
- PathPromptOptions, Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper,
- PlatformTextSystem, PlatformWindow, Result, SystemMenuType, Task, ThermalState,
- WindowAppearance, WindowParams, platform::mac::pasteboard::Pasteboard,
+ BoolExt, MacDispatcher, MacDisplay, MacKeyboardLayout, MacKeyboardMapper, MacWindow,
+ events::key_to_native, ns_string, pasteboard::Pasteboard, renderer,
};
use anyhow::{Context as _, anyhow};
use block::ConcreteBlock;
@@ -31,6 +25,12 @@ use core_foundation::{
};
use ctor::ctor;
use futures::channel::oneshot;
+use gpui::{
+ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
+ KeyContext, Keymap, Menu, MenuItem, OsMenu, OwnedMenu, PathPromptOptions, Platform,
+ PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem,
+ PlatformWindow, Result, SystemMenuType, Task, ThermalState, WindowAppearance, WindowParams,
+};
use itertools::Itertools;
use objc::{
class,
@@ -154,7 +154,7 @@ unsafe fn build_classes() {
}
}
-pub(crate) struct MacPlatform(Mutex<MacPlatformState>);
+pub struct MacPlatform(Mutex<MacPlatformState>);
pub(crate) struct MacPlatformState {
background_executor: BackgroundExecutor,
@@ -180,14 +180,14 @@ pub(crate) struct MacPlatformState {
}
impl MacPlatform {
- pub(crate) fn new(headless: bool) -> Self {
+ pub fn new(headless: bool) -> Self {
let dispatcher = Arc::new(MacDispatcher::new());
#[cfg(feature = "font-kit")]
let text_system = Arc::new(crate::MacTextSystem::new());
#[cfg(not(feature = "font-kit"))]
- let text_system = Arc::new(crate::NoopTextSystem::new());
+ let text_system = Arc::new(gpui::NoopTextSystem::new());
let keyboard_layout = MacKeyboardLayout::new();
let keyboard_mapper = Rc::new(MacKeyboardMapper::new(keyboard_layout.id()));
@@ -321,14 +321,14 @@ impl MacPlatform {
.map(|binding| binding.keystrokes());
let selector = match os_action {
- Some(crate::OsAction::Cut) => selector("cut:"),
- Some(crate::OsAction::Copy) => selector("copy:"),
- Some(crate::OsAction::Paste) => selector("paste:"),
- Some(crate::OsAction::SelectAll) => selector("selectAll:"),
+ Some(gpui::OsAction::Cut) => selector("cut:"),
+ Some(gpui::OsAction::Copy) => selector("copy:"),
+ Some(gpui::OsAction::Paste) => selector("paste:"),
+ Some(gpui::OsAction::SelectAll) => selector("selectAll:"),
// "undo:" and "redo:" are always disabled in our case, as
// we don't have a NSTextView/NSTextField to enable them on.
- Some(crate::OsAction::Undo) => selector("handleGPUIMenuItem:"),
- Some(crate::OsAction::Redo) => selector("handleGPUIMenuItem:"),
+ Some(gpui::OsAction::Undo) => selector("handleGPUIMenuItem:"),
+ Some(gpui::OsAction::Redo) => selector("handleGPUIMenuItem:"),
None => selector("handleGPUIMenuItem:"),
};
@@ -448,7 +448,7 @@ impl Platform for MacPlatform {
self.0.lock().background_executor.clone()
}
- fn foreground_executor(&self) -> crate::ForegroundExecutor {
+ fn foreground_executor(&self) -> gpui::ForegroundExecutor {
self.0.lock().foreground_executor.clone()
}
@@ -493,7 +493,7 @@ impl Platform for MacPlatform {
// this, we make quitting the application asynchronous so that we aren't holding borrows to
// the app state on the stack when we actually terminate the app.
- use super::dispatcher::{dispatch_get_main_queue, dispatch_sys::dispatch_async_f};
+ use crate::dispatcher::{dispatch_get_main_queue, dispatch_sys::dispatch_async_f};
unsafe {
dispatch_async_f(dispatch_get_main_queue(), ptr::null_mut(), Some(quit));
@@ -587,14 +587,14 @@ impl Platform for MacPlatform {
#[cfg(feature = "screen-capture")]
fn is_screen_capture_supported(&self) -> bool {
let min_version = cocoa::foundation::NSOperatingSystemVersion::new(12, 3, 0);
- super::is_macos_version_at_least(min_version)
+ crate::is_macos_version_at_least(min_version)
}
#[cfg(feature = "screen-capture")]
fn screen_capture_sources(
&self,
- ) -> oneshot::Receiver<Result<Vec<Rc<dyn crate::ScreenCaptureSource>>>> {
- super::screen_capture::get_sources()
+ ) -> oneshot::Receiver<Result<Vec<Rc<dyn gpui::ScreenCaptureSource>>>> {
+ crate::screen_capture::get_sources()
}
fn active_window(&self) -> Option<AnyWindowHandle> {
@@ -626,7 +626,7 @@ impl Platform for MacPlatform {
unsafe {
let app = NSApplication::sharedApplication(nil);
let appearance: id = msg_send![app, effectiveAppearance];
- WindowAppearance::from_native(appearance)
+ crate::window_appearance::window_appearance_from_native(appearance)
}
}
@@ -1261,7 +1261,7 @@ extern "C" fn on_thermal_state_change(this: &mut Object, _: Sel, _: id) {
// Defer to the next run loop iteration to avoid re-entrant borrows of the App RefCell,
// as NSNotificationCenter delivers this notification synchronously and it may fire while
// the App is already borrowed (same pattern as quit() above).
- use super::dispatcher::{dispatch_get_main_queue, dispatch_sys::dispatch_async_f};
+ use crate::dispatcher::{dispatch_get_main_queue, dispatch_sys::dispatch_async_f};
let platform = unsafe { get_mac_platform(this) };
let platform_ptr = platform as *const MacPlatform as *mut c_void;
@@ -1367,7 +1367,7 @@ extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) {
extern "C" fn handle_dock_menu(this: &mut Object, _: Sel, _: id) -> id {
unsafe {
let platform = get_mac_platform(this);
- let mut state = platform.0.lock();
+ let state = platform.0.lock();
if let Some(id) = state.dock_menu {
id
} else {
@@ -1,9 +1,4 @@
-use super::ns_string;
-use crate::{
- DevicePixels, ForegroundExecutor, SharedString, SourceMetadata,
- platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream},
- size,
-};
+use crate::ns_string;
use anyhow::{Result, anyhow};
use block::ConcreteBlock;
use cocoa::{
@@ -18,6 +13,10 @@ use core_graphics::display::{
};
use ctor::ctor;
use futures::channel::oneshot;
+use gpui::{
+ DevicePixels, ForegroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream,
+ SharedString, SourceMetadata, size,
+};
use media::core_media::{CMSampleBuffer, CMSampleBufferRef};
use metal::NSInteger;
use objc::{
@@ -29,7 +28,7 @@ use objc::{
};
use std::{cell::RefCell, ffi::c_void, mem, ptr, rc::Rc};
-use super::NSStringExt;
+use crate::NSStringExt;
#[derive(Clone)]
pub struct MacScreenCaptureSource {
@@ -116,7 +115,7 @@ impl ScreenCaptureSource for MacScreenCaptureSource {
let _: () = msg_send![configuration, release];
let _: () = msg_send![delegate, release];
- let (mut tx, rx) = oneshot::channel();
+ let (tx, rx) = oneshot::channel();
let mut error: id = nil;
let _: () = msg_send![stream, addStreamOutput:output type:SCStreamOutputTypeScreen sampleHandlerQueue:0 error:&mut error as *mut id];
@@ -243,11 +242,11 @@ unsafe fn screen_id_to_human_label() -> HashMap<CGDirectDisplayID, ScreenMeta> {
pub(crate) fn get_sources() -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
unsafe {
- let (mut tx, rx) = oneshot::channel();
+ let (tx, rx) = oneshot::channel();
let tx = Rc::new(RefCell::new(Some(tx)));
let screen_id_to_label = screen_id_to_human_label();
let block = ConcreteBlock::new(move |shareable_content: id, error: id| {
- let Some(mut tx) = tx.borrow_mut().take() else {
+ let Some(tx) = tx.borrow_mut().take() else {
return;
};
@@ -1,9 +1,3 @@
-use crate::{
- Bounds, DevicePixels, Font, FontFallbacks, FontFeatures, FontId, FontMetrics, FontRun,
- FontStyle, FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, Point,
- RenderGlyphParams, Result, SUBPIXEL_VARIANTS_X, ShapedGlyph, ShapedRun, SharedString, Size,
- TextRenderingMode, point, px, size, swap_rgba_pa_to_bgra,
-};
use anyhow::anyhow;
use cocoa::appkit::CGFloat;
use collections::HashMap;
@@ -39,16 +33,22 @@ use font_kit::{
source::SystemSource,
sources::mem::MemSource,
};
+use gpui::{
+ Bounds, DevicePixels, Font, FontFallbacks, FontFeatures, FontId, FontMetrics, FontRun,
+ FontStyle, FontWeight, GlyphId, LineLayout, Pixels, PlatformTextSystem, RenderGlyphParams,
+ Result, SUBPIXEL_VARIANTS_X, ShapedGlyph, ShapedRun, SharedString, Size, TextRenderingMode,
+ point, px, size, swap_rgba_pa_to_bgra,
+};
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use pathfinder_geometry::{
rect::{RectF, RectI},
transform2d::Transform2F,
- vector::{Vector2F, Vector2I},
+ vector::Vector2F,
};
use smallvec::SmallVec;
use std::{borrow::Cow, char, convert::TryFrom, sync::Arc};
-use super::open_type::apply_features_and_fallbacks;
+use crate::open_type::apply_features_and_fallbacks;
#[allow(non_upper_case_globals)]
const kCGImageAlphaOnly: u32 = 7;
@@ -159,8 +159,8 @@ impl PlatformTextSystem for MacTextSystem {
let ix = font_kit::matching::find_best_match(
&candidate_properties,
&font_kit::properties::Properties {
- style: font.style.into(),
- weight: font.weight.into(),
+ style: fontkit_style(font.style),
+ weight: fontkit_weight(font.weight),
stretch: Default::default(),
},
)?;
@@ -172,13 +172,13 @@ impl PlatformTextSystem for MacTextSystem {
}
fn font_metrics(&self, font_id: FontId) -> FontMetrics {
- self.0.read().fonts[font_id.0].metrics().into()
+ font_kit_metrics_to_metrics(self.0.read().fonts[font_id.0].metrics())
}
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
- Ok(self.0.read().fonts[font_id.0]
- .typographic_bounds(glyph_id.0)?
- .into())
+ Ok(bounds_from_rect(
+ self.0.read().fonts[font_id.0].typographic_bounds(glyph_id.0)?,
+ ))
}
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
@@ -241,7 +241,7 @@ impl MacTextSystemState {
features: &FontFeatures,
fallbacks: Option<&FontFallbacks>,
) -> Result<SmallVec<[FontId; 4]>> {
- let name = crate::text_system::font_name_with_fallbacks(name, ".AppleSystemUIFont");
+ let name = gpui::font_name_with_fallbacks(name, ".AppleSystemUIFont");
let mut font_ids = SmallVec::new();
let family = self
@@ -321,7 +321,9 @@ impl MacTextSystemState {
}
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
- Ok(self.fonts[font_id.0].advance(glyph_id.0)?.into())
+ Ok(size_from_vector2f(
+ self.fonts[font_id.0].advance(glyph_id.0)?,
+ ))
}
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
@@ -357,15 +359,13 @@ impl MacTextSystemState {
fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
let font = &self.fonts[params.font_id.0];
let scale = Transform2F::from_scale(params.scale_factor);
- Ok(font
- .raster_bounds(
- params.glyph_id.0,
- params.font_size.into(),
- scale,
- HintingOptions::None,
- font_kit::canvas::RasterizationOptions::GrayscaleAa,
- )?
- .into())
+ Ok(bounds_from_rect_i(font.raster_bounds(
+ params.glyph_id.0,
+ params.font_size.into(),
+ scale,
+ HintingOptions::None,
+ font_kit::canvas::RasterizationOptions::GrayscaleAa,
+ )?))
}
fn rasterize_glyph(
@@ -480,12 +480,12 @@ impl MacTextSystemState {
let font = &self.fonts[run.font_id.0];
let font_metrics = font.metrics();
- let font_scale = font_size.0 / font_metrics.units_per_em as f32;
+ let font_scale = f32::from(font_size) / font_metrics.units_per_em as f32;
max_ascent = max_ascent.max(font_metrics.ascent * font_scale);
max_descent = max_descent.max(-font_metrics.descent * font_scale);
let font_size = if break_ligature {
- px(font_size.0.next_up())
+ px(f32::from(font_size).next_up())
} else {
font_size
};
@@ -514,7 +514,7 @@ impl MacTextSystemState {
};
let font_id = self.id_for_native_font(font);
- let mut glyphs = match runs.last_mut() {
+ let glyphs = match runs.last_mut() {
Some(run) if run.font_id == font_id => &mut run.glyphs,
_ => {
runs.push(ShapedRun {
@@ -530,7 +530,7 @@ impl MacTextSystemState {
.zip(run.positions().iter())
.zip(run.string_indices().iter())
{
- let mut glyph_utf16_ix = usize::try_from(glyph_utf16_ix).unwrap();
+ let glyph_utf16_ix = usize::try_from(glyph_utf16_ix).unwrap();
if ix_converter.utf16_ix > glyph_utf16_ix {
// We cannot reuse current index converter, as it can only seek forward. Restart the search.
ix_converter = StringIndexConverter::new(text);
@@ -586,80 +586,68 @@ impl<'a> StringIndexConverter<'a> {
}
}
-impl From<Metrics> for FontMetrics {
- fn from(metrics: Metrics) -> Self {
- FontMetrics {
- units_per_em: metrics.units_per_em,
- ascent: metrics.ascent,
- descent: metrics.descent,
- line_gap: metrics.line_gap,
- underline_position: metrics.underline_position,
- underline_thickness: metrics.underline_thickness,
- cap_height: metrics.cap_height,
- x_height: metrics.x_height,
- bounding_box: metrics.bounding_box.into(),
- }
- }
-}
-
-impl From<RectF> for Bounds<f32> {
- fn from(rect: RectF) -> Self {
- Bounds {
- origin: point(rect.origin_x(), rect.origin_y()),
- size: size(rect.width(), rect.height()),
- }
- }
-}
-
-impl From<RectI> for Bounds<DevicePixels> {
- fn from(rect: RectI) -> Self {
- Bounds {
- origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())),
- size: size(DevicePixels(rect.width()), DevicePixels(rect.height())),
- }
- }
-}
-
-impl From<Vector2I> for Size<DevicePixels> {
- fn from(value: Vector2I) -> Self {
- size(value.x().into(), value.y().into())
+fn font_kit_metrics_to_metrics(metrics: Metrics) -> FontMetrics {
+ FontMetrics {
+ units_per_em: metrics.units_per_em,
+ ascent: metrics.ascent,
+ descent: metrics.descent,
+ line_gap: metrics.line_gap,
+ underline_position: metrics.underline_position,
+ underline_thickness: metrics.underline_thickness,
+ cap_height: metrics.cap_height,
+ x_height: metrics.x_height,
+ bounding_box: bounds_from_rect(metrics.bounding_box),
}
}
-impl From<RectI> for Bounds<i32> {
- fn from(rect: RectI) -> Self {
- Bounds {
- origin: point(rect.origin_x(), rect.origin_y()),
- size: size(rect.width(), rect.height()),
- }
+fn bounds_from_rect(rect: RectF) -> Bounds<f32> {
+ Bounds {
+ origin: point(rect.origin_x(), rect.origin_y()),
+ size: size(rect.width(), rect.height()),
}
}
-impl From<Point<u32>> for Vector2I {
- fn from(size: Point<u32>) -> Self {
- Vector2I::new(size.x as i32, size.y as i32)
+fn bounds_from_rect_i(rect: RectI) -> Bounds<DevicePixels> {
+ Bounds {
+ origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())),
+ size: size(DevicePixels(rect.width()), DevicePixels(rect.height())),
}
}
-impl From<Vector2F> for Size<f32> {
- fn from(vec: Vector2F) -> Self {
- size(vec.x(), vec.y())
- }
+// impl From<Vector2I> for Size<DevicePixels> {
+// fn from(value: Vector2I) -> Self {
+// size(value.x().into(), value.y().into())
+// }
+// }
+
+// impl From<RectI> for Bounds<i32> {
+// fn from(rect: RectI) -> Self {
+// Bounds {
+// origin: point(rect.origin_x(), rect.origin_y()),
+// size: size(rect.width(), rect.height()),
+// }
+// }
+// }
+
+// impl From<Point<u32>> for Vector2I {
+// fn from(size: Point<u32>) -> Self {
+// Vector2I::new(size.x as i32, size.y as i32)
+// }
+// }
+
+fn size_from_vector2f(vec: Vector2F) -> Size<f32> {
+ size(vec.x(), vec.y())
}
-impl From<FontWeight> for FontkitWeight {
- fn from(value: FontWeight) -> Self {
- FontkitWeight(value.0)
- }
+fn fontkit_weight(value: FontWeight) -> FontkitWeight {
+ FontkitWeight(value.0)
}
-impl From<FontStyle> for FontkitStyle {
- fn from(style: FontStyle) -> Self {
- match style {
- FontStyle::Normal => FontkitStyle::Normal,
- FontStyle::Italic => FontkitStyle::Italic,
- FontStyle::Oblique => FontkitStyle::Oblique,
- }
+fn fontkit_style(style: FontStyle) -> FontkitStyle {
+ match style {
+ FontStyle::Normal => FontkitStyle::Normal,
+ FontStyle::Italic => FontkitStyle::Italic,
+ FontStyle::Oblique => FontkitStyle::Oblique,
}
}
@@ -706,7 +694,8 @@ mod lenient_font_attributes {
#[cfg(test)]
mod tests {
- use crate::{FontRun, GlyphId, MacTextSystem, PlatformTextSystem, font, px};
+ use crate::MacTextSystem;
+ use gpui::{FontRun, GlyphId, PlatformTextSystem, font, px};
#[test]
fn test_layout_line_bom_char() {
@@ -1,13 +1,7 @@
-use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};
use crate::{
- AnyWindowHandle, BackgroundExecutor, Bounds, Capslock, DisplayLink, ExternalPaths,
- FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent,
- MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas,
- PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel,
- RequestFrameOptions, SharedString, Size, SystemWindowTab, WindowAppearance,
- WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowKind, WindowParams,
- dispatch_get_main_queue, dispatch_sys::dispatch_async_f, platform::PlatformInputHandler, point,
- px, size,
+ BoolExt, DisplayLink, MacDisplay, NSRange, NSStringExt, dispatch_get_main_queue,
+ dispatcher::dispatch_sys::dispatch_async_f, events::platform_input_from_native, ns_string,
+ renderer,
};
#[cfg(any(test, feature = "test-support"))]
use anyhow::Result;
@@ -28,6 +22,15 @@ use cocoa::{
NSUserDefaults,
},
};
+use gpui::{
+ AnyWindowHandle, BackgroundExecutor, Bounds, Capslock, ExternalPaths, FileDropEvent,
+ ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
+ MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
+ PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptButton, PromptLevel,
+ RequestFrameOptions, SharedString, Size, SystemWindowTab, WindowAppearance,
+ WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowKind, WindowParams, point,
+ px, size,
+};
#[cfg(any(test, feature = "test-support"))]
use image::RgbaImage;
@@ -408,7 +411,7 @@ struct MacWindowState {
display_link: Option<DisplayLink>,
renderer: renderer::Renderer,
request_frame_callback: Option<Box<dyn FnMut(RequestFrameOptions)>>,
- event_callback: Option<Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>>,
+ event_callback: Option<Box<dyn FnMut(PlatformInput) -> gpui::DispatchEventResult>>,
activate_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
moved_callback: Option<Box<dyn FnMut()>>,
@@ -514,9 +517,14 @@ impl MacWindowState {
}
fn is_maximized(&self) -> bool {
+ fn rect_to_size(rect: NSRect) -> Size<Pixels> {
+ let NSSize { width, height } = rect.size;
+ size(width.into(), height.into())
+ }
+
unsafe {
let bounds = self.bounds();
- let screen_size = self.native_window.screen().visibleFrame().into();
+ let screen_size = rect_to_size(self.native_window.screen().visibleFrame());
bounds.size == screen_size
}
}
@@ -532,7 +540,7 @@ impl MacWindowState {
let mut window_frame = unsafe { NSWindow::frame(self.native_window) };
let screen = unsafe { NSWindow::screen(self.native_window) };
if screen == nil {
- return Bounds::new(point(px(0.), px(0.)), crate::DEFAULT_WINDOW_SIZE);
+ return Bounds::new(point(px(0.), px(0.)), gpui::DEFAULT_WINDOW_SIZE);
}
let screen_frame = unsafe { NSScreen::frame(screen) };
@@ -674,11 +682,14 @@ impl MacWindow {
let window_rect = NSRect::new(
NSPoint::new(
- screen_frame.origin.x + bounds.origin.x.0 as f64,
+ screen_frame.origin.x + bounds.origin.x.as_f32() as f64,
screen_frame.origin.y
- + (display.bounds().size.height - bounds.origin.y).0 as f64,
+ + (display.bounds().size.height - bounds.origin.y).as_f32() as f64,
+ ),
+ NSSize::new(
+ bounds.size.width.as_f32() as f64,
+ bounds.size.height.as_f32() as f64,
),
- NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
);
let native_window = native_window.initWithContentRect_styleMask_backing_defer_screen_(
@@ -717,7 +728,7 @@ impl MacWindow {
renderer_context,
native_window as *mut _,
native_view as *mut _,
- bounds.size.map(|pixels| pixels.0),
+ bounds.size.map(|pixels| pixels.as_f32()),
false,
),
request_frame_callback: None,
@@ -1029,8 +1040,8 @@ impl PlatformWindow for MacWindow {
.spawn(async move {
unsafe {
window.setContentSize_(NSSize {
- width: size.width.0 as f64,
- height: size.height.0 as f64,
+ width: size.width.as_f32() as f64,
+ height: size.height.as_f32() as f64,
});
}
})
@@ -1103,7 +1114,7 @@ impl PlatformWindow for MacWindow {
fn appearance(&self) -> WindowAppearance {
unsafe {
let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
- WindowAppearance::from_native(appearance)
+ crate::window_appearance::window_appearance_from_native(appearance)
}
}
@@ -1227,7 +1238,7 @@ impl PlatformWindow for MacWindow {
if answer.is_cancel() {
// Bind Escape Key to Cancel Button
- if let Some(key) = std::char::from_u32(super::events::ESCAPE_KEY as u32) {
+ if let Some(key) = std::char::from_u32(crate::events::ESCAPE_KEY as u32) {
let _: () =
msg_send![button, setKeyEquivalent: ns_string(&key.to_string())];
}
@@ -1443,7 +1454,7 @@ impl PlatformWindow for MacWindow {
self.0.as_ref().lock().request_frame_callback = Some(callback);
}
- fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>) {
+ fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> gpui::DispatchEventResult>) {
self.0.as_ref().lock().event_callback = Some(callback);
}
@@ -1532,7 +1543,7 @@ impl PlatformWindow for MacWindow {
self.0.as_ref().lock().toggle_tab_bar_callback = Some(callback);
}
- fn draw(&self, scene: &crate::Scene) {
+ fn draw(&self, scene: &gpui::Scene) {
let mut this = self.0.lock();
this.renderer.draw(scene);
}
@@ -1541,7 +1552,7 @@ impl PlatformWindow for MacWindow {
self.0.lock().renderer.sprite_atlas().clone()
}
- fn gpu_specs(&self) -> Option<crate::GpuSpecs> {
+ fn gpu_specs(&self) -> Option<gpui::GpuSpecs> {
None
}
@@ -1613,13 +1624,13 @@ impl PlatformWindow for MacWindow {
unsafe {
let app = NSApplication::sharedApplication(nil);
- let mut event: id = msg_send![app, currentEvent];
+ let event: id = msg_send![app, currentEvent];
let _: () = msg_send![window, performWindowDragWithEvent: event];
}
}
#[cfg(any(test, feature = "test-support"))]
- fn render_to_image(&self, scene: &crate::Scene) -> Result<RgbaImage> {
+ fn render_to_image(&self, scene: &gpui::Scene) -> Result<RgbaImage> {
let mut this = self.0.lock();
this.renderer.render_to_image(scene)
}
@@ -1742,7 +1753,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
let mut lock = window_state.as_ref().lock();
let window_height = lock.content_size().height;
- let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
+ let event = unsafe { platform_input_from_native(native_event, Some(window_height)) };
let Some(event) = event else {
return NO;
@@ -1760,7 +1771,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
};
match event {
- PlatformInput::KeyDown(mut key_down_event) => {
+ PlatformInput::KeyDown(key_down_event) => {
// For certain keystrokes, macOS will first dispatch a "key equivalent" event.
// If that event isn't handled, it will then dispatch a "key down" event. GPUI
// makes no distinction between these two types of events, so we need to ignore
@@ -1859,7 +1870,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
let weak_window_state = Arc::downgrade(&window_state);
let mut lock = window_state.as_ref().lock();
let window_height = lock.content_size().height;
- let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
+ let event = unsafe { platform_input_from_native(native_event, Some(window_height)) };
if let Some(mut event) = event {
match &mut event {
@@ -2023,7 +2034,7 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
let window_state = unsafe { get_window_state(this) };
- let mut lock = window_state.as_ref().lock();
+ let lock = window_state.as_ref().lock();
let min_version = NSOperatingSystemVersion::new(15, 3, 0);
@@ -2082,7 +2093,7 @@ extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
let window_state = unsafe { get_window_state(this) };
- let mut lock = window_state.lock();
+ let lock = window_state.lock();
let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
// When opening a pop-up while the application isn't active, Cocoa sends a spurious
@@ -2188,13 +2199,20 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
}
extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
+ fn convert(value: NSSize) -> Size<Pixels> {
+ Size {
+ width: px(value.width as f32),
+ height: px(value.height as f32),
+ }
+ }
+
let window_state = unsafe { get_window_state(this) };
let mut lock = window_state.as_ref().lock();
- let new_size = Size::<Pixels>::from(size);
+ let new_size = convert(size);
let old_size = unsafe {
let old_frame: NSRect = msg_send![this, frame];
- Size::<Pixels>::from(old_frame.size)
+ convert(old_frame.size)
};
if old_size == new_size {
@@ -2289,12 +2307,15 @@ extern "C" fn first_rect_for_character_range(
|bounds| {
NSRect::new(
NSPoint::new(
- frame.origin.x + bounds.origin.x.0 as f64,
+ frame.origin.x + bounds.origin.x.as_f32() as f64,
frame.origin.y + frame.size.height
- - bounds.origin.y.0 as f64
- - bounds.size.height.0 as f64,
+ - bounds.origin.y.as_f32() as f64
+ - bounds.size.height.as_f32() as f64,
+ ),
+ NSSize::new(
+ bounds.size.width.as_f32() as f64,
+ bounds.size.height.as_f32() as f64,
),
- NSSize::new(bounds.size.width.0 as f64, bounds.size.height.0 as f64),
)
},
)
@@ -2397,7 +2418,7 @@ extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
let mut event_callback = lock.event_callback.take();
drop(lock);
- if let Some((keystroke, mut callback)) = keystroke.zip(event_callback.as_mut()) {
+ if let Some((keystroke, callback)) = keystroke.zip(event_callback.as_mut()) {
let handled = (callback)(PlatformInput::KeyDown(KeyDownEvent {
keystroke,
is_held: false,
@@ -2532,7 +2553,7 @@ fn send_file_drop_event(
) -> bool {
let mut window_state = window_state.lock();
let window_event_callback = window_state.event_callback.as_mut();
- if let Some(mut callback) = window_event_callback {
+ if let Some(callback) = window_event_callback {
let external_files_dragged = match file_drop_event {
FileDropEvent::Entered { .. } => Some(true),
FileDropEvent::Exited => Some(false),
@@ -0,0 +1,35 @@
+use cocoa::{
+ appkit::{NSAppearanceNameVibrantDark, NSAppearanceNameVibrantLight},
+ base::id,
+ foundation::NSString,
+};
+use gpui::WindowAppearance;
+use objc::{msg_send, sel, sel_impl};
+use std::ffi::CStr;
+
+pub(crate) unsafe fn window_appearance_from_native(appearance: id) -> WindowAppearance {
+ let name: id = msg_send![appearance, name];
+ unsafe {
+ if name == NSAppearanceNameVibrantLight {
+ WindowAppearance::VibrantLight
+ } else if name == NSAppearanceNameVibrantDark {
+ WindowAppearance::VibrantDark
+ } else if name == NSAppearanceNameAqua {
+ WindowAppearance::Light
+ } else if name == NSAppearanceNameDarkAqua {
+ WindowAppearance::Dark
+ } else {
+ println!(
+ "unknown appearance: {:?}",
+ CStr::from_ptr(name.UTF8String())
+ );
+ WindowAppearance::Light
+ }
+ }
+}
+
+#[link(name = "AppKit", kind = "framework")]
+unsafe extern "C" {
+ pub static NSAppearanceNameAqua: id;
+ pub static NSAppearanceNameDarkAqua: id;
+}
@@ -0,0 +1,32 @@
+[package]
+name = "gpui_platform"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/gpui_platform.rs"
+
+[features]
+default = []
+font-kit = ["gpui_macos/font-kit"]
+test-support = ["gpui/test-support"]
+screen-capture = ["gpui/screen-capture", "gpui_macos/screen-capture", "gpui_windows/screen-capture", "gpui_linux/screen-capture"]
+wayland = ["gpui_linux/wayland"]
+x11 = ["gpui_linux/x11"]
+
+[dependencies]
+gpui.workspace = true
+
+[target.'cfg(target_os = "macos")'.dependencies]
+gpui_macos.workspace = true
+
+[target.'cfg(target_os = "windows")'.dependencies]
+gpui_windows.workspace = true
+
+[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
+gpui_linux.workspace = true
@@ -0,0 +1 @@
+../../LICENSE-APACHE
@@ -0,0 +1,150 @@
+//! Convenience crate that re-exports GPUI's platform traits and the
+//! `current_platform` constructor so consumers don't need `#[cfg]` gating.
+
+pub use gpui::Platform;
+
+use std::rc::Rc;
+
+/// Returns a background executor for the current platform.
+pub fn background_executor() -> gpui::BackgroundExecutor {
+ current_platform(true).background_executor()
+}
+
+pub fn application() -> gpui::Application {
+ gpui::Application::with_platform(current_platform(false))
+}
+
+pub fn headless() -> gpui::Application {
+ gpui::Application::with_platform(current_platform(true))
+}
+
+/// Returns the default [`Platform`] for the current OS.
+pub fn current_platform(headless: bool) -> Rc<dyn Platform> {
+ #[cfg(target_os = "macos")]
+ {
+ Rc::new(gpui_macos::MacPlatform::new(headless))
+ }
+
+ #[cfg(target_os = "windows")]
+ {
+ Rc::new(
+ gpui_windows::WindowsPlatform::new(headless)
+ .expect("failed to initialize Windows platform"),
+ )
+ }
+
+ #[cfg(not(any(target_os = "macos", target_os = "windows")))]
+ {
+ gpui_linux::current_platform(headless)
+ }
+}
+
+#[cfg(all(test, target_os = "macos"))]
+mod tests {
+ use super::*;
+ use gpui::{AppContext, Empty, VisualTestAppContext};
+ use std::cell::RefCell;
+ use std::time::Duration;
+
+ // Note: All VisualTestAppContext tests are ignored by default because they require
+ // the macOS main thread. Standard Rust tests run on worker threads, which causes
+ // SIGABRT when interacting with macOS AppKit/Cocoa APIs.
+ //
+ // To run these tests, use:
+ // cargo test -p gpui visual_test_context -- --ignored --test-threads=1
+
+ #[test]
+ #[ignore] // Requires macOS main thread
+ fn test_foreground_tasks_run_with_run_until_parked() {
+ let mut cx = VisualTestAppContext::new(current_platform(false));
+
+ let task_ran = Rc::new(RefCell::new(false));
+
+ // Spawn a foreground task via the App's spawn method
+ // This should use our TestDispatcher, not the MacDispatcher
+ {
+ let task_ran = task_ran.clone();
+ cx.update(|cx| {
+ cx.spawn(async move |_| {
+ *task_ran.borrow_mut() = true;
+ })
+ .detach();
+ });
+ }
+
+ // The task should not have run yet
+ assert!(!*task_ran.borrow());
+
+ // Run until parked should execute the foreground task
+ cx.run_until_parked();
+
+ // Now the task should have run
+ assert!(*task_ran.borrow());
+ }
+
+ #[test]
+ #[ignore] // Requires macOS main thread
+ fn test_advance_clock_triggers_delayed_tasks() {
+ let mut cx = VisualTestAppContext::new(current_platform(false));
+
+ let task_ran = Rc::new(RefCell::new(false));
+
+ // Spawn a task that waits for a timer
+ {
+ let task_ran = task_ran.clone();
+ let executor = cx.background_executor.clone();
+ cx.update(|cx| {
+ cx.spawn(async move |_| {
+ executor.timer(Duration::from_millis(500)).await;
+ *task_ran.borrow_mut() = true;
+ })
+ .detach();
+ });
+ }
+
+ // Run until parked - the task should be waiting on the timer
+ cx.run_until_parked();
+ assert!(!*task_ran.borrow());
+
+ // Advance clock past the timer duration
+ cx.advance_clock(Duration::from_millis(600));
+
+ // Now the task should have completed
+ assert!(*task_ran.borrow());
+ }
+
+ #[test]
+ #[ignore] // Requires macOS main thread - window creation fails on test threads
+ fn test_window_spawn_uses_test_dispatcher() {
+ let mut cx = VisualTestAppContext::new(current_platform(false));
+
+ let task_ran = Rc::new(RefCell::new(false));
+
+ let window = cx
+ .open_offscreen_window_default(|_, cx| cx.new(|_| Empty))
+ .expect("Failed to open window");
+
+ // Spawn a task via window.spawn - this is the critical test case
+ // for tooltip behavior, as tooltips use window.spawn for delayed show
+ {
+ let task_ran = task_ran.clone();
+ cx.update_window(window.into(), |_, window, cx| {
+ window
+ .spawn(cx, async move |_| {
+ *task_ran.borrow_mut() = true;
+ })
+ .detach();
+ })
+ .ok();
+ }
+
+ // The task should not have run yet
+ assert!(!*task_ran.borrow());
+
+ // Run until parked should execute the foreground task spawned via window
+ cx.run_until_parked();
+
+ // Now the task should have run
+ assert!(*task_ran.borrow());
+ }
+}
@@ -0,0 +1,26 @@
+[package]
+name = "gpui_wgpu"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/gpui_wgpu.rs"
+
+[target.'cfg(not(target_os = "windows"))'.dependencies]
+gpui.workspace = true
+anyhow.workspace = true
+bytemuck = "1"
+collections.workspace = true
+etagere = "0.2"
+log.workspace = true
+parking_lot.workspace = true
+profiling.workspace = true
+raw-window-handle = "0.6"
+smol.workspace = true
+util.workspace = true
+wgpu.workspace = true
@@ -0,0 +1 @@
+../../LICENSE-APACHE
@@ -0,0 +1,8 @@
+#![cfg(not(target_os = "windows"))]
+mod wgpu_atlas;
+mod wgpu_context;
+mod wgpu_renderer;
+
+pub use wgpu_atlas::*;
+pub use wgpu_context::*;
+pub use wgpu_renderer::*;
@@ -1,10 +1,10 @@
-use crate::{
- AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
- Point, Size, platform::AtlasTextureList,
-};
use anyhow::Result;
use collections::FxHashMap;
use etagere::{BucketedAtlasAllocator, size2};
+use gpui::{
+ AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTextureList, AtlasTile, Bounds, DevicePixels,
+ PlatformAtlas, Point, Size,
+};
use parking_lot::Mutex;
use std::{borrow::Cow, ops, sync::Arc};
@@ -19,7 +19,7 @@ fn etagere_point_to_device(point: etagere::Point) -> Point<DevicePixels> {
}
}
-pub(crate) struct WgpuAtlas(Mutex<WgpuAtlasState>);
+pub struct WgpuAtlas(Mutex<WgpuAtlasState>);
struct PendingUpload {
id: AtlasTextureId,
@@ -40,7 +40,7 @@ pub struct WgpuTextureInfo {
}
impl WgpuAtlas {
- pub(crate) fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self {
+ pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self {
WgpuAtlas(Mutex::new(WgpuAtlasState {
device,
queue,
@@ -1,10 +1,10 @@
-use super::{WgpuAtlas, WgpuContext};
-use crate::{
+use crate::{WgpuAtlas, WgpuContext};
+use bytemuck::{Pod, Zeroable};
+use gpui::{
AtlasTextureId, Background, Bounds, DevicePixels, GpuSpecs, MonochromeSprite, Path, Point,
PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, SubpixelSprite,
Underline, get_gamma_correction_ratios,
};
-use bytemuck::{Pod, Zeroable};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use std::num::NonZeroU64;
use std::sync::Arc;
@@ -0,0 +1,46 @@
+[package]
+name = "gpui_windows"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/gpui_windows.rs"
+
+[features]
+default = ["gpui/default"]
+test-support = ["gpui/test-support"]
+screen-capture = ["gpui/screen-capture", "scap"]
+
+[dependencies]
+gpui.workspace = true
+
+[target.'cfg(target_os = "windows")'.dependencies]
+anyhow.workspace = true
+collections.workspace = true
+etagere = "0.2"
+futures.workspace = true
+image.workspace = true
+itertools.workspace = true
+log.workspace = true
+parking_lot.workspace = true
+rand.workspace = true
+raw-window-handle = "0.6"
+smallvec.workspace = true
+util.workspace = true
+uuid.workspace = true
+windows.workspace = true
+windows-core.workspace = true
+windows-numerics = "0.2"
+windows-registry = "0.5"
+
+[target.'cfg(target_os = "windows")'.dependencies.scap]
+workspace = true
+optional = true
+
+[target.'cfg(target_os = "windows")'.build-dependencies]
+windows-registry = "0.5"
@@ -0,0 +1 @@
+../../LICENSE-APACHE
@@ -0,0 +1,242 @@
+#![allow(clippy::disallowed_methods, reason = "build scripts are exempt")]
+
+fn main() {
+ #[cfg(target_os = "windows")]
+ {
+ // Compile HLSL shaders
+ #[cfg(not(debug_assertions))]
+ compile_shaders();
+ }
+}
+
+#[cfg(all(target_os = "windows", not(debug_assertions)))]
+mod shader_compilation {
+ use std::{
+ fs,
+ io::Write,
+ path::{Path, PathBuf},
+ process::{self, Command},
+ };
+
+ pub fn compile_shaders() {
+ let shader_path =
+ PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("src/shaders.hlsl");
+ let out_dir = std::env::var("OUT_DIR").unwrap();
+
+ println!("cargo:rerun-if-changed={}", shader_path.display());
+
+ // Check if fxc.exe is available
+ let fxc_path = find_fxc_compiler();
+
+ // Define all modules
+ let modules = [
+ "quad",
+ "shadow",
+ "path_rasterization",
+ "path_sprite",
+ "underline",
+ "monochrome_sprite",
+ "subpixel_sprite",
+ "polychrome_sprite",
+ ];
+
+ let rust_binding_path = format!("{}/shaders_bytes.rs", out_dir);
+ if Path::new(&rust_binding_path).exists() {
+ fs::remove_file(&rust_binding_path)
+ .expect("Failed to remove existing Rust binding file");
+ }
+ for module in modules {
+ compile_shader_for_module(
+ module,
+ &out_dir,
+ &fxc_path,
+ shader_path.to_str().unwrap(),
+ &rust_binding_path,
+ );
+ }
+
+ {
+ let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
+ .join("src/color_text_raster.hlsl");
+ compile_shader_for_module(
+ "emoji_rasterization",
+ &out_dir,
+ &fxc_path,
+ shader_path.to_str().unwrap(),
+ &rust_binding_path,
+ );
+ }
+ }
+
+ /// Locate `binary` in the newest installed Windows SDK.
+ pub fn find_latest_windows_sdk_binary(
+ binary: &str,
+ ) -> Result<Option<PathBuf>, Box<dyn std::error::Error>> {
+ let key = windows_registry::LOCAL_MACHINE
+ .open("SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0")?;
+
+ let install_folder: String = key.get_string("InstallationFolder")?; // "C:\Program Files (x86)\Windows Kits\10\"
+ let install_folder_bin = Path::new(&install_folder).join("bin");
+
+ let mut versions: Vec<_> = std::fs::read_dir(&install_folder_bin)?
+ .flatten()
+ .filter(|entry| entry.path().is_dir())
+ .filter_map(|entry| entry.file_name().into_string().ok())
+ .collect();
+
+ versions.sort_by_key(|s| {
+ s.split('.')
+ .filter_map(|p| p.parse().ok())
+ .collect::<Vec<u32>>()
+ });
+
+ let arch = match std::env::consts::ARCH {
+ "x86_64" => "x64",
+ "aarch64" => "arm64",
+ _ => Err(format!(
+ "Unsupported architecture: {}",
+ std::env::consts::ARCH
+ ))?,
+ };
+
+ if let Some(highest_version) = versions.last() {
+ return Ok(Some(
+ install_folder_bin
+ .join(highest_version)
+ .join(arch)
+ .join(binary),
+ ));
+ }
+
+ Ok(None)
+ }
+
+ /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler.
+ fn find_fxc_compiler() -> String {
+ // Check environment variable
+ if let Ok(path) = std::env::var("GPUI_FXC_PATH")
+ && Path::new(&path).exists()
+ {
+ return path;
+ }
+
+ // Try to find in PATH
+ // NOTE: This has to be `where.exe` on Windows, not `where`, it must be ended with `.exe`
+ if let Ok(output) = std::process::Command::new("where.exe")
+ .arg("fxc.exe")
+ .output()
+ && output.status.success()
+ {
+ let path = String::from_utf8_lossy(&output.stdout);
+ return path.trim().to_string();
+ }
+
+ if let Ok(Some(path)) = find_latest_windows_sdk_binary("fxc.exe") {
+ return path.to_string_lossy().into_owned();
+ }
+
+ panic!("Failed to find fxc.exe");
+ }
+
+ fn compile_shader_for_module(
+ module: &str,
+ out_dir: &str,
+ fxc_path: &str,
+ shader_path: &str,
+ rust_binding_path: &str,
+ ) {
+ // Compile vertex shader
+ let output_file = format!("{}/{}_vs.h", out_dir, module);
+ let const_name = format!("{}_VERTEX_BYTES", module.to_uppercase());
+ compile_shader_impl(
+ fxc_path,
+ &format!("{module}_vertex"),
+ &output_file,
+ &const_name,
+ shader_path,
+ "vs_4_1",
+ );
+ generate_rust_binding(&const_name, &output_file, rust_binding_path);
+
+ // Compile fragment shader
+ let output_file = format!("{}/{}_ps.h", out_dir, module);
+ let const_name = format!("{}_FRAGMENT_BYTES", module.to_uppercase());
+ compile_shader_impl(
+ fxc_path,
+ &format!("{module}_fragment"),
+ &output_file,
+ &const_name,
+ shader_path,
+ "ps_4_1",
+ );
+ generate_rust_binding(&const_name, &output_file, rust_binding_path);
+ }
+
+ fn compile_shader_impl(
+ fxc_path: &str,
+ entry_point: &str,
+ output_path: &str,
+ var_name: &str,
+ shader_path: &str,
+ target: &str,
+ ) {
+ let output = Command::new(fxc_path)
+ .args([
+ "/T",
+ target,
+ "/E",
+ entry_point,
+ "/Fh",
+ output_path,
+ "/Vn",
+ var_name,
+ "/O3",
+ shader_path,
+ ])
+ .output();
+
+ match output {
+ Ok(result) => {
+ if result.status.success() {
+ return;
+ }
+ println!(
+ "cargo::error=Shader compilation failed for {}:\n{}",
+ entry_point,
+ String::from_utf8_lossy(&result.stderr)
+ );
+ process::exit(1);
+ }
+ Err(e) => {
+ println!("cargo::error=Failed to run fxc for {}: {}", entry_point, e);
+ process::exit(1);
+ }
+ }
+ }
+
+ fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) {
+ let header_content = fs::read_to_string(head_file).expect("Failed to read header file");
+ let const_definition = {
+ let global_var_start = header_content.find("const BYTE").unwrap();
+ let global_var = &header_content[global_var_start..];
+ let equal = global_var.find('=').unwrap();
+ global_var[equal + 1..].trim()
+ };
+ let rust_binding = format!(
+ "const {}: &[u8] = &{}\n",
+ const_name,
+ const_definition.replace('{', "[").replace('}', "]")
+ );
+ let mut options = fs::OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(output_path)
+ .expect("Failed to open Rust binding file");
+ options
+ .write_all(rust_binding.as_bytes())
+ .expect("Failed to write Rust binding file");
+ }
+}
+
+#[cfg(all(target_os = "windows", not(debug_assertions)))]
+use shader_compilation::compile_shaders;
@@ -18,7 +18,7 @@ use windows::Win32::{
};
use windows_core::PCWSTR;
-use crate::{
+use gpui::{
ClipboardEntry, ClipboardItem, ClipboardString, ExternalPaths, Image, ImageFormat, hash,
};
@@ -229,7 +229,8 @@ fn write_image_to_clipboard(item: &Image) -> Result<()> {
}
fn convert_image_to_png_format(bytes: &[u8], image_format: ImageFormat) -> Result<Vec<u8>> {
- let image = image::load_from_memory_with_format(bytes, image_format.into())?;
+ let image =
+ image::load_from_memory_with_format(bytes, gpui_image_format_to_image(image_format))?;
let mut output_buf = Vec::new();
image.write_to(
&mut std::io::Cursor::new(&mut output_buf),
@@ -440,17 +441,15 @@ where
Some(result)
}
-impl From<ImageFormat> for image::ImageFormat {
- fn from(value: ImageFormat) -> Self {
- match value {
- ImageFormat::Png => image::ImageFormat::Png,
- ImageFormat::Jpeg => image::ImageFormat::Jpeg,
- ImageFormat::Webp => image::ImageFormat::WebP,
- ImageFormat::Gif => image::ImageFormat::Gif,
- // TODO: ImageFormat::Svg
- ImageFormat::Bmp => image::ImageFormat::Bmp,
- ImageFormat::Tiff => image::ImageFormat::Tiff,
- _ => unreachable!(),
- }
+fn gpui_image_format_to_image(value: ImageFormat) -> image::ImageFormat {
+ match value {
+ ImageFormat::Png => image::ImageFormat::Png,
+ ImageFormat::Jpeg => image::ImageFormat::Jpeg,
+ ImageFormat::Webp => image::ImageFormat::WebP,
+ ImageFormat::Gif => image::ImageFormat::Gif,
+ // TODO: ImageFormat::Svg
+ ImageFormat::Bmp => image::ImageFormat::Bmp,
+ ImageFormat::Tiff => image::ImageFormat::Tiff,
+ _ => unreachable!(),
}
}
@@ -20,7 +20,7 @@ use windows::{
core::{GUID, HSTRING, Interface},
};
-use crate::{Action, MenuItem, SharedString};
+use gpui::{Action, MenuItem, SharedString};
pub(crate) struct JumpList {
pub(crate) dock_menus: Vec<DockMenuItem>,
@@ -24,6 +24,7 @@ use windows::{
use windows_numerics::Vector2;
use crate::*;
+use gpui::*;
#[derive(Debug)]
struct FontInfo {
@@ -468,7 +469,7 @@ impl DirectWriteState {
let family = if family == SYSTEM_UI_FONT_NAME {
system_ui_font_name
} else {
- font_name_with_fallbacks_shared(&family, &system_ui_font_name)
+ gpui::font_name_with_fallbacks_shared(&family, &system_ui_font_name)
};
let fontset = unsafe { collection.GetFontSet().log_err()? };
let font_family_h = HSTRING::from(family.as_str());
@@ -476,9 +477,9 @@ impl DirectWriteState {
fontset
.GetMatchingFonts(
&font_family_h,
- weight.into(),
+ font_weight_to_dwrite(weight),
DWRITE_FONT_STRETCH_NORMAL,
- style.into(),
+ font_style_to_dwrite(style),
)
.log_err()?
};
@@ -542,7 +543,7 @@ impl DirectWriteState {
font_info.font_face.GetWeight(),
font_info.font_face.GetStyle(),
DWRITE_FONT_STRETCH_NORMAL,
- font_size.0,
+ font_size.as_f32(),
&components.locale,
)?
.cast()?;
@@ -569,7 +570,7 @@ impl DirectWriteState {
layout
};
- let (mut ascent, mut descent) = {
+ let (ascent, descent) = {
let mut first_metrics = [DWRITE_LINE_METRICS::default(); 4];
let mut line_count = 0u32;
text_layout.GetLineMetrics(Some(&mut first_metrics), &mut line_count)?;
@@ -594,9 +595,9 @@ impl DirectWriteState {
text_layout.SetFontCollection(collection, text_range)?;
text_layout.SetFontFamilyName(&font_info.font_family_h, text_range)?;
let font_size = if break_ligatures {
- font_size.0.next_up()
+ font_size.as_f32().next_up()
} else {
- font_size.0
+ font_size.as_f32()
};
text_layout.SetFontSize(font_size, text_range)?;
text_layout.SetFontStyle(font_info.font_face.GetStyle(), text_range)?;
@@ -673,7 +674,7 @@ impl DirectWriteState {
let offset = [DWRITE_GLYPH_OFFSET::default()];
let glyph_run = DWRITE_GLYPH_RUN {
fontFace: ManuallyDrop::new(Some(unsafe { std::ptr::read(&***font.font_face) })),
- fontEmSize: params.font_size.0,
+ fontEmSize: params.font_size.as_f32(),
glyphCount: 1,
glyphIndices: glyph_id.as_ptr(),
glyphAdvances: advance.as_ptr(),
@@ -691,14 +692,15 @@ impl DirectWriteState {
};
let baseline_origin_x =
params.subpixel_variant.x as f32 / SUBPIXEL_VARIANTS_X as f32 / params.scale_factor;
- let baseline_origin_y =
- params.subpixel_variant.y as f32 / SUBPIXEL_VARIANTS_Y as f32 / params.scale_factor;
+ let baseline_origin_y = params.subpixel_variant.y as f32
+ / gpui::SUBPIXEL_VARIANTS_Y as f32
+ / params.scale_factor;
let mut rendering_mode = DWRITE_RENDERING_MODE1::default();
let mut grid_fit_mode = DWRITE_GRID_FIT_MODE::default();
unsafe {
font.font_face.GetRecommendedRenderingMode(
- params.font_size.0,
+ params.font_size.as_f32(),
// Using 96 as scale is applied by the transform
96.0,
96.0,
@@ -904,7 +906,7 @@ impl DirectWriteState {
}];
let glyph_run = DWRITE_GLYPH_RUN {
fontFace: ManuallyDrop::new(Some(unsafe { std::ptr::read(&***font.font_face) })),
- fontEmSize: params.font_size.0,
+ fontEmSize: params.font_size.as_f32(),
glyphCount: 1,
glyphIndices: glyph_id.as_ptr(),
glyphAdvances: advance.as_ptr(),
@@ -1525,7 +1527,7 @@ impl IDWriteTextRenderer_Impl for TextRenderer_Impl {
let cluster_map =
unsafe { std::slice::from_raw_parts(desc.clusterMap, desc.stringLength as usize) };
- let mut cluster_analyzer = ClusterAnalyzer::new(cluster_map, glyph_count);
+ let cluster_analyzer = ClusterAnalyzer::new(cluster_map, glyph_count);
let mut utf16_idx = desc.textPosition as usize;
let mut glyph_idx = 0;
let mut glyphs = Vec::with_capacity(glyph_count);
@@ -1642,37 +1644,29 @@ impl<'a> StringIndexConverter<'a> {
}
}
-impl Into<DWRITE_FONT_STYLE> for FontStyle {
- fn into(self) -> DWRITE_FONT_STYLE {
- match self {
- FontStyle::Normal => DWRITE_FONT_STYLE_NORMAL,
- FontStyle::Italic => DWRITE_FONT_STYLE_ITALIC,
- FontStyle::Oblique => DWRITE_FONT_STYLE_OBLIQUE,
- }
+fn font_style_to_dwrite(style: FontStyle) -> DWRITE_FONT_STYLE {
+ match style {
+ FontStyle::Normal => DWRITE_FONT_STYLE_NORMAL,
+ FontStyle::Italic => DWRITE_FONT_STYLE_ITALIC,
+ FontStyle::Oblique => DWRITE_FONT_STYLE_OBLIQUE,
}
}
-impl From<DWRITE_FONT_STYLE> for FontStyle {
- fn from(value: DWRITE_FONT_STYLE) -> Self {
- match value.0 {
- 0 => FontStyle::Normal,
- 1 => FontStyle::Italic,
- 2 => FontStyle::Oblique,
- _ => unreachable!(),
- }
+fn font_style_from_dwrite(value: DWRITE_FONT_STYLE) -> FontStyle {
+ match value.0 {
+ 0 => FontStyle::Normal,
+ 1 => FontStyle::Italic,
+ 2 => FontStyle::Oblique,
+ _ => unreachable!(),
}
}
-impl Into<DWRITE_FONT_WEIGHT> for FontWeight {
- fn into(self) -> DWRITE_FONT_WEIGHT {
- DWRITE_FONT_WEIGHT(self.0 as i32)
- }
+fn font_weight_to_dwrite(weight: FontWeight) -> DWRITE_FONT_WEIGHT {
+ DWRITE_FONT_WEIGHT(weight.0 as i32)
}
-impl From<DWRITE_FONT_WEIGHT> for FontWeight {
- fn from(value: DWRITE_FONT_WEIGHT) -> Self {
- FontWeight(value.0 as f32)
- }
+fn font_weight_from_dwrite(value: DWRITE_FONT_WEIGHT) -> FontWeight {
+ FontWeight(value.0 as f32)
}
fn get_font_names_from_collection(
@@ -1707,8 +1701,8 @@ fn font_face_to_font(font_face: &IDWriteFontFace3, locale: &HSTRING) -> Option<F
Some(Font {
family: family_name.into(),
features: FontFeatures::default(),
- weight: weight.into(),
- style: style.into(),
+ weight: font_weight_from_dwrite(weight),
+ style: font_style_from_dwrite(style),
fallbacks: None,
})
}
@@ -1885,7 +1879,7 @@ const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
#[cfg(test)]
mod tests {
- use crate::platform::windows::direct_write::ClusterAnalyzer;
+ use crate::direct_write::ClusterAnalyzer;
#[test]
fn test_cluster_map() {
@@ -9,9 +9,9 @@ use windows::Win32::Graphics::{
Dxgi::Common::*,
};
-use crate::{
- AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
- Point, Size, platform::AtlasTextureList,
+use gpui::{
+ AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTextureList, AtlasTile, Bounds, DevicePixels,
+ PlatformAtlas, Point, Size,
};
pub(crate) struct DirectXAtlas(Mutex<DirectXAtlasState>);
@@ -230,7 +230,7 @@ impl DirectXAtlasState {
kind,
},
bytes_per_pixel,
- allocator: etagere::BucketedAtlasAllocator::new(size.into()),
+ allocator: etagere::BucketedAtlasAllocator::new(device_size_to_etagere(size)),
texture,
view,
live_atlas_keys: 0,
@@ -246,13 +246,13 @@ impl DirectXAtlasState {
fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture {
match id.kind {
- crate::AtlasTextureKind::Monochrome => &self.monochrome_textures[id.index as usize]
+ AtlasTextureKind::Monochrome => &self.monochrome_textures[id.index as usize]
.as_ref()
.unwrap(),
- crate::AtlasTextureKind::Polychrome => &self.polychrome_textures[id.index as usize]
+ AtlasTextureKind::Polychrome => &self.polychrome_textures[id.index as usize]
.as_ref()
.unwrap(),
- crate::AtlasTextureKind::Subpixel => {
+ AtlasTextureKind::Subpixel => {
&self.subpixel_textures[id.index as usize].as_ref().unwrap()
}
}
@@ -261,12 +261,12 @@ impl DirectXAtlasState {
impl DirectXAtlasTexture {
fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {
- let allocation = self.allocator.allocate(size.into())?;
+ let allocation = self.allocator.allocate(device_size_to_etagere(size))?;
let tile = AtlasTile {
texture_id: self.id,
tile_id: allocation.id.into(),
bounds: Bounds {
- origin: allocation.rectangle.min.into(),
+ origin: etagere_point_to_device(allocation.rectangle.min),
size,
},
padding: 0,
@@ -309,17 +309,13 @@ impl DirectXAtlasTexture {
}
}
-impl From<Size<DevicePixels>> for etagere::Size {
- fn from(size: Size<DevicePixels>) -> Self {
- etagere::Size::new(size.width.into(), size.height.into())
- }
+fn device_size_to_etagere(size: Size<DevicePixels>) -> etagere::Size {
+ etagere::Size::new(size.width.into(), size.height.into())
}
-impl From<etagere::Point> for Point<DevicePixels> {
- fn from(value: etagere::Point) -> Self {
- Point {
- x: DevicePixels::from(value.x),
- y: DevicePixels::from(value.y),
- }
+fn etagere_point_to_device(value: etagere::Point) -> Point<DevicePixels> {
+ Point {
+ x: DevicePixels::from(value.x),
+ y: DevicePixels::from(value.y),
}
}
@@ -19,12 +19,9 @@ use windows::{
core::Interface,
};
-use crate::{
- platform::windows::directx_renderer::shader_resources::{
- RawShaderBytes, ShaderModule, ShaderTarget,
- },
- *,
-};
+use crate::directx_renderer::shader_resources::{RawShaderBytes, ShaderModule, ShaderTarget};
+use crate::*;
+use gpui::*;
pub(crate) const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION";
const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM;
@@ -741,7 +738,7 @@ impl DirectXRenderer {
let render_params: IDWriteRenderingParams1 =
factory.CreateRenderingParams().unwrap().cast().unwrap();
FontInfo {
- gamma_ratios: get_gamma_correction_ratios(render_params.GetGamma()),
+ gamma_ratios: gpui::get_gamma_correction_ratios(render_params.GetGamma()),
grayscale_enhanced_contrast: render_params.GetGrayscaleEnhancedContrast(),
subpixel_enhanced_contrast: render_params.GetEnhancedContrast(),
}
@@ -21,10 +21,10 @@ use windows::{
},
};
-use crate::{
- GLOBAL_THREAD_TIMINGS, HWND, PlatformDispatcher, Priority, PriorityQueueSender,
- RunnableVariant, SafeHwnd, THREAD_TIMINGS, TaskTiming, ThreadTaskTimings, TimerResolutionGuard,
- WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, profiler,
+use crate::{HWND, SafeHwnd, WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD};
+use gpui::{
+ GLOBAL_THREAD_TIMINGS, PlatformDispatcher, Priority, PriorityQueueSender, RunnableVariant,
+ THREAD_TIMINGS, TaskTiming, ThreadTaskTimings, TimerResolutionGuard,
};
pub(crate) struct WindowsDispatcher {
@@ -96,14 +96,14 @@ impl WindowsDispatcher {
start,
end: None,
};
- profiler::add_task_timing(timing);
+ gpui::profiler::add_task_timing(timing);
runnable.run();
let end = Instant::now();
timing.end = Some(end);
- profiler::add_task_timing(timing);
+ gpui::profiler::add_task_timing(timing);
}
}
@@ -113,7 +113,7 @@ impl PlatformDispatcher for WindowsDispatcher {
ThreadTaskTimings::convert(&global_thread_timings)
}
- fn get_current_thread_timings(&self) -> crate::ThreadTaskTimings {
+ fn get_current_thread_timings(&self) -> gpui::ThreadTaskTimings {
THREAD_TIMINGS.with(|timings| {
let timings = timings.lock();
let thread_name = timings.thread_name.clone();
@@ -126,7 +126,7 @@ impl PlatformDispatcher for WindowsDispatcher {
vec.extend_from_slice(s1);
vec.extend_from_slice(s2);
- crate::ThreadTaskTimings {
+ gpui::ThreadTaskTimings {
thread_name,
thread_id: std::thread::current().id(),
timings: vec,
@@ -207,10 +207,8 @@ impl PlatformDispatcher for WindowsDispatcher {
unsafe {
timeBeginPeriod(1);
}
- TimerResolutionGuard {
- cleanup: Some(Box::new(|| unsafe {
- timeEndPeriod(1);
- })),
- }
+ util::defer(Box::new(|| unsafe {
+ timeEndPeriod(1);
+ }))
}
}
@@ -15,7 +15,8 @@ use windows::{
core::*,
};
-use crate::{Bounds, DevicePixels, DisplayId, Pixels, PlatformDisplay, logical_point, point, size};
+use crate::logical_point;
+use gpui::{Bounds, DevicePixels, DisplayId, Pixels, PlatformDisplay, point, size};
#[derive(Debug, Clone, Copy)]
pub(crate) struct WindowsDisplay {
@@ -34,7 +35,9 @@ unsafe impl Sync for WindowsDisplay {}
impl WindowsDisplay {
pub(crate) fn new(display_id: DisplayId) -> Option<Self> {
- let screen = available_monitors().into_iter().nth(display_id.0 as _)?;
+ let screen = available_monitors()
+ .into_iter()
+ .nth(u32::from(display_id) as _)?;
let info = get_monitor_info(screen).log_err()?;
let monitor_size = info.monitorInfo.rcMonitor;
let work_area = info.monitorInfo.rcWork;
@@ -63,7 +66,7 @@ impl WindowsDisplay {
(work_area.right - work_area.left) as f32 / scale_factor,
(work_area.bottom - work_area.top) as f32 / scale_factor,
)
- .map(crate::px),
+ .map(gpui::px),
},
physical_bounds: Bounds {
origin: point(monitor_size.left.into(), monitor_size.top.into()),
@@ -90,7 +93,7 @@ impl WindowsDisplay {
Ok(WindowsDisplay {
handle: monitor,
- display_id: DisplayId(display_id as _),
+ display_id: DisplayId::new(display_id as _),
scale_factor,
bounds: Bounds {
origin: logical_point(
@@ -106,7 +109,7 @@ impl WindowsDisplay {
(work_area.right - work_area.left) as f32 / scale_factor,
(work_area.bottom - work_area.top) as f32 / scale_factor,
)
- .map(crate::px),
+ .map(gpui::px),
},
physical_bounds: Bounds {
origin: point(monitor_size.left.into(), monitor_size.top.into()),
@@ -145,7 +148,7 @@ impl WindowsDisplay {
(work_area.right - work_area.left) as f32 / scale_factor,
(work_area.bottom - work_area.top) as f32 / scale_factor,
)
- .map(crate::px),
+ .map(gpui::px),
},
physical_bounds: Bounds {
origin: point(monitor_size.left.into(), monitor_size.top.into()),
@@ -173,8 +176,8 @@ impl WindowsDisplay {
pub fn check_given_bounds(&self, bounds: Bounds<Pixels>) -> bool {
let center = bounds.center();
let center = POINT {
- x: (center.x.0 * self.scale_factor) as i32,
- y: (center.y.0 * self.scale_factor) as i32,
+ x: (center.x.as_f32() * self.scale_factor) as i32,
+ y: (center.y.as_f32() * self.scale_factor) as i32,
};
let monitor = unsafe { MonitorFromPoint(center, MONITOR_DEFAULTTONULL) };
if monitor.is_invalid() {
@@ -193,7 +196,7 @@ impl WindowsDisplay {
.enumerate()
.filter_map(|(id, handle)| {
Some(Rc::new(
- WindowsDisplay::new_with_handle_and_id(handle, DisplayId(id as _)).ok()?,
+ WindowsDisplay::new_with_handle_and_id(handle, DisplayId::new(id as _)).ok()?,
) as Rc<dyn PlatformDisplay>)
})
.collect()
@@ -18,6 +18,7 @@ use windows::{
};
use crate::*;
+use gpui::*;
pub(crate) const WM_GPUI_CURSOR_STYLE_CHANGED: u32 = WM_USER + 1;
pub(crate) const WM_GPUI_CLOSE_ONE_WINDOW: u32 = WM_USER + 2;
@@ -127,13 +128,13 @@ impl WindowsWindowInner {
);
self.state.origin.set(origin);
let size = self.state.logical_size.get();
- let center_x = origin.x.0 + size.width.0 / 2.;
- let center_y = origin.y.0 + size.height.0 / 2.;
+ let center_x = origin.x.as_f32() + size.width.as_f32() / 2.;
+ let center_y = origin.y.as_f32() + size.height.as_f32() / 2.;
let monitor_bounds = self.state.display.get().bounds();
- if center_x < monitor_bounds.left().0
- || center_x > monitor_bounds.right().0
- || center_y < monitor_bounds.top().0
- || center_y > monitor_bounds.bottom().0
+ if center_x < monitor_bounds.left().as_f32()
+ || center_x > monitor_bounds.right().as_f32()
+ || center_y < monitor_bounds.top().as_f32()
+ || center_y > monitor_bounds.bottom().as_f32()
{
// center of the window may have moved to another monitor
let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
@@ -160,10 +161,10 @@ impl WindowsWindowInner {
unsafe {
let minmax_info = &mut *(lparam.0 as *mut MINMAXINFO);
- minmax_info.ptMinTrackSize.x =
- min_size.width.scale(scale_factor).0 as i32 + boarder_offset.width_offset.get();
- minmax_info.ptMinTrackSize.y =
- min_size.height.scale(scale_factor).0 as i32 + boarder_offset.height_offset.get();
+ minmax_info.ptMinTrackSize.x = min_size.width.scale(scale_factor).as_f32() as i32
+ + boarder_offset.width_offset.get();
+ minmax_info.ptMinTrackSize.y = min_size.height.scale(scale_factor).as_f32() as i32
+ + boarder_offset.height_offset.get();
}
Some(0)
}
@@ -577,9 +578,9 @@ impl WindowsWindowInner {
let caret_position = input_handler.bounds_for_range(caret_range.range)?;
Some(POINT {
// logical to physical
- x: (caret_position.origin.x.0 * scale_factor) as i32,
- y: (caret_position.origin.y.0 * scale_factor) as i32
- + ((caret_position.size.height.0 * scale_factor) as i32 / 2),
+ x: (caret_position.origin.x.as_f32() * scale_factor) as i32,
+ y: (caret_position.origin.y.as_f32() * scale_factor) as i32
+ + ((caret_position.size.height.as_f32() * scale_factor) as i32 / 2),
})
})
}
@@ -1,3 +1,5 @@
+#![cfg(target_os = "windows")]
+
mod clipboard;
mod destination_list;
mod direct_write;
@@ -32,9 +34,6 @@ pub(crate) use vsync::*;
pub(crate) use window::*;
pub(crate) use wrapper::*;
-pub(crate) use windows::Win32::Foundation::HWND;
+pub use platform::WindowsPlatform;
-#[cfg(feature = "screen-capture")]
-pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame;
-#[cfg(not(feature = "screen-capture"))]
-pub(crate) type PlatformScreenCaptureFrame = ();
+pub(crate) use windows::Win32::Foundation::HWND;
@@ -10,7 +10,7 @@ use windows::Win32::UI::{
WindowsAndMessaging::KL_NAMELENGTH,
};
-use crate::{
+use gpui::{
KeybindingKeystroke, Keystroke, Modifiers, PlatformKeyboardLayout, PlatformKeyboardMapper,
};
@@ -316,7 +316,8 @@ const CANDIDATE_VKEYS: &[VIRTUAL_KEY] = &[
#[cfg(test)]
mod tests {
- use crate::{Keystroke, Modifiers, PlatformKeyboardMapper, WindowsKeyboardMapper};
+ use crate::WindowsKeyboardMapper;
+ use gpui::{Keystroke, Modifiers, PlatformKeyboardMapper};
#[test]
fn test_keyboard_mapper() {
@@ -28,8 +28,9 @@ use windows::{
};
use crate::*;
+use gpui::*;
-pub(crate) struct WindowsPlatform {
+pub struct WindowsPlatform {
inner: Rc<WindowsPlatformInner>,
raw_window_handles: Arc<RwLock<SmallVec<[SafeHwnd; 4]>>>,
// The below members will never change throughout the entire lifecycle of the app.
@@ -93,7 +94,7 @@ impl WindowsPlatformState {
}
impl WindowsPlatform {
- pub(crate) fn new(headless: bool) -> Result<Self> {
+ pub fn new(headless: bool) -> Result<Self> {
unsafe {
OleInitialize(None).context("unable to initialize Windows OLE")?;
}
@@ -111,7 +112,7 @@ impl WindowsPlatform {
} else {
(
None,
- Arc::new(crate::NoopTextSystem::new()) as Arc<dyn PlatformTextSystem>,
+ Arc::new(gpui::NoopTextSystem::new()) as Arc<dyn PlatformTextSystem>,
None,
)
};
@@ -195,7 +196,7 @@ impl WindowsPlatform {
})
}
- pub fn window_from_hwnd(&self, hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
+ pub(crate) fn window_from_hwnd(&self, hwnd: HWND) -> Option<Rc<WindowsWindowInner>> {
self.raw_window_handles
.read()
.iter()
@@ -452,7 +453,7 @@ impl Platform for WindowsPlatform {
reason = "We are restarting ourselves, using std command thus is fine"
)]
let restart_process =
- util::command::new_std_command(util::shell::get_windows_system_shell())
+ ::util::command::new_std_command(::util::shell::get_windows_system_shell())
.arg("-command")
.arg(script)
.spawn();
@@ -496,7 +497,7 @@ impl Platform for WindowsPlatform {
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
- crate::platform::scap_screen_capture::scap_screen_sources(&self.foreground_executor)
+ gpui::scap_screen_capture::scap_screen_sources(&self.foreground_executor)
}
fn active_window(&self) -> Option<AnyWindowHandle> {
@@ -683,7 +684,7 @@ impl Platform for WindowsPlatform {
}
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>> {
- let mut password = password.to_vec();
+ let password = password.to_vec();
let mut username = username.encode_utf16().chain(Some(0)).collect_vec();
let mut target_name = windows_credentials_target_name(url)
.encode_utf16()
@@ -714,7 +715,7 @@ impl Platform for WindowsPlatform {
}
fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
- let mut target_name = windows_credentials_target_name(url)
+ let target_name = windows_credentials_target_name(url)
.encode_utf16()
.chain(Some(0))
.collect_vec();
@@ -756,7 +757,7 @@ impl Platform for WindowsPlatform {
}
fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
- let mut target_name = windows_credentials_target_name(url)
+ let target_name = windows_credentials_target_name(url)
.encode_utf16()
.chain(Some(0))
.collect_vec();
@@ -1340,7 +1341,8 @@ unsafe extern "system" fn window_procedure(
#[cfg(test)]
mod tests {
- use crate::{ClipboardItem, read_from_clipboard, write_to_clipboard};
+ use crate::{read_from_clipboard, write_to_clipboard};
+ use gpui::ClipboardItem;
#[test]
fn test_clipboard() {
@@ -11,10 +11,11 @@ use windows::{
Foundation::*, Graphics::Dwm::*, System::LibraryLoader::LoadLibraryA,
UI::WindowsAndMessaging::*,
},
- core::{BOOL, HSTRING, PCSTR},
+ core::{BOOL, PCSTR},
};
use crate::*;
+use gpui::*;
pub(crate) trait HiLoWord {
fn hiword(&self) -> u16;
@@ -174,17 +175,6 @@ fn is_color_light(color: &Color) -> bool {
((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128)
}
-pub(crate) fn show_error(title: &str, content: String) {
- let _ = unsafe {
- MessageBoxW(
- None,
- &HSTRING::from(content),
- &HSTRING::from(title),
- MB_ICONERROR | MB_SYSTEMMODAL,
- )
- };
-}
-
pub(crate) fn with_dll_library<R, F>(dll_name: PCSTR, f: F) -> Result<R>
where
F: FnOnce(HMODULE) -> Result<R>,
@@ -27,6 +27,7 @@ use windows::{
};
use crate::*;
+use gpui::*;
pub(crate) struct WindowsWindow(pub Rc<WindowsWindowInner>);
@@ -342,7 +343,7 @@ impl WindowsWindowInner {
#[derive(Default)]
pub(crate) struct Callbacks {
pub(crate) request_frame: Cell<Option<Box<dyn FnMut(RequestFrameOptions)>>>,
- pub(crate) input: Cell<Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>>,
+ pub(crate) input: Cell<Option<Box<dyn FnMut(PlatformInput) -> DispatchEventResult>>>,
pub(crate) active_status_change: Cell<Option<Box<dyn FnMut(bool)>>>,
pub(crate) hovered_status_change: Cell<Option<Box<dyn FnMut(bool)>>>,
pub(crate) resize: Cell<Option<Box<dyn FnMut(Size<Pixels>, f32)>>>,
@@ -576,8 +577,7 @@ impl PlatformWindow for WindowsWindow {
fn resize(&mut self, size: Size<Pixels>) {
let hwnd = self.0.hwnd;
- let bounds =
- crate::bounds(self.bounds().origin, size).to_device_pixels(self.scale_factor());
+ let bounds = gpui::bounds(self.bounds().origin, size).to_device_pixels(self.scale_factor());
let rect = calculate_window_rect(bounds, &self.state.border_offset);
self.0
@@ -663,15 +663,15 @@ impl PlatformWindow for WindowsWindow {
let title;
let main_icon;
match level {
- crate::PromptLevel::Info => {
+ PromptLevel::Info => {
title = windows::core::w!("Info");
main_icon = TD_INFORMATION_ICON;
}
- crate::PromptLevel::Warning => {
+ PromptLevel::Warning => {
title = windows::core::w!("Warning");
main_icon = TD_WARNING_ICON;
}
- crate::PromptLevel::Critical => {
+ PromptLevel::Critical => {
title = windows::core::w!("Critical");
main_icon = TD_ERROR_ICON;
}
@@ -935,9 +935,9 @@ impl PlatformWindow for WindowsWindow {
fn update_ime_position(&self, bounds: Bounds<Pixels>) {
let scale_factor = self.state.scale_factor.get();
let caret_position = POINT {
- x: (bounds.origin.x.0 * scale_factor) as i32,
- y: (bounds.origin.y.0 * scale_factor) as i32
- + ((bounds.size.height.0 * scale_factor) as i32 / 2),
+ x: (bounds.origin.x.as_f32() * scale_factor) as i32,
+ y: (bounds.origin.y.as_f32() * scale_factor) as i32
+ + ((bounds.size.height.as_f32() * scale_factor) as i32 / 2),
};
self.0.update_ime_position(self.0.hwnd, caret_position);
@@ -1476,12 +1476,12 @@ fn set_non_rude_hwnd(hwnd: HWND, non_rude: bool) {
#[cfg(test)]
mod tests {
use super::ClickState;
- use crate::{DevicePixels, MouseButton, point};
+ use gpui::{DevicePixels, MouseButton, point};
use std::time::Duration;
#[test]
fn test_double_click_interval() {
- let mut state = ClickState::new();
+ let state = ClickState::new();
assert_eq!(
state.update(MouseButton::Left, point(DevicePixels(0), DevicePixels(0))),
1
@@ -1509,7 +1509,7 @@ mod tests {
#[test]
fn test_double_click_spatial_tolerance() {
- let mut state = ClickState::new();
+ let state = ClickState::new();
assert_eq!(
state.update(MouseButton::Left, point(DevicePixels(-3), DevicePixels(0))),
1
@@ -26,7 +26,7 @@ audio.workspace = true
collections.workspace = true
cpal.workspace = true
futures.workspace = true
-gpui = { workspace = true, features = ["screen-capture", "x11", "wayland", "windows-manifest"] }
+gpui = { workspace = true, features = ["screen-capture", "x11", "wayland"] }
gpui_tokio.workspace = true
http_client_tls.workspace = true
image.workspace = true
@@ -63,6 +63,7 @@ objc.workspace = true
[dev-dependencies]
collections = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] }
+gpui_platform.workspace = true
sha2.workspace = true
simplelog.workspace = true
@@ -23,7 +23,7 @@ actions!(livekit_client, [Quit]);
fn main() {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
- gpui::Application::new().run(|cx| {
+ gpui_platform::application().run(|cx| {
#[cfg(any(test, feature = "test-support"))]
println!("USING TEST LIVEKIT");
@@ -38,6 +38,7 @@ assets.workspace = true
env_logger.workspace = true
fs = {workspace = true, features = ["test-support"]}
gpui = { workspace = true, features = ["test-support"] }
+gpui_platform.workspace = true
language = { workspace = true, features = ["test-support"] }
languages = { workspace = true, features = ["load-grammars"] }
node_runtime.workspace = true
@@ -1,5 +1,5 @@
use assets::Assets;
-use gpui::{Application, Entity, KeyBinding, StyleRefinement, WindowOptions, prelude::*, rgb};
+use gpui::{Entity, KeyBinding, StyleRefinement, WindowOptions, prelude::*, rgb};
use language::LanguageRegistry;
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use node_runtime::NodeRuntime;
@@ -35,7 +35,7 @@ Remember, markdown processors may have slight differences and extensions, so alw
pub fn main() {
env_logger::init();
- Application::new().with_assets(Assets).run(|cx| {
+ gpui_platform::application().with_assets(Assets).run(|cx| {
let store = SettingsStore::test(cx);
cx.set_global(store);
cx.bind_keys([KeyBinding::new("cmd-c", markdown::Copy, None)]);
@@ -1,5 +1,5 @@
use assets::Assets;
-use gpui::{Application, Entity, KeyBinding, Length, StyleRefinement, WindowOptions, rgb};
+use gpui::{Entity, KeyBinding, Length, StyleRefinement, WindowOptions, rgb};
use language::LanguageRegistry;
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use node_runtime::NodeRuntime;
@@ -19,7 +19,7 @@ wow so cool
pub fn main() {
env_logger::init();
- Application::new().with_assets(Assets).run(|cx| {
+ gpui_platform::application().with_assets(Assets).run(|cx| {
let store = SettingsStore::test(cx);
cx.set_global(store);
cx.bind_keys([KeyBinding::new("cmd-c", markdown::Copy, None)]);
@@ -10,7 +10,8 @@ askpass.workspace = true
clap.workspace = true
client.workspace = true
futures.workspace = true
-gpui = { workspace = true, features = ["windows-manifest"] }
+gpui.workspace = true
+gpui_platform.workspace = true
http_client = { workspace = true, features = ["test-support"]}
language.workspace = true
node_runtime.workspace = true
@@ -5,7 +5,7 @@ use askpass::EncryptedPassword;
use clap::Parser;
use client::{Client, UserStore};
use futures::channel::oneshot;
-use gpui::{AppContext as _, Application};
+use gpui::AppContext as _;
use http_client::FakeHttpClient;
use language::LanguageRegistry;
use node_runtime::NodeRuntime;
@@ -125,7 +125,7 @@ fn main() -> Result<(), anyhow::Error> {
None,
)
}?;
- Application::headless().run(|cx| {
+ gpui_platform::headless().run(|cx| {
release_channel::init_test(semver::Version::new(0, 0, 0), ReleaseChannel::Dev, cx);
settings::init(cx);
let client = Client::production(cx);
@@ -38,7 +38,8 @@ futures.workspace = true
git.workspace = true
git_hosting_providers.workspace = true
git2 = { workspace = true, features = ["vendored-libgit2"] }
-gpui = { workspace = true, features = ["windows-manifest"] }
+gpui.workspace = true
+gpui_platform.workspace = true
gpui_tokio.workspace = true
http_client.workspace = true
image.workspace = true
@@ -449,7 +449,7 @@ pub fn execute_run(
init_paths()?;
let startup_time = Instant::now();
- let app = gpui::Application::headless();
+ let app = gpui_platform::headless();
let pid = std::process::id();
let id = pid.to_string();
app.background_executor()
@@ -20,6 +20,7 @@ dialoguer = { version = "0.11.0", features = ["fuzzy-select"] }
editor.workspace = true
fuzzy.workspace = true
gpui = { workspace = true, default-features = true }
+gpui_platform.workspace = true
indoc.workspace = true
language.workspace = true
log.workspace = true
@@ -65,45 +65,47 @@ fn main() {
});
let theme_name = args.theme.unwrap_or("One Dark".to_string());
- gpui::Application::new().with_assets(Assets).run(move |cx| {
- load_embedded_fonts(cx).unwrap();
-
- cx.set_global(GlobalColors(Arc::new(Colors::default())));
-
- let http_client = ReqwestClient::user_agent("zed_storybook").unwrap();
- cx.set_http_client(Arc::new(http_client));
-
- settings::init(cx);
- theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
-
- let selector = story_selector;
-
- let mut theme_settings = ThemeSettings::get_global(cx).clone();
- theme_settings.theme =
- theme::ThemeSelection::Static(settings::ThemeName(theme_name.into()));
- ThemeSettings::override_global(theme_settings, cx);
-
- editor::init(cx);
- init(cx);
- load_storybook_keymap(cx);
- cx.set_menus(app_menus());
-
- let size = size(px(1500.), px(780.));
- let bounds = Bounds::centered(None, size, cx);
- let _window = cx.open_window(
- WindowOptions {
- window_bounds: Some(WindowBounds::Windowed(bounds)),
- ..Default::default()
- },
- move |window, cx| {
- theme::setup_ui_font(window, cx);
-
- cx.new(|cx| StoryWrapper::new(selector.story(window, cx)))
- },
- );
-
- cx.activate(true);
- });
+ gpui_platform::application()
+ .with_assets(Assets)
+ .run(move |cx| {
+ load_embedded_fonts(cx).unwrap();
+
+ cx.set_global(GlobalColors(Arc::new(Colors::default())));
+
+ let http_client = ReqwestClient::user_agent("zed_storybook").unwrap();
+ cx.set_http_client(Arc::new(http_client));
+
+ settings::init(cx);
+ theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
+
+ let selector = story_selector;
+
+ let mut theme_settings = ThemeSettings::get_global(cx).clone();
+ theme_settings.theme =
+ theme::ThemeSelection::Static(settings::ThemeName(theme_name.into()));
+ ThemeSettings::override_global(theme_settings, cx);
+
+ editor::init(cx);
+ init(cx);
+ load_storybook_keymap(cx);
+ cx.set_menus(app_menus());
+
+ let size = size(px(1500.), px(780.));
+ let bounds = Bounds::centered(None, size, cx);
+ let _window = cx.open_window(
+ WindowOptions {
+ window_bounds: Some(WindowBounds::Windowed(bounds)),
+ ..Default::default()
+ },
+ move |window, cx| {
+ theme::setup_ui_font(window, cx);
+
+ cx.new(|cx| StoryWrapper::new(selector.story(window, cx)))
+ },
+ );
+
+ cx.activate(true);
+ });
}
#[derive(Clone)]
@@ -6,7 +6,7 @@ edition.workspace = true
[dependencies]
fs.workspace = true
-gpui = { workspace = true, features = ["windows-manifest"] }
+gpui_platform.workspace = true
settings.workspace = true
worktree.workspace = true
@@ -4,7 +4,6 @@ use std::{
};
use fs::RealFs;
-use gpui::Application;
use settings::WorktreeId;
use worktree::Worktree;
@@ -15,7 +14,7 @@ fn main() {
);
return;
};
- let app = Application::headless();
+ let app = gpui_platform::headless();
app.run(|cx| {
settings::init(cx);
@@ -15,7 +15,7 @@ workspace = true
tracy = ["ztracing/tracy"]
test-support = [
"gpui/test-support",
- "gpui/screen-capture",
+ "gpui_platform/screen-capture",
"dep:image",
"dep:semver",
"workspace/test-support",
@@ -29,7 +29,7 @@ test-support = [
]
visual-tests = [
"gpui/test-support",
- "gpui/screen-capture",
+ "gpui_platform/screen-capture",
"dep:image",
"dep:semver",
"dep:tempfile",
@@ -120,9 +120,8 @@ system_specs.workspace = true
gpui = { workspace = true, features = [
"wayland",
"x11",
- "font-kit",
- "windows-manifest",
] }
+gpui_platform = {workspace = true, features=["screen-capture", "font-kit", "wayland", "x11"]}
image = { workspace = true, optional = true }
semver = { workspace = true, optional = true }
tempfile = { workspace = true, optional = true }
@@ -241,7 +240,7 @@ ashpd.workspace = true
call = { workspace = true, features = ["test-support"] }
dap = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
-gpui = { workspace = true, features = ["test-support", "screen-capture"] }
+gpui = { workspace = true, features = ["test-support"] }
image_viewer = { workspace = true, features = ["test-support"] }
itertools.workspace = true
language = { workspace = true, features = ["test-support"] }
@@ -22,6 +22,7 @@ use futures::{StreamExt, channel::oneshot, future};
use git::GitHostingProviderRegistry;
use git_ui::clone::clone_and_open;
use gpui::{App, AppContext, Application, AsyncApp, Focusable as _, QuitMode, UpdateGlobal as _};
+use gpui_platform;
use gpui_tokio::Tokio;
use language::LanguageRegistry;
@@ -98,7 +99,7 @@ fn files_not_created_on_launch(errors: HashMap<io::ErrorKind, Vec<&Path>>) {
.collect::<Vec<_>>().join("\n\n");
eprintln!("{message}: {error_details}");
- Application::new()
+ Application::with_platform(gpui_platform::current_platform(false))
.with_quit_mode(QuitMode::Explicit)
.run(move |cx| {
if let Ok(window) = cx.open_window(gpui::WindowOptions::default(), |_, cx| {
@@ -297,7 +298,8 @@ fn main() {
#[cfg(windows)]
check_for_conpty_dll();
- let app = Application::new().with_assets(Assets);
+ let app =
+ Application::with_platform(gpui_platform::current_platform(false)).with_assets(Assets);
let system_id = app.background_executor().spawn(system_id());
let installation_id = app.background_executor().spawn(installation_id());
@@ -150,7 +150,10 @@ fn main() {
fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()> {
// Create the visual test context with deterministic task scheduling
// Use real Assets so that SVG icons render properly
- let mut cx = VisualTestAppContext::with_asset_source(Arc::new(Assets));
+ let mut cx = VisualTestAppContext::with_asset_source(
+ gpui_platform::current_platform(false),
+ Arc::new(Assets),
+ );
// Load embedded fonts (IBM Plex Sans, Lilex, etc.) so UI renders with correct fonts
cx.update(|cx| {
@@ -426,7 +426,7 @@ mod tests {
#[test]
#[ignore]
fn test_visual_test_smoke() {
- let mut cx = VisualTestAppContext::new();
+ let mut cx = VisualTestAppContext::new(gpui_platform::current_platform(false));
let _window = cx
.open_offscreen_window_default(|_, cx| cx.new(|_| Empty))
@@ -438,7 +438,7 @@ mod tests {
#[test]
#[ignore]
fn test_workspace_opens() {
- let mut cx = VisualTestAppContext::new();
+ let mut cx = VisualTestAppContext::new(gpui_platform::current_platform(false));
let app_state = init_visual_test(&mut cx);
smol::block_on(async {
@@ -479,7 +479,7 @@ mod tests {
#[test]
#[ignore]
fn test_workspace_screenshot() {
- let mut cx = VisualTestAppContext::new();
+ let mut cx = VisualTestAppContext::new(gpui_platform::current_platform(false));
let app_state = init_visual_test(&mut cx);
smol::block_on(async {
@@ -36,10 +36,10 @@ extend-exclude = [
"extensions/proto/extension.toml",
"extensions/proto/src/language_servers/protols.rs",
# Windows likes its abbreviations.
- "crates/gpui/src/platform/windows/directx_renderer.rs",
- "crates/gpui/src/platform/windows/events.rs",
- "crates/gpui/src/platform/windows/direct_write.rs",
- "crates/gpui/src/platform/windows/window.rs",
+ "crates/gpui_windows/src/directx_renderer.rs",
+ "crates/gpui_windows/src/events.rs",
+ "crates/gpui_windows/src/direct_write.rs",
+ "crates/gpui_windows/src/window.rs",
# Some typos in the base mdBook CSS.
"docs/theme/css/",
# Spellcheck triggers on `|Fixe[sd]|` regex part.
@@ -57,7 +57,7 @@ extend-exclude = [
# Some multibuffer test cases have word fragments that register as typos
"crates/multi_buffer/src/multi_buffer_tests.rs",
# Macos apis
- "crates/gpui/src/platform/mac/dispatcher.rs",
+ "crates/gpui_macos/src/dispatcher.rs",
# Tests contain partially incomplete words (by design)
"crates/edit_prediction_cli/src/split_commit.rs",
# Eval examples contain intentionally partial words (e.g. "secur" for "secure")