From 222f9d373df677d7c5f8427984b4206f36f53a2a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Sat, 20 Feb 2021 16:05:36 -0700 Subject: [PATCH] WIP --- .vscode/launch.json | 64 +++++ Cargo.lock | 231 +++++++++++++++++- gpui/Cargo.toml | 6 + gpui/build.rs | 23 ++ gpui/src/executor.rs | 114 +++++++++ gpui/src/lib.rs | 1 + gpui/src/platform/mac/app.rs | 189 ++------------- gpui/src/platform/mac/dispatch.h | 1 + gpui/src/platform/mac/dispatcher.rs | 43 ++++ gpui/src/platform/mac/event.rs | 3 +- gpui/src/platform/mac/geometry.rs | 26 ++ gpui/src/platform/mac/mod.rs | 34 ++- gpui/src/platform/mac/runner.rs | 188 +++++++++++++++ gpui/src/platform/mac/window.rs | 353 ++++++++++++++++++++++++++++ gpui/src/platform/mod.rs | 30 ++- zed/src/main.rs | 37 ++- 16 files changed, 1164 insertions(+), 179 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 gpui/build.rs create mode 100644 gpui/src/executor.rs create mode 100644 gpui/src/platform/mac/dispatch.h create mode 100644 gpui/src/platform/mac/dispatcher.rs create mode 100644 gpui/src/platform/mac/geometry.rs create mode 100644 gpui/src/platform/mac/runner.rs create mode 100644 gpui/src/platform/mac/window.rs diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..8071d75f8b --- /dev/null +++ b/.vscode/launch.json @@ -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}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 67be2f74aa..c648e2379c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/gpui/Cargo.toml b/gpui/Cargo.toml index 04373d80eb..1e77e4b139 100644 --- a/gpui/Cargo.toml +++ b/gpui/Cargo.toml @@ -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" diff --git a/gpui/build.rs b/gpui/build.rs new file mode 100644 index 0000000000..82a5c82617 --- /dev/null +++ b/gpui/build.rs @@ -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"); +} diff --git a/gpui/src/executor.rs b/gpui/src/executor.rs new file mode 100644 index 0000000000..1261ec9e87 --- /dev/null +++ b/gpui/src/executor.rs @@ -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, + _not_send_or_sync: PhantomData>, + }, + Test(smol::LocalExecutor<'static>), +} + +pub enum ForegroundTask { + Platform(async_task::Task), + Test(smol::Task), +} + +pub struct Background { + executor: Arc>, + _stop: channel::Sender<()>, +} + +pub type BackgroundTask = smol::Task; + +impl Foreground { + pub fn platform(dispatcher: Arc) -> Result { + 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( + &self, + future: impl Future + 'static, + ) -> ForegroundTask { + 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(&self, future: impl Future) -> T { + match self { + Self::Platform { .. } => panic!("you can't call run on a platform foreground executor"), + Self::Test(executor) => executor.run(future).await, + } + } +} + +impl ForegroundTask { + pub fn detach(self) { + match self { + Self::Platform(task) => task.detach(), + Self::Test(task) => task.detach(), + } + } + + pub async fn cancel(self) -> Option { + 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(&self, future: impl Send + Future + 'static) -> BackgroundTask + where + T: 'static + Send, + { + self.executor.spawn(future) + } +} diff --git a/gpui/src/lib.rs b/gpui/src/lib.rs index ee2a24d465..67fbbf1ac7 100644 --- a/gpui/src/lib.rs +++ b/gpui/src/lib.rs @@ -1,3 +1,4 @@ +pub mod executor; pub mod keymap; pub mod platform; diff --git a/gpui/src/platform/mac/app.rs b/gpui/src/platform/mac/app.rs index c0ba2ad7d0..11805a5db3 100644 --- a/gpui/src/platform/mac/app.rs +++ b/gpui/src/platform/mac/app.rs @@ -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>, - become_active_callback: Option>, - resign_active_callback: Option>, - event_callback: Option bool>>, - open_files_callback: Option)>>, + dispatcher: Arc, } -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(mut self, callback: F) -> Self { - self.finish_launching_callback = Some(Box::new(callback)); - self +impl platform::App for App { + fn dispatcher(&self) -> Arc { + self.dispatcher.clone() } - fn on_become_active(mut self, callback: F) -> Self { - self.become_active_callback = Some(Box::new(callback)); - self - } - - fn on_resign_active(mut self, callback: F) -> Self { - self.resign_active_callback = Some(Box::new(callback)); - self - } - - fn on_event bool>(mut self, callback: F) -> Self { - self.event_callback = Some(Box::new(callback)); - self - } - - fn on_open_files)>(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::>() - }; - 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, + ) -> Result> { + Ok(Rc::new(Window::open(options, executor)?)) } } diff --git a/gpui/src/platform/mac/dispatch.h b/gpui/src/platform/mac/dispatch.h new file mode 100644 index 0000000000..f56a0eae33 --- /dev/null +++ b/gpui/src/platform/mac/dispatch.h @@ -0,0 +1 @@ +#include diff --git a/gpui/src/platform/mac/dispatcher.rs b/gpui/src/platform/mac/dispatcher.rs new file mode 100644 index 0000000000..6b41676c4c --- /dev/null +++ b/gpui/src/platform/mac/dispatcher.rs @@ -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(); + } + } +} diff --git a/gpui/src/platform/mac/event.rs b/gpui/src/platform/mac/event.rs index 3123961b64..694c160784 100644 --- a/gpui/src/platform/mac/event.rs +++ b/gpui/src/platform/mac/event.rs @@ -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, diff --git a/gpui/src/platform/mac/geometry.rs b/gpui/src/platform/mac/geometry.rs new file mode 100644 index 0000000000..dc64c48f52 --- /dev/null +++ b/gpui/src/platform/mac/geometry.rs @@ -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()) + } +} diff --git a/gpui/src/platform/mac/mod.rs b/gpui/src/platform/mac/mod.rs index ad903d444e..47c3a13ae6 100644 --- a/gpui/src/platform/mac/mod.rs +++ b/gpui/src/platform/mac/mod.rs @@ -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 + } + } } diff --git a/gpui/src/platform/mac/runner.rs b/gpui/src/platform/mac/runner.rs new file mode 100644 index 0000000000..c407bf59d7 --- /dev/null +++ b/gpui/src/platform/mac/runner.rs @@ -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>, + become_active_callback: Option>, + resign_active_callback: Option>, + event_callback: Option bool>>, + open_files_callback: Option)>>, +} + +impl Runner { + pub fn new() -> Self { + Default::default() + } +} + +impl crate::platform::Runner for Runner { + fn on_finish_launching(mut self, callback: F) -> Self { + self.finish_launching_callback = Some(Box::new(callback)); + self + } + + fn on_become_active(mut self, callback: F) -> Self { + log::info!("become active"); + self.become_active_callback = Some(Box::new(callback)); + self + } + + fn on_resign_active(mut self, callback: F) -> Self { + self.resign_active_callback = Some(Box::new(callback)); + self + } + + fn on_event bool>(mut self, callback: F) -> Self { + self.event_callback = Some(Box::new(callback)); + self + } + + fn on_open_files)>(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::>() + }; + let runner = unsafe { get_runner(this) }; + if let Some(callback) = runner.open_files_callback.as_mut() { + callback(paths); + } +} diff --git a/gpui/src/platform/mac/window.rs b/gpui/src/platform/mac/window.rs new file mode 100644 index 0000000000..5bb7bc6190 --- /dev/null +++ b/gpui/src/platform/mac/window.rs @@ -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); + +struct WindowState { + native_window: id, + event_callback: RefCell bool>>>, + resize_callback: RefCell>>, + synthetic_drag_counter: Cell, + executor: Rc, +} + +impl Window { + pub fn open( + options: platform::WindowOptions, + executor: Rc, + ) -> Result { + 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 bool>(&mut self, callback: F) { + *self.0.event_callback.borrow_mut() = Some(Box::new(callback)); + } + + pub fn on_resize(&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 { + 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, 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(); +} diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index 6fa70cfc08..e22e6ff674 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -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(self, callback: F) -> Self where; fn on_become_active(self, callback: F) -> Self; fn on_resign_active(self, callback: F) -> Self; @@ -18,3 +20,25 @@ pub trait App { fn on_open_files)>(self, callback: F) -> Self; fn run(self); } + +pub trait App { + fn dispatcher(&self) -> Arc; + fn activate(&self, ignoring_other_apps: bool); + fn open_window( + &self, + options: WindowOptions, + executor: Rc, + ) -> Result>; +} + +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>, +} diff --git a/zed/src/main.rs b/zed/src/main.rs index ef373ae5bc..038c5917f8 100644 --- a/zed/src/main.rs +++ b/zed/src/main.rs @@ -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(); }