mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-25 01:34:02 +00:00
WIP
This commit is contained in:
parent
b400449a58
commit
222f9d373d
16 changed files with 1164 additions and 179 deletions
64
.vscode/launch.json
vendored
Normal file
64
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'zed'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=zed",
|
||||
"--package=zed"
|
||||
],
|
||||
"filter": {
|
||||
"name": "zed",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in executable 'zed'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--bin=zed",
|
||||
"--package=zed"
|
||||
],
|
||||
"filter": {
|
||||
"name": "zed",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in library 'gpui'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--lib",
|
||||
"--package=gpui"
|
||||
],
|
||||
"filter": {
|
||||
"name": "gpui",
|
||||
"kind": "lib"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
231
Cargo.lock
generated
231
Cargo.lock
generated
|
@ -9,6 +9,15 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.38"
|
||||
|
@ -44,7 +53,7 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"async-task 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
|
@ -126,12 +135,28 @@ version = "4.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.0.3"
|
||||
source = "git+https://github.com/zedit-io/async-task?rev=341b57d6de98cdfd7b418567b8de2022ca993a6e#341b57d6de98cdfd7b418567b8de2022ca993a6e"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
|
@ -144,6 +169,29 @@ version = "0.13.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd4865004a46a0aafb2a0a5eb19d3c9fc46ee5f063a6cfc605c69ac9ecf5263d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
|
@ -174,7 +222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-task",
|
||||
"async-task 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
|
@ -193,6 +241,15 @@ version = "1.0.67"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
|
@ -218,6 +275,32 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f54d78e30b388d4815220c8dd03fea5656b6c6d32adb59e89061552a102f8da1"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.24.0"
|
||||
|
@ -329,6 +412,16 @@ dependencies = [
|
|||
"loom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "3.0.1"
|
||||
|
@ -349,6 +442,19 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.1"
|
||||
|
@ -457,17 +563,27 @@ dependencies = [
|
|||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gpui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-task 4.0.3 (git+https://github.com/zedit-io/async-task?rev=341b57d6de98cdfd7b418567b8de2022ca993a6e)",
|
||||
"bindgen",
|
||||
"cocoa",
|
||||
"core-foundation",
|
||||
"core-text",
|
||||
"ctor",
|
||||
"foreign-types 0.5.0",
|
||||
"log",
|
||||
"metal",
|
||||
"num_cpus",
|
||||
"objc",
|
||||
"pathfinder_color",
|
||||
"pathfinder_geometry",
|
||||
|
@ -475,6 +591,21 @@ dependencies = [
|
|||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.9"
|
||||
|
@ -490,12 +621,28 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
|
@ -555,6 +702,16 @@ dependencies = [
|
|||
"socket2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
|
@ -574,6 +731,16 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
|
@ -633,6 +800,12 @@ dependencies = [
|
|||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.4"
|
||||
|
@ -717,6 +890,12 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
|
@ -747,6 +926,12 @@ version = "0.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.6"
|
||||
|
@ -806,6 +991,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.60"
|
||||
|
@ -826,6 +1017,15 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.3"
|
||||
|
@ -856,6 +1056,12 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
|
@ -868,6 +1074,18 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "waker-fn"
|
||||
version = "1.1.0"
|
||||
|
@ -895,6 +1113,15 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
@ -5,11 +5,17 @@ name = "gpui"
|
|||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
async-task = {git = "https://github.com/zedit-io/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"}
|
||||
ctor = "0.1"
|
||||
num_cpus = "1.13"
|
||||
pathfinder_color = "0.5"
|
||||
pathfinder_geometry = "0.5"
|
||||
smol = "1.2"
|
||||
tree-sitter = "0.17"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.57"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
anyhow = "1"
|
||||
cocoa = "0.24"
|
||||
|
|
23
gpui/build.rs
Normal file
23
gpui/build.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use std::{env, path::PathBuf};
|
||||
|
||||
fn main() {
|
||||
generate_dispatch_bindings();
|
||||
}
|
||||
|
||||
fn generate_dispatch_bindings() {
|
||||
println!("cargo:rustc-link-lib=framework=System");
|
||||
println!("cargo:rerun-if-changed=src/platform/mac/dispatch.h");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("src/platform/mac/dispatch.h")
|
||||
.whitelist_var("_dispatch_main_q")
|
||||
.whitelist_function("dispatch_async_f")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
.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 bindings");
|
||||
}
|
114
gpui/src/executor.rs
Normal file
114
gpui/src/executor.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
// #[cfg(not(test))]
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_task::Runnable;
|
||||
use smol::prelude::*;
|
||||
use smol::{channel, Executor};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::{marker::PhantomData, thread};
|
||||
|
||||
use crate::platform;
|
||||
|
||||
pub enum Foreground {
|
||||
Platform {
|
||||
dispatcher: Arc<dyn platform::Dispatcher>,
|
||||
_not_send_or_sync: PhantomData<Rc<()>>,
|
||||
},
|
||||
Test(smol::LocalExecutor<'static>),
|
||||
}
|
||||
|
||||
pub enum ForegroundTask<T> {
|
||||
Platform(async_task::Task<T>),
|
||||
Test(smol::Task<T>),
|
||||
}
|
||||
|
||||
pub struct Background {
|
||||
executor: Arc<smol::Executor<'static>>,
|
||||
_stop: channel::Sender<()>,
|
||||
}
|
||||
|
||||
pub type BackgroundTask<T> = smol::Task<T>;
|
||||
|
||||
impl Foreground {
|
||||
pub fn platform(dispatcher: Arc<dyn platform::Dispatcher>) -> Result<Self> {
|
||||
if dispatcher.is_main_thread() {
|
||||
Ok(Self::Platform {
|
||||
dispatcher,
|
||||
_not_send_or_sync: PhantomData,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("must be constructed on main thread"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test() -> Self {
|
||||
Self::Test(smol::LocalExecutor::new())
|
||||
}
|
||||
|
||||
pub fn spawn<T: 'static>(
|
||||
&self,
|
||||
future: impl Future<Output = T> + 'static,
|
||||
) -> ForegroundTask<T> {
|
||||
match self {
|
||||
Self::Platform { dispatcher, .. } => {
|
||||
let dispatcher = dispatcher.clone();
|
||||
let schedule = move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
|
||||
let (runnable, task) = async_task::spawn_local(future, schedule);
|
||||
runnable.schedule();
|
||||
ForegroundTask::Platform(task)
|
||||
}
|
||||
Self::Test(executor) => ForegroundTask::Test(executor.spawn(future)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run<T>(&self, future: impl Future<Output = T>) -> T {
|
||||
match self {
|
||||
Self::Platform { .. } => panic!("you can't call run on a platform foreground executor"),
|
||||
Self::Test(executor) => executor.run(future).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ForegroundTask<T> {
|
||||
pub fn detach(self) {
|
||||
match self {
|
||||
Self::Platform(task) => task.detach(),
|
||||
Self::Test(task) => task.detach(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn cancel(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Platform(task) => task.cancel().await,
|
||||
Self::Test(task) => task.cancel().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Background {
|
||||
pub fn new() -> Self {
|
||||
let executor = Arc::new(Executor::new());
|
||||
let stop = channel::unbounded::<()>();
|
||||
|
||||
for i in 0..num_cpus::get() {
|
||||
let executor = executor.clone();
|
||||
let stop = stop.1.clone();
|
||||
thread::Builder::new()
|
||||
.name(format!("background-executor-{}", i))
|
||||
.spawn(move || smol::block_on(executor.run(stop.recv())))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Self {
|
||||
executor,
|
||||
_stop: stop.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn<T>(&self, future: impl Send + Future<Output = T> + 'static) -> BackgroundTask<T>
|
||||
where
|
||||
T: 'static + Send,
|
||||
{
|
||||
self.executor.spawn(future)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod executor;
|
||||
pub mod keymap;
|
||||
pub mod platform;
|
||||
|
||||
|
|
|
@ -1,176 +1,39 @@
|
|||
use super::Event;
|
||||
pub use cocoa::foundation::NSSize;
|
||||
use cocoa::{
|
||||
base::{id, nil},
|
||||
foundation::{NSArray, NSAutoreleasePool, NSString},
|
||||
};
|
||||
use objc::{
|
||||
class,
|
||||
declare::ClassDecl,
|
||||
msg_send,
|
||||
runtime::{Class, Object, Sel},
|
||||
sel, sel_impl,
|
||||
};
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
os::raw::{c_char, c_void},
|
||||
path::PathBuf,
|
||||
};
|
||||
use super::{BoolExt as _, Dispatcher, Window};
|
||||
use crate::{executor, platform};
|
||||
use anyhow::Result;
|
||||
use cocoa::base::id;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use std::{rc::Rc, sync::Arc};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct App {
|
||||
finish_launching_callback: Option<Box<dyn FnOnce()>>,
|
||||
become_active_callback: Option<Box<dyn FnMut()>>,
|
||||
resign_active_callback: Option<Box<dyn FnMut()>>,
|
||||
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
||||
open_files_callback: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
|
||||
dispatcher: Arc<Dispatcher>,
|
||||
}
|
||||
|
||||
const RUST_WRAPPER_IVAR_NAME: &'static str = "rustWrapper";
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
dispatcher: Arc::new(Dispatcher),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::App for App {
|
||||
fn on_finish_launching<F: 'static + FnOnce()>(mut self, callback: F) -> Self {
|
||||
self.finish_launching_callback = Some(Box::new(callback));
|
||||
self
|
||||
impl platform::App for App {
|
||||
fn dispatcher(&self) -> Arc<dyn platform::Dispatcher> {
|
||||
self.dispatcher.clone()
|
||||
}
|
||||
|
||||
fn on_become_active<F: 'static + FnMut()>(mut self, callback: F) -> Self {
|
||||
self.become_active_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_resign_active<F: 'static + FnMut()>(mut self, callback: F) -> Self {
|
||||
self.resign_active_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_event<F: 'static + FnMut(Event) -> bool>(mut self, callback: F) -> Self {
|
||||
self.event_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_open_files<F: 'static + FnMut(Vec<PathBuf>)>(mut self, callback: F) -> Self {
|
||||
self.open_files_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn run(self) {
|
||||
fn activate(&self, ignoring_other_apps: bool) {
|
||||
unsafe {
|
||||
let self_ptr = Box::into_raw(Box::new(self));
|
||||
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let app: id = msg_send![build_app_class(), sharedApplication];
|
||||
(*app).set_ivar(RUST_WRAPPER_IVAR_NAME, self_ptr as *mut c_void);
|
||||
let app_delegate: id = msg_send![build_app_delegate_class(), new];
|
||||
(*app_delegate).set_ivar(RUST_WRAPPER_IVAR_NAME, self_ptr as *mut c_void);
|
||||
let _: () = msg_send![app, setDelegate: app_delegate];
|
||||
let _: () = msg_send![app, run];
|
||||
let _: () = msg_send![pool, drain];
|
||||
|
||||
// App is done running when we get here, so we can reinstantiate the Box and drop it.
|
||||
Box::from_raw(self_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_app_class() -> *const Class {
|
||||
unsafe {
|
||||
let mut decl = ClassDecl::new("GPUIApplication", class!(NSApplication)).unwrap();
|
||||
decl.add_ivar::<*mut c_void>(RUST_WRAPPER_IVAR_NAME);
|
||||
decl.add_method(
|
||||
sel!(sendEvent:),
|
||||
send_event as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.register()
|
||||
}
|
||||
}
|
||||
|
||||
fn build_app_delegate_class() -> *const Class {
|
||||
unsafe {
|
||||
let superclass = class!(NSResponder);
|
||||
let mut decl = ClassDecl::new("GPUIApplicationDelegate", superclass).unwrap();
|
||||
decl.add_ivar::<*mut c_void>(RUST_WRAPPER_IVAR_NAME);
|
||||
decl.add_method(
|
||||
sel!(applicationDidFinishLaunching:),
|
||||
did_finish_launching as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidBecomeActive:),
|
||||
did_become_active as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidResignActive:),
|
||||
did_resign_active as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:openFiles:),
|
||||
open_files as extern "C" fn(&Object, Sel, id, id),
|
||||
);
|
||||
decl.register()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_app(object: &Object) -> &mut App {
|
||||
let wrapper_ptr: *mut c_void = *object.get_ivar(RUST_WRAPPER_IVAR_NAME);
|
||||
&mut *(wrapper_ptr as *mut App)
|
||||
}
|
||||
|
||||
extern "C" fn send_event(this: &Object, _sel: Sel, native_event: id) {
|
||||
let event = unsafe { Event::from_native(native_event, None) };
|
||||
|
||||
if let Some(event) = event {
|
||||
let app = unsafe { get_app(this) };
|
||||
if let Some(callback) = app.event_callback.as_mut() {
|
||||
if callback(event) {
|
||||
return;
|
||||
}
|
||||
let app: id = msg_send![class!(NSApplication), sharedApplication];
|
||||
let _: () = msg_send![app, activateIgnoringOtherApps: ignoring_other_apps.to_objc()];
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![super(this, class!(NSApplication)), sendEvent: native_event];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn did_finish_launching(this: &Object, _: Sel, _: id) {
|
||||
let app = unsafe { get_app(this) };
|
||||
if let Some(callback) = app.finish_launching_callback.take() {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn did_become_active(this: &Object, _: Sel, _: id) {
|
||||
let app = unsafe { get_app(this) };
|
||||
if let Some(callback) = app.become_active_callback.as_mut() {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn did_resign_active(this: &Object, _: Sel, _: id) {
|
||||
let app = unsafe { get_app(this) };
|
||||
if let Some(callback) = app.resign_active_callback.as_mut() {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn open_files(this: &Object, _: Sel, _: id, paths: id) {
|
||||
let paths = unsafe {
|
||||
(0..paths.count())
|
||||
.into_iter()
|
||||
.filter_map(|i| {
|
||||
let path = paths.objectAtIndex(i);
|
||||
match CStr::from_ptr(path.UTF8String() as *mut c_char).to_str() {
|
||||
Ok(string) => Some(PathBuf::from(string)),
|
||||
Err(err) => {
|
||||
log::error!("error converting path to string: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let app = unsafe { get_app(this) };
|
||||
if let Some(callback) = app.open_files_callback.as_mut() {
|
||||
callback(paths);
|
||||
fn open_window(
|
||||
&self,
|
||||
options: platform::WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Result<Rc<dyn platform::Window>> {
|
||||
Ok(Rc::new(Window::open(options, executor)?))
|
||||
}
|
||||
}
|
||||
|
|
1
gpui/src/platform/mac/dispatch.h
Normal file
1
gpui/src/platform/mac/dispatch.h
Normal file
|
@ -0,0 +1 @@
|
|||
#include <dispatch/dispatch.h>
|
43
gpui/src/platform/mac/dispatcher.rs
Normal file
43
gpui/src/platform/mac/dispatcher.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use async_task::Runnable;
|
||||
use objc::{
|
||||
class, msg_send,
|
||||
runtime::{BOOL, YES},
|
||||
sel, sel_impl,
|
||||
};
|
||||
use std::ffi::c_void;
|
||||
|
||||
use crate::platform;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/dispatch_sys.rs"));
|
||||
|
||||
pub fn dispatch_get_main_queue() -> dispatch_queue_t {
|
||||
unsafe { &_dispatch_main_q as *const _ as dispatch_queue_t }
|
||||
}
|
||||
|
||||
pub struct Dispatcher;
|
||||
|
||||
impl platform::Dispatcher for Dispatcher {
|
||||
fn is_main_thread(&self) -> bool {
|
||||
let is_main_thread: BOOL = unsafe { msg_send![class!(NSThread), isMainThread] };
|
||||
is_main_thread == YES
|
||||
}
|
||||
|
||||
fn run_on_main_thread(&self, runnable: Runnable) {
|
||||
unsafe {
|
||||
dispatch_async_f(
|
||||
dispatch_get_main_queue(),
|
||||
runnable.into_raw() as *mut c_void,
|
||||
Some(trampoline),
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" fn trampoline(runnable: *mut c_void) {
|
||||
let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
use super::Event;
|
||||
use crate::{geometry::vector::vec2f, keymap::Keystroke};
|
||||
use crate::{geometry::vector::vec2f, keymap::Keystroke, platform::Event};
|
||||
use cocoa::appkit::{
|
||||
NSDeleteFunctionKey as DELETE_KEY, NSDownArrowFunctionKey as ARROW_DOWN_KEY,
|
||||
NSLeftArrowFunctionKey as ARROW_LEFT_KEY, NSPageDownFunctionKey as PAGE_DOWN_KEY,
|
||||
|
|
26
gpui/src/platform/mac/geometry.rs
Normal file
26
gpui/src/platform/mac/geometry.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use cocoa::foundation::{NSPoint, NSRect, NSSize};
|
||||
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
pub trait Vector2FExt {
|
||||
fn to_ns_point(&self) -> NSPoint;
|
||||
fn to_ns_size(&self) -> NSSize;
|
||||
}
|
||||
|
||||
pub trait RectFExt {
|
||||
fn to_ns_rect(&self) -> NSRect;
|
||||
}
|
||||
|
||||
impl Vector2FExt for Vector2F {
|
||||
fn to_ns_point(&self) -> NSPoint {
|
||||
NSPoint::new(self.x() as f64, self.y() as f64)
|
||||
}
|
||||
|
||||
fn to_ns_size(&self) -> NSSize {
|
||||
NSSize::new(self.x() as f64, self.y() as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl RectFExt for RectF {
|
||||
fn to_ns_rect(&self) -> NSRect {
|
||||
NSRect::new(self.origin().to_ns_point(), self.size().to_ns_size())
|
||||
}
|
||||
}
|
|
@ -1,8 +1,34 @@
|
|||
use super::*;
|
||||
|
||||
mod app;
|
||||
mod dispatcher;
|
||||
mod event;
|
||||
mod geometry;
|
||||
mod runner;
|
||||
mod window;
|
||||
|
||||
pub fn app() -> impl App {
|
||||
app::App::default()
|
||||
use crate::platform;
|
||||
pub use app::App;
|
||||
use cocoa::base::{BOOL, NO, YES};
|
||||
pub use dispatcher::Dispatcher;
|
||||
pub use runner::Runner;
|
||||
use window::Window;
|
||||
|
||||
pub fn app() -> impl platform::App {
|
||||
App::new()
|
||||
}
|
||||
|
||||
pub fn runner() -> impl platform::Runner {
|
||||
Runner::new()
|
||||
}
|
||||
trait BoolExt {
|
||||
fn to_objc(self) -> BOOL;
|
||||
}
|
||||
|
||||
impl BoolExt for bool {
|
||||
fn to_objc(self) -> BOOL {
|
||||
if self {
|
||||
YES
|
||||
} else {
|
||||
NO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
188
gpui/src/platform/mac/runner.rs
Normal file
188
gpui/src/platform/mac/runner.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
use crate::platform::Event;
|
||||
use cocoa::{
|
||||
appkit::NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
|
||||
base::{id, nil},
|
||||
foundation::{NSArray, NSAutoreleasePool, NSString},
|
||||
};
|
||||
use ctor::ctor;
|
||||
use objc::{
|
||||
class,
|
||||
declare::ClassDecl,
|
||||
msg_send,
|
||||
runtime::{Class, Object, Sel},
|
||||
sel, sel_impl,
|
||||
};
|
||||
use std::{
|
||||
ffi::CStr,
|
||||
os::raw::{c_char, c_void},
|
||||
path::PathBuf,
|
||||
ptr,
|
||||
};
|
||||
|
||||
const RUNNER_IVAR: &'static str = "runner";
|
||||
static mut APP_CLASS: *const Class = ptr::null();
|
||||
static mut APP_DELEGATE_CLASS: *const Class = ptr::null();
|
||||
|
||||
#[ctor]
|
||||
unsafe fn build_classes() {
|
||||
APP_CLASS = {
|
||||
let mut decl = ClassDecl::new("GPUIApplication", class!(NSApplication)).unwrap();
|
||||
decl.add_ivar::<*mut c_void>(RUNNER_IVAR);
|
||||
decl.add_method(
|
||||
sel!(sendEvent:),
|
||||
send_event as extern "C" fn(&mut Object, Sel, id),
|
||||
);
|
||||
decl.register()
|
||||
};
|
||||
|
||||
APP_DELEGATE_CLASS = {
|
||||
let mut decl = ClassDecl::new("GPUIApplicationDelegate", class!(NSResponder)).unwrap();
|
||||
decl.add_ivar::<*mut c_void>(RUNNER_IVAR);
|
||||
decl.add_method(
|
||||
sel!(applicationDidFinishLaunching:),
|
||||
did_finish_launching as extern "C" fn(&mut Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidBecomeActive:),
|
||||
did_become_active as extern "C" fn(&mut Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidResignActive:),
|
||||
did_resign_active as extern "C" fn(&mut Object, Sel, id),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:openFiles:),
|
||||
open_files as extern "C" fn(&mut Object, Sel, id, id),
|
||||
);
|
||||
decl.register()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Runner {
|
||||
finish_launching_callback: Option<Box<dyn FnOnce()>>,
|
||||
become_active_callback: Option<Box<dyn FnMut()>>,
|
||||
resign_active_callback: Option<Box<dyn FnMut()>>,
|
||||
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
||||
open_files_callback: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
|
||||
}
|
||||
|
||||
impl Runner {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::platform::Runner for Runner {
|
||||
fn on_finish_launching<F: 'static + FnOnce()>(mut self, callback: F) -> Self {
|
||||
self.finish_launching_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_become_active<F: 'static + FnMut()>(mut self, callback: F) -> Self {
|
||||
log::info!("become active");
|
||||
self.become_active_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_resign_active<F: 'static + FnMut()>(mut self, callback: F) -> Self {
|
||||
self.resign_active_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_event<F: 'static + FnMut(Event) -> bool>(mut self, callback: F) -> Self {
|
||||
self.event_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn on_open_files<F: 'static + FnMut(Vec<PathBuf>)>(mut self, callback: F) -> Self {
|
||||
self.open_files_callback = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
fn run(self) {
|
||||
unsafe {
|
||||
let self_ptr = Box::into_raw(Box::new(self));
|
||||
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
let app: id = msg_send![APP_CLASS, sharedApplication];
|
||||
let _: () = msg_send![
|
||||
app,
|
||||
setActivationPolicy: NSApplicationActivationPolicyRegular
|
||||
];
|
||||
(*app).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void);
|
||||
let app_delegate: id = msg_send![APP_DELEGATE_CLASS, new];
|
||||
(*app_delegate).set_ivar(RUNNER_IVAR, self_ptr as *mut c_void);
|
||||
let _: () = msg_send![app, setDelegate: app_delegate];
|
||||
let _: () = msg_send![app, run];
|
||||
let _: () = msg_send![pool, drain];
|
||||
// The Runner is done running when we get here, so we can reinstantiate the Box and drop it.
|
||||
Box::from_raw(self_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get_runner(object: &mut Object) -> &mut Runner {
|
||||
let runner_ptr: *mut c_void = *object.get_ivar(RUNNER_IVAR);
|
||||
&mut *(runner_ptr as *mut Runner)
|
||||
}
|
||||
|
||||
extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
|
||||
let event = unsafe { Event::from_native(native_event, None) };
|
||||
|
||||
if let Some(event) = event {
|
||||
let runner = unsafe { get_runner(this) };
|
||||
if let Some(callback) = runner.event_callback.as_mut() {
|
||||
if callback(event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![super(this, class!(NSApplication)), sendEvent: native_event];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
|
||||
let runner = unsafe { get_runner(this) };
|
||||
if let Some(callback) = runner.finish_launching_callback.take() {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) {
|
||||
let runner = unsafe { get_runner(this) };
|
||||
if let Some(callback) = runner.become_active_callback.as_mut() {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) {
|
||||
let runner = unsafe { get_runner(this) };
|
||||
if let Some(callback) = runner.resign_active_callback.as_mut() {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn open_files(this: &mut Object, _: Sel, _: id, paths: id) {
|
||||
let paths = unsafe {
|
||||
(0..paths.count())
|
||||
.into_iter()
|
||||
.filter_map(|i| {
|
||||
let path = paths.objectAtIndex(i);
|
||||
match CStr::from_ptr(path.UTF8String() as *mut c_char).to_str() {
|
||||
Ok(string) => Some(PathBuf::from(string)),
|
||||
Err(err) => {
|
||||
log::error!("error converting path to string: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let runner = unsafe { get_runner(this) };
|
||||
if let Some(callback) = runner.open_files_callback.as_mut() {
|
||||
callback(paths);
|
||||
}
|
||||
}
|
353
gpui/src/platform/mac/window.rs
Normal file
353
gpui/src/platform/mac/window.rs
Normal file
|
@ -0,0 +1,353 @@
|
|||
use crate::{
|
||||
executor,
|
||||
geometry::vector::Vector2F,
|
||||
platform::{self, Event},
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use cocoa::{
|
||||
appkit::{
|
||||
NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable, NSViewWidthSizable,
|
||||
NSWindow, NSWindowStyleMask,
|
||||
},
|
||||
base::{id, nil},
|
||||
foundation::{NSAutoreleasePool, NSSize, NSString},
|
||||
};
|
||||
use ctor::ctor;
|
||||
use objc::{
|
||||
class,
|
||||
declare::ClassDecl,
|
||||
msg_send,
|
||||
runtime::{Class, Object, Sel, BOOL, NO, YES},
|
||||
sel, sel_impl,
|
||||
};
|
||||
use smol::Timer;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
ffi::c_void,
|
||||
mem, ptr,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use super::geometry::RectFExt;
|
||||
|
||||
const WINDOW_STATE_IVAR: &'static str = "windowState";
|
||||
|
||||
static mut WINDOW_CLASS: *const Class = ptr::null();
|
||||
static mut VIEW_CLASS: *const Class = ptr::null();
|
||||
static mut DELEGATE_CLASS: *const Class = ptr::null();
|
||||
|
||||
#[ctor]
|
||||
unsafe fn build_classes() {
|
||||
WINDOW_CLASS = {
|
||||
let mut decl = ClassDecl::new("GPUIWindow", class!(NSWindow)).unwrap();
|
||||
decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
|
||||
decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
|
||||
decl.add_method(
|
||||
sel!(canBecomeMainWindow),
|
||||
yes as extern "C" fn(&Object, Sel) -> BOOL,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(canBecomeKeyWindow),
|
||||
yes as extern "C" fn(&Object, Sel) -> BOOL,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(sendEvent:),
|
||||
send_event as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.register()
|
||||
};
|
||||
|
||||
VIEW_CLASS = {
|
||||
let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
|
||||
decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
|
||||
decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
|
||||
decl.add_method(
|
||||
sel!(keyDown:),
|
||||
handle_view_event as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
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!(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.register()
|
||||
};
|
||||
|
||||
DELEGATE_CLASS = {
|
||||
let mut decl = ClassDecl::new("GPUIWindowDelegate", class!(NSObject)).unwrap();
|
||||
decl.add_method(
|
||||
sel!(dealloc),
|
||||
dealloc_delegate as extern "C" fn(&Object, Sel),
|
||||
);
|
||||
decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
|
||||
decl.add_method(
|
||||
sel!(windowDidResize:),
|
||||
window_did_resize as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
decl.register()
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Window(Rc<WindowState>);
|
||||
|
||||
struct WindowState {
|
||||
native_window: id,
|
||||
event_callback: RefCell<Option<Box<dyn FnMut(Event) -> bool>>>,
|
||||
resize_callback: RefCell<Option<Box<dyn FnMut(NSSize, f64)>>>,
|
||||
synthetic_drag_counter: Cell<usize>,
|
||||
executor: Rc<executor::Foreground>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn open(
|
||||
options: platform::WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Result<Self> {
|
||||
unsafe {
|
||||
let pool = NSAutoreleasePool::new(nil);
|
||||
|
||||
let frame = options.bounds.to_ns_rect();
|
||||
let style_mask = NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSTitledWindowMask;
|
||||
|
||||
let native_window: id = msg_send![WINDOW_CLASS, alloc];
|
||||
let native_window = native_window.initWithContentRect_styleMask_backing_defer_(
|
||||
frame,
|
||||
style_mask,
|
||||
NSBackingStoreBuffered,
|
||||
NO,
|
||||
);
|
||||
|
||||
if native_window == nil {
|
||||
return Err(anyhow!("window returned nil from initializer"));
|
||||
}
|
||||
|
||||
let delegate: id = msg_send![DELEGATE_CLASS, alloc];
|
||||
let delegate = delegate.init();
|
||||
if native_window == nil {
|
||||
return Err(anyhow!("delegate returned nil from initializer"));
|
||||
}
|
||||
native_window.setDelegate_(delegate);
|
||||
|
||||
let native_view: id = msg_send![VIEW_CLASS, alloc];
|
||||
let native_view = NSView::init(native_view);
|
||||
if native_view == nil {
|
||||
return Err(anyhow!("view return nil from initializer"));
|
||||
}
|
||||
|
||||
let window = Self(Rc::new(WindowState {
|
||||
native_window,
|
||||
event_callback: RefCell::new(None),
|
||||
resize_callback: RefCell::new(None),
|
||||
synthetic_drag_counter: Cell::new(0),
|
||||
executor,
|
||||
}));
|
||||
|
||||
(*native_window).set_ivar(
|
||||
WINDOW_STATE_IVAR,
|
||||
Rc::into_raw(window.0.clone()) as *const c_void,
|
||||
);
|
||||
(*native_view).set_ivar(
|
||||
WINDOW_STATE_IVAR,
|
||||
Rc::into_raw(window.0.clone()) as *const c_void,
|
||||
);
|
||||
(*delegate).set_ivar(
|
||||
WINDOW_STATE_IVAR,
|
||||
Rc::into_raw(window.0.clone()) as *const c_void,
|
||||
);
|
||||
|
||||
if let Some(title) = options.title.as_ref() {
|
||||
native_window.setTitle_(NSString::alloc(nil).init_str(title));
|
||||
}
|
||||
native_window.setAcceptsMouseMovedEvents_(YES);
|
||||
|
||||
native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
|
||||
native_view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
|
||||
// From winit crate: On Mojave, views automatically become layer-backed shortly after
|
||||
// being added to a native_window. Changing the layer-backedness of a view breaks the
|
||||
// association between the view and its associated OpenGL context. To work around this,
|
||||
// on we explicitly make the view layer-backed up front so that AppKit doesn't do it
|
||||
// itself and break the association with its context.
|
||||
native_view.setWantsLayer(YES);
|
||||
|
||||
native_view.layer().setBackgroundColor_(
|
||||
msg_send![class!(NSColor), colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0],
|
||||
);
|
||||
|
||||
native_window.setContentView_(native_view.autorelease());
|
||||
native_window.makeFirstResponder_(native_view);
|
||||
|
||||
native_window.center();
|
||||
native_window.makeKeyAndOrderFront_(nil);
|
||||
|
||||
pool.drain();
|
||||
|
||||
Ok(window)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zoom(&self) {
|
||||
unsafe {
|
||||
self.0.native_window.performZoom_(nil);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> NSSize {
|
||||
self.0.size()
|
||||
}
|
||||
|
||||
pub fn backing_scale_factor(&self) -> f64 {
|
||||
self.0.backing_scale_factor()
|
||||
}
|
||||
|
||||
pub fn on_event<F: 'static + FnMut(Event) -> bool>(&mut self, callback: F) {
|
||||
*self.0.event_callback.borrow_mut() = Some(Box::new(callback));
|
||||
}
|
||||
|
||||
pub fn on_resize<F: 'static + FnMut(NSSize, f64)>(&mut self, callback: F) {
|
||||
*self.0.resize_callback.borrow_mut() = Some(Box::new(callback));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.0.native_window.close();
|
||||
let _: () = msg_send![self.0.native_window.delegate(), release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl platform::Window for Window {}
|
||||
|
||||
impl WindowState {
|
||||
fn size(&self) -> NSSize {
|
||||
let view_frame = unsafe { NSView::frame(self.native_window.contentView()) };
|
||||
view_frame.size
|
||||
}
|
||||
|
||||
fn backing_scale_factor(&self) -> f64 {
|
||||
unsafe {
|
||||
let screen: id = msg_send![self.native_window, screen];
|
||||
NSScreen::backingScaleFactor(screen)
|
||||
}
|
||||
}
|
||||
|
||||
fn next_synthetic_drag_id(&self) -> usize {
|
||||
let next_id = self.synthetic_drag_counter.get() + 1;
|
||||
self.synthetic_drag_counter.set(next_id);
|
||||
next_id
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn window_state(object: &Object) -> Rc<WindowState> {
|
||||
let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
|
||||
let rc1 = Rc::from_raw(raw as *mut WindowState);
|
||||
let rc2 = rc1.clone();
|
||||
mem::forget(rc1);
|
||||
rc2
|
||||
}
|
||||
|
||||
unsafe fn drop_window_state(object: &Object) {
|
||||
let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
|
||||
Rc::from_raw(raw as *mut WindowState);
|
||||
}
|
||||
|
||||
extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
|
||||
YES
|
||||
}
|
||||
|
||||
extern "C" fn dealloc_window(this: &Object, _: Sel) {
|
||||
unsafe {
|
||||
drop_window_state(this);
|
||||
let () = msg_send![super(this, class!(NSWindow)), dealloc];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn dealloc_view(this: &Object, _: Sel) {
|
||||
unsafe {
|
||||
drop_window_state(this);
|
||||
let () = msg_send![super(this, class!(NSView)), dealloc];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn dealloc_delegate(this: &Object, _: Sel) {
|
||||
unsafe {
|
||||
let raw: *mut c_void = *this.get_ivar(WINDOW_STATE_IVAR);
|
||||
Rc::from_raw(raw as *mut WindowState);
|
||||
let () = msg_send![super(this, class!(NSObject)), dealloc];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
let window = unsafe { window_state(this) };
|
||||
|
||||
let event = unsafe { Event::from_native(native_event, Some(window.size().height as f32)) };
|
||||
|
||||
if let Some(event) = event {
|
||||
match event {
|
||||
Event::LeftMouseDragged { position } => schedule_synthetic_drag(&window, position),
|
||||
Event::LeftMouseUp { .. } => {
|
||||
window.next_synthetic_drag_id();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(callback) = window.event_callback.borrow_mut().as_mut() {
|
||||
if callback(event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
|
||||
unsafe {
|
||||
let () = msg_send![super(this, class!(NSWindow)), sendEvent: native_event];
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
|
||||
let window = unsafe { window_state(this) };
|
||||
let size = window.size();
|
||||
let scale_factor = window.backing_scale_factor();
|
||||
if let Some(callback) = window.resize_callback.borrow_mut().as_mut() {
|
||||
callback(size, scale_factor);
|
||||
}
|
||||
drop(window);
|
||||
}
|
||||
|
||||
fn schedule_synthetic_drag(window_state: &Rc<WindowState>, position: Vector2F) {
|
||||
let drag_id = window_state.next_synthetic_drag_id();
|
||||
let weak_window_state = Rc::downgrade(window_state);
|
||||
let instant = Instant::now() + Duration::from_millis(16);
|
||||
window_state
|
||||
.executor
|
||||
.spawn(async move {
|
||||
Timer::at(instant).await;
|
||||
if let Some(window_state) = weak_window_state.upgrade() {
|
||||
if window_state.synthetic_drag_counter.get() == drag_id {
|
||||
if let Some(callback) = window_state.event_callback.borrow_mut().as_mut() {
|
||||
schedule_synthetic_drag(&window_state, position);
|
||||
callback(Event::LeftMouseDragged { position });
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
|
@ -6,11 +6,13 @@ pub mod current {
|
|||
pub use super::mac::*;
|
||||
}
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{executor, geometry::rect::RectF};
|
||||
use anyhow::Result;
|
||||
use async_task::Runnable;
|
||||
use event::Event;
|
||||
use std::{path::PathBuf, rc::Rc, sync::Arc};
|
||||
|
||||
pub trait App {
|
||||
pub trait Runner {
|
||||
fn on_finish_launching<F: 'static + FnOnce()>(self, callback: F) -> Self where;
|
||||
fn on_become_active<F: 'static + FnMut()>(self, callback: F) -> Self;
|
||||
fn on_resign_active<F: 'static + FnMut()>(self, callback: F) -> Self;
|
||||
|
@ -18,3 +20,25 @@ pub trait App {
|
|||
fn on_open_files<F: 'static + FnMut(Vec<PathBuf>)>(self, callback: F) -> Self;
|
||||
fn run(self);
|
||||
}
|
||||
|
||||
pub trait App {
|
||||
fn dispatcher(&self) -> Arc<dyn Dispatcher>;
|
||||
fn activate(&self, ignoring_other_apps: bool);
|
||||
fn open_window(
|
||||
&self,
|
||||
options: WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Result<Rc<dyn Window>>;
|
||||
}
|
||||
|
||||
pub trait Dispatcher: Send + Sync {
|
||||
fn is_main_thread(&self) -> bool;
|
||||
fn run_on_main_thread(&self, task: Runnable);
|
||||
}
|
||||
|
||||
pub trait Window {}
|
||||
|
||||
pub struct WindowOptions<'a> {
|
||||
pub bounds: RectF,
|
||||
pub title: Option<&'a str>,
|
||||
}
|
||||
|
|
|
@ -1,14 +1,41 @@
|
|||
use std::fs;
|
||||
|
||||
use fs::OpenOptions;
|
||||
use gpui::platform::{current as platform, App as _};
|
||||
use gpui::{
|
||||
executor,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
platform::{current as platform, App as _, Runner as _, WindowOptions},
|
||||
};
|
||||
use log::LevelFilter;
|
||||
use simplelog::SimpleLogger;
|
||||
use std::{fs, mem, rc::Rc, sync::Arc};
|
||||
|
||||
fn main() {
|
||||
init_logger();
|
||||
platform::app()
|
||||
.on_finish_launching(|| log::info!("finish launching"))
|
||||
|
||||
let platform = Arc::new(platform::app());
|
||||
|
||||
let foreground = Rc::new(
|
||||
executor::Foreground::platform(platform.dispatcher())
|
||||
.expect("could not foreground create executor"),
|
||||
);
|
||||
|
||||
platform::runner()
|
||||
.on_finish_launching(move || {
|
||||
log::info!("finish launching");
|
||||
if stdout_is_a_pty() {
|
||||
platform.activate(true);
|
||||
}
|
||||
let window = platform
|
||||
.open_window(
|
||||
WindowOptions {
|
||||
bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)),
|
||||
title: Some("Zed"),
|
||||
},
|
||||
foreground,
|
||||
)
|
||||
.expect("error opening window");
|
||||
|
||||
mem::forget(window); // Leak window for now so it doesn't close
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue