mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
gpui: Extract gpui_platform out of gpui (#49277)
#2874 on steroids Before you mark this PR as ready for review, make sure that you have: - [ ] Added a solid test coverage and/or screenshots from doing manual testing - [ ] Done a self-review taking into account security and performance aspects - [ ] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A --------- Co-authored-by: Eric Holk <eric@zed.dev>
This commit is contained in:
parent
da7b8f2939
commit
bc31ad4a8c
178 changed files with 3114 additions and 3204 deletions
166
Cargo.lock
generated
166
Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
12
Cargo.toml
12
Cargo.toml
|
|
@ -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(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(all(target_os = "windows", feature = "windows-manifest"))]
|
||||
embed_resource();
|
||||
}
|
||||
|
||||
#[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");
|
||||
|
||||
/// Builds an app with a caller-provided platform implementation.
|
||||
pub fn with_platform(platform: Rc<dyn Platform>) -> Self {
|
||||
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 {
|
||||
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;
|
||||
/// Number of subpixel glyph variants along the X axis.
|
||||
pub 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 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,
|
||||
}
|
||||
|
|
|
|||
134
crates/gpui_linux/Cargo.toml
Normal file
134
crates/gpui_linux/Cargo.toml
Normal file
|
|
@ -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 }
|
||||
1
crates/gpui_linux/LICENSE-APACHE
Symbolic link
1
crates/gpui_linux/LICENSE-APACHE
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../LICENSE-APACHE
|
||||
4
crates/gpui_linux/src/gpui_linux.rs
Normal file
4
crates/gpui_linux/src/gpui_linux.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#![cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
mod linux;
|
||||
|
||||
pub use linux::current_platform;
|
||||
57
crates/gpui_linux/src/linux.rs
Normal file
57
crates/gpui_linux/src/linux.rs
Normal file
|
|
@ -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);
|
||||
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(),
|
||||
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::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::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::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::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(),
|
||||
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()
|
||||
_ => {
|
||||
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"))]
|
||||
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"))]
|
||||
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"))]
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 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 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")?;
|
||||
|
||||
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);
|
||||
|
||||
let tag = cosmic_text::FeatureTag::new(&name_bytes);
|
||||
|
||||
result.set(tag, feature.1);
|
||||
}
|
||||
Ok(result)
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FontWeight> for cosmic_text::Weight {
|
||||
fn from(value: FontWeight) -> Self {
|
||||
cosmic_text::Weight(value.0 as u16)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
47
crates/gpui_linux/src/linux/wayland.rs
Normal file
47
crates/gpui_linux/src/linux/wayland.rs
Normal file
|
|
@ -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> {
|
||||
26
crates/gpui_linux/src/linux/wayland/layer_shell.rs
Normal file
26
crates/gpui_linux/src/linux/wayland/layer_shell.rs
Normal file
|
|
@ -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,
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue