diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index 449f5b598..1e63fa5d5 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -6,8 +6,15 @@ app_main!(App); script_mod! { use mod.prelude.widgets.* + let state = { + counter: 0 + } + mod.state = state startup() do #(App::script_component(vm)){ ui: Root{ + on_startup:||{ // right now render isnt called automatically yet + ui.main_view.render() + } main_window := Window{ window.inner_size: vec2(420, 220) body +: { @@ -17,9 +24,11 @@ script_mod! { flow: Down spacing: 12 align: Center - counter_label := Label{ - text: "Count: 0" - draw_text.text_style.font_size: 24 + on_render: ||{ + counter_label := Label{ + text: "Count: " + state.counter + draw_text.text_style.font_size: 24 + } } } increment_button := Button{ @@ -35,15 +44,15 @@ script_mod! { pub struct App { #[live] ui: WidgetRef, - #[rust] - counter: i32, } impl MatchEvent for App { fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) { if self.ui.button(cx, ids!(increment_button)).clicked(actions) { - self.counter += 1; - self.ui.label(cx, ids!(counter_label)).set_text(cx, &format!("Count: {}", self.counter)); + script_eval!(cx,{ + mod.state.counter += 1 + ui.main_view.render() + }); } } } diff --git a/libs/makepad_test/src/runtime.rs b/libs/makepad_test/src/runtime.rs index a2c8d74d5..9a8c947d7 100644 --- a/libs/makepad_test/src/runtime.rs +++ b/libs/makepad_test/src/runtime.rs @@ -5,10 +5,8 @@ use makepad_micro_serde::{SerBin, SerJson}; use makepad_studio_hub::{HubConfig, HubConnection, MountConfig, StudioHub}; use makepad_studio_protocol::hub_protocol::{ClientToHub, HubToClient, LogEntry, QueryId}; use makepad_studio_protocol::{ - KeyCode, KeyEvent, KeyModifiers, MouseButton, RemoteIMEComposition, RemoteKeyModifiers, - RemoteLongPress, RemoteMouseDown, RemoteMouseMove, RemoteMouseUp, RemoteScroll, - RemoteTextPaste, RemoteTouchPoint, RemoteTouchState, RemoteTouchUpdate, StudioToApp, - StudioToAppVec, WidgetSnapshot, + KeyCode, KeyEvent, KeyModifiers, MouseButton, RemoteKeyModifiers, RemoteMouseDown, + RemoteMouseMove, RemoteMouseUp, RemoteScroll, StudioToApp, StudioToAppVec, WidgetSnapshot, }; use std::cell::RefCell; use std::cmp; @@ -87,11 +85,6 @@ pub struct TestConfig { pub startup_pause: Duration, pub action_delay: Duration, pub keep_open: Duration, - pub android: bool, - pub device_serial: Option, - pub adb_path: Option, - pub android_port: u16, - pub android_native_activity: bool, } impl TestConfig { @@ -133,11 +126,6 @@ impl TestConfig { startup_pause: env_duration_ms("MAKEPAD_TEST_STARTUP_DELAY_MS"), action_delay: env_duration_ms("MAKEPAD_TEST_ACTION_DELAY_MS"), keep_open: env_duration_ms("MAKEPAD_TEST_KEEP_OPEN_MS"), - android: android_test_enabled(), - device_serial: android_device_serial(), - adb_path: android_adb_path(), - android_port: android_hub_port(), - android_native_activity: env_truthy("MAKEPAD_TEST_NATIVE_ACTIVITY"), }) } @@ -169,13 +157,6 @@ impl TestConnection { Self::Remote(connection) => connection.recv_timeout(timeout), } } - - fn studio_addr(&self) -> Option { - match self { - Self::InProcess(connection) => connection.studio_addr(), - Self::Remote(_) => None, - } - } } struct TestAppInner { @@ -227,9 +208,7 @@ impl TestApp { } fn start_once(config: TestConfig) -> TestResult { - let (connection, build_id) = if config.android { - start_android_app(&config)? - } else if visible_mode_enabled() { + let (connection, build_id) = if visible_mode_enabled() { start_visible_app(&config)? } else { start_headless_app(&config)? @@ -475,128 +454,6 @@ impl TestApp { Ok(()) } - pub fn touch_down(&self, x: f64, y: f64) { - if let Err(err) = self.try_touch_down(x, y) { - panic_for_error(err); - } - } - - pub fn try_touch_down(&self, x: f64, y: f64) -> TestResult<()> { - self.try_forward(vec![StudioToApp::TouchUpdate(RemoteTouchUpdate { - time: now_seconds(), - touches: vec![RemoteTouchPoint { - state: RemoteTouchState::Start, - abs_x: x, - abs_y: y, - time: now_seconds(), - uid: 0, - rotation_angle: 0.0, - force: 1.0, - radius_x: 4.0, - radius_y: 4.0, - }], - })])?; - self.pace_after_action(); - Ok(()) - } - - pub fn touch_move(&self, x: f64, y: f64) { - if let Err(err) = self.try_touch_move(x, y) { - panic_for_error(err); - } - } - - pub fn try_touch_move(&self, x: f64, y: f64) -> TestResult<()> { - self.try_forward(vec![StudioToApp::TouchUpdate(RemoteTouchUpdate { - time: now_seconds(), - touches: vec![RemoteTouchPoint { - state: RemoteTouchState::Move, - abs_x: x, - abs_y: y, - time: now_seconds(), - uid: 0, - rotation_angle: 0.0, - force: 1.0, - radius_x: 4.0, - radius_y: 4.0, - }], - })])?; - self.pace_after_action(); - Ok(()) - } - - pub fn touch_up(&self, x: f64, y: f64) { - if let Err(err) = self.try_touch_up(x, y) { - panic_for_error(err); - } - } - - pub fn try_touch_up(&self, x: f64, y: f64) -> TestResult<()> { - self.try_forward(vec![StudioToApp::TouchUpdate(RemoteTouchUpdate { - time: now_seconds(), - touches: vec![RemoteTouchPoint { - state: RemoteTouchState::Stop, - abs_x: x, - abs_y: y, - time: now_seconds(), - uid: 0, - rotation_angle: 0.0, - force: 0.0, - radius_x: 4.0, - radius_y: 4.0, - }], - })])?; - self.pace_after_action(); - Ok(()) - } - - pub fn long_press(&self, x: f64, y: f64, duration_ms: f64) { - if let Err(err) = self.try_long_press(x, y, duration_ms) { - panic_for_error(err); - } - } - - pub fn try_long_press(&self, x: f64, y: f64, duration_ms: f64) -> TestResult<()> { - self.try_forward(vec![StudioToApp::LongPress(RemoteLongPress { - x, - y, - time: now_seconds(), - duration_ms, - })])?; - self.pace_after_action(); - Ok(()) - } - - pub fn paste_text(&self, text: impl AsRef) { - if let Err(err) = self.try_paste_text(text) { - panic_for_error(err); - } - } - - pub fn try_paste_text(&self, text: impl AsRef) -> TestResult<()> { - let text = text.as_ref().to_string(); - self.try_forward(vec![StudioToApp::TextPaste(RemoteTextPaste { - text, - })])?; - self.pace_after_action(); - Ok(()) - } - - pub fn ime_composition(&self, text: impl AsRef) { - if let Err(err) = self.try_ime_composition(text) { - panic_for_error(err); - } - } - - pub fn try_ime_composition(&self, text: impl AsRef) -> TestResult<()> { - let text = text.as_ref().to_string(); - self.try_forward(vec![StudioToApp::IMEComposition( - RemoteIMEComposition { text }, - )])?; - self.pace_after_action(); - Ok(()) - } - fn query_widgets( &self, selector: &Selector, @@ -766,10 +623,6 @@ impl TestApp { if inner.build_stopped.is_some() { return; } - if inner.config.android { - let full_package = android_full_package_name(&inner.config.package_name); - let _ = adb_force_stop(&inner.config, &full_package); - } let build_id = inner.build_id; let _ = inner.connection.send(ClientToHub::ClearBuild { build_id }); } @@ -1111,82 +964,6 @@ impl Locator { self.app.try_drag_from(&target, dx, dy) } - pub fn touch_down(self) -> Self { - if let Err(err) = self.try_touch_down() { - panic_for_error(err); - } - self - } - - pub fn try_touch_down(&self) -> TestResult<()> { - let target = self.resolve_unique_visible()?; - let (x, y) = snapshot_center_f64(&target); - self.app.try_touch_down(x, y) - } - - pub fn touch_move(self, dx: f64, dy: f64) -> Self { - if let Err(err) = self.try_touch_move(dx, dy) { - panic_for_error(err); - } - self - } - - pub fn try_touch_move(&self, dx: f64, dy: f64) -> TestResult<()> { - let target = self.resolve_unique_visible()?; - let (cx, cy) = snapshot_center_f64(&target); - self.app.try_touch_move(cx + dx, cy + dy) - } - - pub fn touch_up(self) -> Self { - if let Err(err) = self.try_touch_up() { - panic_for_error(err); - } - self - } - - pub fn try_touch_up(&self) -> TestResult<()> { - let target = self.resolve_unique_visible()?; - let (x, y) = snapshot_center_f64(&target); - self.app.try_touch_up(x, y) - } - - pub fn long_press(self, duration_ms: f64) -> Self { - if let Err(err) = self.try_long_press(duration_ms) { - panic_for_error(err); - } - self - } - - pub fn try_long_press(&self, duration_ms: f64) -> TestResult<()> { - let target = self.resolve_unique_visible()?; - let (x, y) = snapshot_center_f64(&target); - self.app.try_long_press(x, y, duration_ms) - } - - pub fn paste(self, text: impl AsRef) -> Self { - if let Err(err) = self.try_paste(text) { - panic_for_error(err); - } - self - } - - pub fn try_paste(&self, text: impl AsRef) -> TestResult<()> { - self.try_click()?; - self.app.try_paste_text(text) - } - - pub fn ime_composition(self, text: impl AsRef) -> Self { - if let Err(err) = self.try_ime_composition(text) { - panic_for_error(err); - } - self - } - - pub fn try_ime_composition(&self, text: impl AsRef) -> TestResult<()> { - self.try_click()?; - self.app.try_ime_composition(text) - } - pub fn snapshot(&self) -> WidgetSnapshot { match self.try_snapshot() { Ok(widget) => widget, @@ -1734,380 +1511,6 @@ fn env_duration_ms(name: &str) -> Duration { .unwrap_or(Duration::ZERO) } -// --------------------------------------------------------------------------- -// Android test helpers -// --------------------------------------------------------------------------- - -const DEFAULT_ANDROID_PORT: u16 = 8001; -const ANDROID_STARTUP_TIMEOUT: Duration = Duration::from_secs(120); -const ANDROID_LAUNCH_TIMEOUT: Duration = Duration::from_secs(60); - -fn android_test_enabled() -> bool { - env_truthy("MAKEPAD_TEST_ANDROID") -} - -fn android_device_serial() -> Option { - std::env::var("MAKEPAD_TEST_DEVICE") - .ok() - .map(|v| v.trim().to_string()) - .filter(|v| !v.is_empty()) -} - -fn android_hub_port() -> u16 { - std::env::var("MAKEPAD_TEST_ANDROID_PORT") - .ok() - .and_then(|v| v.trim().parse().ok()) - .unwrap_or(DEFAULT_ANDROID_PORT) -} - -fn android_adb_path() -> Option { - std::env::var("MAKEPAD_TEST_ADB") - .ok() - .map(|v| v.trim().to_string()) - .filter(|v| !v.is_empty()) -} - -fn adb_command(config: &TestConfig) -> std::process::Command { - let adb = config - .adb_path - .as_deref() - .unwrap_or("adb"); - let mut cmd = std::process::Command::new(adb); - if let Some(ref serial) = config.device_serial { - cmd.arg("-s").arg(serial); - } - cmd -} - -fn adb_exec(config: &TestConfig, args: &[&str]) -> TestResult { - let output = adb_command(config) - .args(args) - .output() - .map_err(|err| TestError::new(format!("failed to run adb: {err}")))?; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - return Err(TestError::new(format!( - "adb {} failed: {}", - args.join(" "), - stderr.trim() - ))); - } - Ok(String::from_utf8_lossy(&output.stdout).to_string()) -} - -fn android_full_package_name(package_name: &str) -> String { - let underscore = package_name.replace('-', "_"); - format!("dev.makepad.{underscore}") -} - -fn adb_forward(config: &TestConfig, port: u16) -> TestResult<()> { - let _ = adb_exec(config, &["reverse", "--remove", &format!("tcp:{port}")]); - adb_exec( - config, - &["reverse", &format!("tcp:{port}"), &format!("tcp:{port}")], - )?; - Ok(()) -} - -fn adb_install(config: &TestConfig, apk_path: &std::path::Path) -> TestResult<()> { - let output = adb_command(config) - .args(["install", "-r"]) - .arg(apk_path) - .output() - .map_err(|err| TestError::new(format!("failed to run adb install: {err}")))?; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - return Err(TestError::new(format!("adb install failed: {}", stderr.trim()))); - } - Ok(()) -} - -fn adb_launch( - config: &TestConfig, - package: &str, - build_id: u64, - crate_name: &str, - port: u16, -) -> TestResult<()> { - // Native-activity builds launch `android.app.NativeActivity` directly (no - // Java Activity). Legacy Java builds launch the generated `MakepadApp` - // subclass that bridges into `MakepadNative.activityOnCreate`. - let activity = if config.android_native_activity { - format!("{package}/android.app.NativeActivity") - } else { - format!("{package}/.MakepadApp") - }; - let studio_host = format!("127.0.0.1:{port}"); - let output = adb_command(config) - .args([ - "shell", "am", "start", - "-n", &activity, - "-e", "makepad.STUDIO_HOST", &studio_host, - "-e", "makepad.STUDIO_BUILD", &build_id.to_string(), - "-e", "makepad.STUDIO_CRATE", crate_name, - ]) - .output() - .map_err(|err| TestError::new(format!("failed to run adb shell am start: {err}")))?; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - return Err(TestError::new(format!("adb launch failed: {}", stderr.trim()))); - } - Ok(()) -} - -fn adb_force_stop(config: &TestConfig, package: &str) -> TestResult<()> { - let _ = adb_exec(config, &["shell", "am", "force-stop", package]); - // Also force-stop known interfering apps that share the Makepad runtime - // and may reclaim the foreground during our tests. - let interfering = ["rs.robius.robrix"]; - for pkg in &interfering { - let _ = adb_exec(config, &["shell", "am", "force-stop", pkg]); - } - Ok(()) -} - -fn adb_enable_package(config: &TestConfig, package: &str) -> TestResult<()> { - let _ = adb_exec(config, &["shell", "pm", "enable", package]); - Ok(()) -} - -fn adb_grant_permissions(config: &TestConfig, package: &str) -> TestResult<()> { - let perms = [ - "android.permission.READ_MEDIA_IMAGES", - "android.permission.READ_MEDIA_VIDEO", - "android.permission.READ_MEDIA_VISUAL_USER_SELECTED", - "android.permission.CAMERA", - "android.permission.RECORD_AUDIO", - "android.permission.READ_EXTERNAL_STORAGE", - "android.permission.BLUETOOTH_CONNECT", - "android.permission.ACCESS_FINE_LOCATION", - "android.permission.ACCESS_COARSE_LOCATION", - ]; - for perm in &perms { - let _ = adb_exec(config, &["shell", "pm", "grant", package, perm]); - } - Ok(()) -} - -fn resolve_workspace_root(manifest_dir: &std::path::Path) -> std::path::PathBuf { - if let Ok(env_root) = std::env::var("MAKEPAD_WORKSPACE_ROOT") { - let root = std::path::PathBuf::from(env_root); - if root.join("Cargo.toml").exists() { - return root; - } - } - let mut dir = manifest_dir.to_path_buf(); - loop { - let cargo_toml = dir.join("Cargo.toml"); - if cargo_toml.exists() { - if let Ok(content) = std::fs::read_to_string(&cargo_toml) { - if content.contains("tools/cargo_makepad") { - // skip Cargo.toml files that reference the tool as a path - } - let has_workspace = content.contains("[workspace]") - || content.contains("workspace.members") - || content.contains("workspace.package"); - if has_workspace && dir.join("tools/cargo_makepad").exists() { - return dir; - } - } - } - if !dir.pop() { - break; - } - } - manifest_dir.to_path_buf() -} - -fn build_android_apk(config: &TestConfig) -> TestResult { - let workspace_root = resolve_workspace_root(&config.manifest_dir); - let cargo_makepad = workspace_root - .join("target") - .join("release") - .join("cargo-makepad"); - if !cargo_makepad.exists() { - return Err(TestError::new(format!( - "cargo-makepad not found at {}. Run: cargo build --release -p cargo-makepad", - cargo_makepad.display() - ))); - } - let mut args = vec!["android"]; - if config.android_native_activity { - args.push("--native-activity"); - } - args.extend_from_slice(&["build", "-p", &config.package_name]); - let output = std::process::Command::new(&cargo_makepad) - .args(&args) - .current_dir(&workspace_root) - .output() - .map_err(|err| TestError::new(format!("failed to run cargo makepad: {err}")))?; - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - return Err(TestError::new(format!( - "android build failed:\nstdout: {}\nstderr: {}", - stdout.trim(), - stderr.trim() - ))); - } - let apk_dir_name = config.package_name.replace('-', "_"); - let apk_dir = workspace_root - .join("target") - .join("android") - .join("makepad-android-apk") - .join(&apk_dir_name) - .join("apk"); - let apk_name = format!("{}.apk", &apk_dir_name); - let apk_path = apk_dir.join(&apk_name); - if !apk_path.exists() { - return Err(TestError::new(format!( - "APK not found at {}", - apk_path.display() - ))); - } - Ok(apk_path) -} - -fn wait_for_android_app_started( - connection: &TestConnection, - build_id: QueryId, - timeout: Duration, -) -> TestResult<()> { - let deadline = Instant::now() + timeout; - loop { - if Instant::now() >= deadline { - return Err(TestError::new( - "timed out waiting for Android app to connect to hub", - )); - } - let slice = cmp::min( - POLL_INTERVAL, - deadline.saturating_duration_since(Instant::now()), - ); - let Some(msg) = connection.recv_timeout(slice) else { - continue; - }; - match msg { - HubToClient::AppStarted { - build_id: msg_build_id, - } if msg_build_id == build_id => { - return Ok(()); - } - HubToClient::Error { message } => return Err(TestError::new(message)), - _ => {} - } - } -} - -fn wait_for_android_app_responsive( - connection: &mut TestConnection, - build_id: QueryId, - timeout: Duration, -) -> TestResult<()> { - let deadline = Instant::now() + timeout; - while Instant::now() < deadline { - let query_id = connection.send(ClientToHub::WidgetTreeDump { build_id })?; - let attempt_deadline = Instant::now() + ACTION_TIMEOUT; - let mut replied = false; - while Instant::now() < attempt_deadline { - if Instant::now() >= deadline { - break; - } - let slice = cmp::min( - POLL_INTERVAL, - attempt_deadline.saturating_duration_since(Instant::now()), - ); - let Some(msg) = connection.recv_timeout(slice) else { - continue; - }; - if let HubToClient::WidgetTreeDump { - query_id: id, dump: _, .. - } = &msg - { - if *id == query_id { - replied = true; - break; - } - } - } - if replied { - return Ok(()); - } - thread::sleep(POLL_INTERVAL); - } - Err(TestError::new( - "timed out waiting for Android app to become responsive", - )) -} - -fn start_android_app(config: &TestConfig) -> TestResult<(TestConnection, QueryId)> { - let hub_port = config.android_port; - let listen_address = SocketAddr::from((Ipv4Addr::LOCALHOST, hub_port)); - let mut connection = TestConnection::InProcess( - StudioHub::start_in_process(HubConfig { - listen_address, - mounts: vec![MountConfig { - name: config.mount_name.clone(), - path: config.manifest_dir.clone(), - }], - enable_in_process_gateway: true, - ..Default::default() - }) - .map_err(TestError::new)?, - ); - - let build_id = QueryId(1); - let full_package = android_full_package_name(&config.package_name); - // The hub may have bound a fallback port (e.g. when a real Studio already - // owns `android_port`). Route adb and the app at the port actually bound, - // never a hardcoded one, or the app would dial a dead listener. - let hub_port = connection - .studio_addr() - .and_then(|addr| addr.rsplit_once(':').map(|(_, p)| p.to_string())) - .and_then(|p| p.parse::().ok()) - .unwrap_or(config.android_port); - - eprintln!("[makepad-test] Android: forwarding ADB port {hub_port}"); - adb_forward(config, hub_port)?; - - eprintln!("[makepad-test] Android: building APK for {}", config.package_name); - let apk_path = build_android_apk(config)?; - - eprintln!("[makepad-test] Android: installing APK"); - adb_install(config, &apk_path)?; - - eprintln!("[makepad-test] Android: granting runtime permissions"); - adb_grant_permissions(config, &full_package)?; - - eprintln!("[makepad-test] Android: force-stopping previous instance"); - let _ = adb_force_stop(config, &full_package); - thread::sleep(Duration::from_secs(1)); - - eprintln!("[makepad-test] Android: launching app"); - adb_launch(config, &full_package, build_id.0, &config.package_name, hub_port)?; - - thread::sleep(Duration::from_secs(2)); - - eprintln!("[makepad-test] Android: waiting for app to connect to hub"); - wait_for_android_app_started(&connection, build_id, ANDROID_STARTUP_TIMEOUT)?; - - eprintln!("[makepad-test] Android: app connected"); - - // The websocket connects on a background thread before the app's event - // loop is up, so a cold start can answer the handshake well before it can - // service hub requests. Legacy Java starts are the slowest: the first - // frame (and with it the main loop that drains requests) only comes after - // the SurfaceView surface materializes, which on a first launch after - // install can exceed the per-request action timeout. Settle until the app - // actually answers a request so the test's first query does not lose that - // boot race. - eprintln!("[makepad-test] Android: waiting for app to become responsive"); - wait_for_android_app_responsive(&mut connection, build_id, ANDROID_STARTUP_TIMEOUT)?; - - eprintln!("[makepad-test] Android: app responsive"); - Ok((connection, build_id)) -} - fn now_seconds() -> f64 { SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/platform/network/src/http_server.rs b/platform/network/src/http_server.rs index 188f7790f..0af6e2cb1 100644 --- a/platform/network/src/http_server.rs +++ b/platform/network/src/http_server.rs @@ -59,50 +59,24 @@ pub enum HttpServerRequest { }, } -/// Handle to a running HTTP server. Sending on `shutdown` (or dropping the -/// sender) makes the accept loop exit so the listen port is released and the -/// thread can be joined. -pub struct HttpServerHandle { - pub thread: std::thread::JoinHandle<()>, - pub shutdown: mpsc::Sender<()>, -} - -pub fn start_http_server(http_server: HttpServer) -> Option { +pub fn start_http_server(http_server: HttpServer) -> Option> { let listener = if let Ok(listener) = TcpListener::bind(http_server.listen_address) { listener } else { println!("Cannot bind http server port"); return None; }; - if listener.set_nonblocking(true).is_err() { - println!("Cannot set http server non-blocking"); - return None; - } - let (shutdown_tx, shutdown_rx) = mpsc::channel::<()>(); let listen_thread = { std::thread::spawn(move || { let mut connection_counter = 0u64; - loop { - if shutdown_rx.try_recv().is_ok() { - break; - } - let mut tcp_stream = match listener.accept() { - Ok((tcp_stream, _)) => tcp_stream, - Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => { - // No pending connection; poll the shutdown channel - // periodically so a drop releases the port promptly. - std::thread::sleep(std::time::Duration::from_millis(5)); - continue; - } - Err(_) => { - println!("Incoming stream failure"); - continue; - } - }; - if tcp_stream.set_nonblocking(false).is_err() { + for tcp_stream in listener.incoming() { + let mut tcp_stream = if let Ok(tcp_stream) = tcp_stream { + tcp_stream + } else { + println!("Incoming stream failure"); continue; - } + }; let http_server = http_server.clone(); connection_counter += 1; let _read_thread = std::thread::spawn(move || { @@ -134,10 +108,7 @@ pub fn start_http_server(http_server: HttpServer) -> Option { } }) }; - Some(HttpServerHandle { - thread: listen_thread, - shutdown: shutdown_tx, - }) + Some(listen_thread) } fn handle_post( diff --git a/platform/network/src/lib.rs b/platform/network/src/lib.rs index 1e7779db9..bb348010a 100644 --- a/platform/network/src/lib.rs +++ b/platform/network/src/lib.rs @@ -11,7 +11,7 @@ pub mod web_socket_parser; pub use crate::backend::{EventSink, NetworkBackend, UnsupportedBackend}; pub use crate::http_server::{ - start_http_server, HttpServer, HttpServerHandle, HttpServerRequest, HttpServerResponse, + start_http_server, HttpServer, HttpServerRequest, HttpServerResponse, }; pub use crate::runtime::{NetworkConfig, NetworkRuntime}; pub use crate::socket_stream::SocketStream; diff --git a/platform/network/src/runtime.rs b/platform/network/src/runtime.rs index 1dbbd08e0..97858ab59 100644 --- a/platform/network/src/runtime.rs +++ b/platform/network/src/runtime.rs @@ -5,7 +5,7 @@ use std::time::Duration; use makepad_live_id::LiveId; use crate::backend::{default_backend, EventSink, NetworkBackend}; -use crate::http_server::{HttpServer, HttpServerHandle}; +use crate::http_server::HttpServer; use crate::types::{HttpRequest, NetworkError, NetworkResponse, WsSend}; #[derive(Default)] @@ -62,7 +62,7 @@ impl NetworkRuntime { pub fn start_http_server( &self, http_server: HttpServer, - ) -> Option { + ) -> Option> { crate::http_server::start_http_server(http_server) } diff --git a/platform/src/os/cx_shared.rs b/platform/src/os/cx_shared.rs index 51f1072bd..14208cf6c 100644 --- a/platform/src/os/cx_shared.rs +++ b/platform/src/os/cx_shared.rs @@ -604,84 +604,6 @@ impl Cx { StudioToApp::Custom(data) => { self.call_event_handler(&Event::Custom(data)); } - StudioToApp::TouchUpdate(remote_touch) => { - let touches: Vec = remote_touch - .touches - .iter() - .map(|rt| crate::event::finger::TouchPoint { - state: match rt.state { - makepad_studio_protocol::RemoteTouchState::Start => { - crate::event::finger::TouchState::Start - } - makepad_studio_protocol::RemoteTouchState::Stop => { - crate::event::finger::TouchState::Stop - } - makepad_studio_protocol::RemoteTouchState::Move => { - crate::event::finger::TouchState::Move - } - makepad_studio_protocol::RemoteTouchState::Stable => { - crate::event::finger::TouchState::Stable - } - }, - abs: crate::makepad_math::dvec2(rt.abs_x - pos.x, rt.abs_y - pos.y), - time: rt.time, - uid: rt.uid, - rotation_angle: rt.rotation_angle, - force: rt.force, - radius: crate::makepad_math::dvec2(rt.radius_x, rt.radius_y), - handled: Cell::new(Area::Empty), - sweep_lock: Cell::new(Area::Empty), - }) - .collect(); - self.fingers.process_touch_update_start(remote_touch.time, &touches); - self.call_event_handler(&Event::TouchUpdate( - crate::event::finger::TouchUpdateEvent { - time: remote_touch.time, - window_id, - modifiers: crate::event::KeyModifiers::default(), - touches, - }, - )); - } - StudioToApp::LongPress(remote_long) => { - let abs = crate::makepad_math::dvec2(remote_long.x - pos.x, remote_long.y - pos.y); - self.fingers.process_tap_count(abs, remote_long.time); - self.fingers.mouse_down(crate::event::MouseButton::PRIMARY, window_id); - self.call_event_handler(&Event::MouseDown(crate::event::MouseDownEvent { - abs, - button: crate::event::MouseButton::PRIMARY, - window_id, - modifiers: crate::event::KeyModifiers::default(), - time: remote_long.time, - handled: Cell::new(Area::Empty), - })); - self.call_event_handler(&Event::MouseUp(crate::event::MouseUpEvent { - abs, - button: crate::event::MouseButton::PRIMARY, - window_id, - modifiers: crate::event::KeyModifiers::default(), - time: remote_long.time + remote_long.duration_ms * 0.001, - })); - self.fingers.mouse_up(crate::event::MouseButton::PRIMARY); - self.fingers.cycle_hover_area(live_id!(mouse).into()); - self.send_studio_key_focus_rect_response(); - } - StudioToApp::TextPaste(remote_paste) => { - self.call_event_handler(&Event::TextInput(crate::event::TextInputEvent { - input: remote_paste.text, - replace_last: false, - was_paste: true, - ..Default::default() - })); - } - StudioToApp::IMEComposition(remote_ime) => { - self.call_event_handler(&Event::TextInput(crate::event::TextInputEvent { - input: remote_ime.text, - replace_last: true, - was_paste: false, - ..Default::default() - })); - } StudioToApp::KeepAlive | StudioToApp::None => {} StudioToApp::LiveChange { file_name, content } => { self.script_data diff --git a/platform/src/os/linux/android/android.rs b/platform/src/os/linux/android/android.rs index 0a07dbffe..4171b68c7 100644 --- a/platform/src/os/linux/android/android.rs +++ b/platform/src/os/linux/android/android.rs @@ -299,9 +299,7 @@ impl Cx { self.display_context.screen_size = self.os.display_size / dpi_factor; self.display_context.safe_area_insets = insets; self.update_safe_inset_script_values(insets); - Self::send_studio_message(AppToStudio::BeforeStartup); self.call_event_handler(&Event::Startup); - Self::send_studio_message(AppToStudio::AfterStartup); self.redraw_all(); self.start_network_live_file_watcher(); diff --git a/platform/src/os/linux/android/android_jni.rs b/platform/src/os/linux/android/android_jni.rs index dd5afb70a..c5a2b82e8 100644 --- a/platform/src/os/linux/android/android_jni.rs +++ b/platform/src/os/linux/android/android_jni.rs @@ -331,7 +331,6 @@ unsafe fn get_intent_string_extra( const MAKEPAD_PREFS_NAME: &str = "makepad"; const MAKEPAD_STUDIO_HOST_PREF_KEY: &str = "studio_host"; const MAKEPAD_STUDIO_CRATE_PREF_KEY: &str = "studio_crate"; -const MAKEPAD_STUDIO_BUILD_PREF_KEY: &str = "studio_build"; const ANDROID_MODE_PRIVATE: i32 = 0; unsafe fn new_jstring(env: *mut jni_sys::JNIEnv, value: &str) -> Option { @@ -454,8 +453,6 @@ pub unsafe fn apply_studio_env_from_activity(activity: *const std::ffi::c_void) .filter(|v| !v.trim().is_empty()); let intent_studio_crate = get_intent_string_extra(env, activity, "makepad.STUDIO_CRATE") .filter(|v| !v.trim().is_empty()); - let intent_studio_build = get_intent_string_extra(env, activity, "makepad.STUDIO_BUILD") - .filter(|v| !v.trim().is_empty()); if let Some(studio_host) = intent_studio_host { let _ = persist_string_pref(env, activity, MAKEPAD_STUDIO_HOST_PREF_KEY, &studio_host); @@ -474,15 +471,6 @@ pub unsafe fn apply_studio_env_from_activity(activity: *const std::ffi::c_void) { std::env::set_var("STUDIO_CRATE", &studio_crate); } - - if let Some(studio_build) = intent_studio_build { - let _ = persist_string_pref(env, activity, MAKEPAD_STUDIO_BUILD_PREF_KEY, &studio_build); - std::env::set_var("STUDIO_BUILD", &studio_build); - } else if let Some(studio_build) = - get_persisted_string_pref(env, activity, MAKEPAD_STUDIO_BUILD_PREF_KEY) - { - std::env::set_var("STUDIO_BUILD", &studio_build); - } } pub unsafe fn attach_jni_env() -> *mut jni_sys::JNIEnv { diff --git a/platform/studio/src/studio.rs b/platform/studio/src/studio.rs index 7bfeeabce..f83edcec0 100644 --- a/platform/studio/src/studio.rs +++ b/platform/studio/src/studio.rs @@ -240,52 +240,6 @@ pub struct RemoteScroll { pub modifiers: RemoteKeyModifiers, } -#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)] -pub enum RemoteTouchState { - Start, - Stop, - Move, - #[default] - Stable, -} - -#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)] -pub struct RemoteTouchPoint { - pub state: RemoteTouchState, - pub abs_x: f64, - pub abs_y: f64, - pub time: f64, - pub uid: u64, - pub rotation_angle: f64, - pub force: f64, - pub radius_x: f64, - pub radius_y: f64, -} - -#[derive(Clone, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)] -pub struct RemoteTouchUpdate { - pub time: f64, - pub touches: Vec, -} - -#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)] -pub struct RemoteLongPress { - pub x: f64, - pub y: f64, - pub time: f64, - pub duration_ms: f64, -} - -#[derive(Clone, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)] -pub struct RemoteTextPaste { - pub text: String, -} - -#[derive(Clone, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)] -pub struct RemoteIMEComposition { - pub text: String, -} - #[derive(SerBin, DeBin, SerJson, DeJson, Debug, Clone)] pub enum AppToStudio { LogItem(StudioLogItem), @@ -465,10 +419,6 @@ pub enum StudioToApp { /// changes. Level state rather than edges, because that is what the OS /// APIs report and what `Cx::game_input_states` hands back. GameInput(Vec), - TouchUpdate(RemoteTouchUpdate), - LongPress(RemoteLongPress), - TextPaste(RemoteTextPaste), - IMEComposition(RemoteIMEComposition), /// Application-defined event. Delivered to the app as `Event::Custom`. Custom(String), #[default] diff --git a/studio/hub/Cargo.toml b/studio/hub/Cargo.toml index 620789b69..900af0548 100644 --- a/studio/hub/Cargo.toml +++ b/studio/hub/Cargo.toml @@ -5,10 +5,6 @@ edition = "2021" description = "Studio2 hub (protocol + gateway + virtual fs)" license = "MIT OR Apache-2.0" -[[bin]] -name = "hub-server" -path = "src/bin/hub_server.rs" - [dependencies] makepad-script-std = { path = "../../platform/script/std", version = "1.0.0" } makepad-studio-protocol = { path = "../../platform/studio", version = "0.1.0" } diff --git a/studio/hub/src/bin/hub_server.rs b/studio/hub/src/bin/hub_server.rs deleted file mode 100644 index f222d918b..000000000 --- a/studio/hub/src/bin/hub_server.rs +++ /dev/null @@ -1,38 +0,0 @@ -use makepad_studio_hub::{HubConfig, MountConfig, StudioHub}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::path::PathBuf; - -fn main() { - let port: u16 = std::env::args() - .nth(1) - .and_then(|s| s.parse().ok()) - .unwrap_or(8001); - - let listen_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port); - - let mount_path = std::env::args() - .nth(2) - .map(PathBuf::from) - .unwrap_or_else(|| std::env::current_dir().unwrap()); - - println!("[hub-server] Listening on {}", listen_address); - println!("[hub-server] Mount path: {}", mount_path.display()); - - let _handle = StudioHub::start_headless(HubConfig { - listen_address, - mounts: vec![MountConfig { - name: "makepad".into(), - path: mount_path, - }], - enable_in_process_gateway: true, - ..Default::default() - }) - .expect("Failed to start hub"); - - println!("[hub-server] Hub started successfully on {}", listen_address); - println!("[hub-server] Waiting for connections..."); - - loop { - std::thread::sleep(std::time::Duration::from_secs(3600)); - } -} diff --git a/studio/hub/src/gateway.rs b/studio/hub/src/gateway.rs index 1e2cc010d..b0cc131ce 100644 --- a/studio/hub/src/gateway.rs +++ b/studio/hub/src/gateway.rs @@ -1,8 +1,7 @@ use crate::dispatch::HubEvent; use makepad_micro_serde::SerBin; use makepad_script_std::makepad_network::{ - start_http_server, HttpServer, HttpServerHandle, HttpServerRequest, HttpServerResponse, - ToUISender, + start_http_server, HttpServer, HttpServerRequest, HttpServerResponse, ToUISender, }; use makepad_studio_protocol::hub_protocol::{HubToClient, QueryId}; use std::collections::HashMap; @@ -20,21 +19,8 @@ enum SocketRole { pub struct GatewayHandle { pub listen_address: SocketAddr, - pub request_thread: Option>, - pub http_thread: Option>, - http_shutdown: Sender<()>, -} - -impl Drop for GatewayHandle { - fn drop(&mut self) { - // Stop the accept loop so the listen port is released. Dropping the - // request channel senders then makes the request thread exit, which - // drops its hub event sender and lets the hub core shut down too. - let _ = self.http_shutdown.send(()); - if let Some(http_thread) = self.http_thread.take() { - let _ = http_thread.join(); - } - } + pub request_thread: JoinHandle<()>, + pub http_thread: JoinHandle<()>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -53,10 +39,7 @@ pub fn start_http_gateway( event_tx: Sender, ) -> Result { let (request_tx, request_rx) = mpsc::channel::(); - let HttpServerHandle { - thread: http_thread, - shutdown: http_shutdown, - } = start_http_server(HttpServer { + let http_thread = start_http_server(HttpServer { listen_address, request: request_tx, post_max_size, @@ -235,9 +218,8 @@ pub fn start_http_gateway( Ok(GatewayHandle { listen_address, - request_thread: Some(request_thread), - http_thread: Some(http_thread), - http_shutdown, + request_thread, + http_thread, }) } diff --git a/tools/web_server/src/main.rs b/tools/web_server/src/main.rs index 6250e1565..80c866150 100644 --- a/tools/web_server/src/main.rs +++ b/tools/web_server/src/main.rs @@ -24,7 +24,7 @@ fn main() { } let root_path = args[1].clone(); - let _http_server = net.start_http_server(HttpServer{ + net.start_http_server(HttpServer{ listen_address:addr, post_max_size: 1024*1024, request: tx_request diff --git a/widgets/Cargo.toml b/widgets/Cargo.toml index 0cbd97070..611eb906e 100644 --- a/widgets/Cargo.toml +++ b/widgets/Cargo.toml @@ -18,11 +18,6 @@ i_overlay = { path = "../libs/i_overlay", version = "7.0.3", optional = true, de makepad-fast-inflate = { path = "../libs/fast_inflate", optional = true } makepad-voice = { path = "../libs/voice", version = "0.1.0", optional = true } makepad-cef = { path = "../libs/cef", optional = true } -# Public optional sibling crates. These are re-exported from lib.rs so an -# application can depend on makepad-widgets as its sole Makepad source. -makepad-gltf = { path = "../libs/gltf", optional = true } -makepad-csg = { path = "../libs/csg/csg", optional = true } -makepad-test = { path = "../libs/makepad_test", optional = true } makepad-html = { path = "../libs/html", version = "1.0.0" } unicode-segmentation = { version = "1.12.0", path = "../libs/unicode/unicode-segmentation" } @@ -33,16 +28,32 @@ ttf-parser = { path = "../libs/ttf-parser" } serde = { version = "1.0", optional = true, features = ["derive"] } +# Public optional sibling crates, re-exported from lib.rs so an application +# can depend on makepad-widgets as its sole Makepad source. These are +# fork-local additions; an upstream sync dropped them at abd70f4, which +# removed the `test` feature and broke every crate using makepad_test. +[dependencies.makepad-gltf] +path = "../libs/gltf" +optional = true + +[dependencies.makepad-csg] +path = "../libs/csg/csg" +optional = true + +[dependencies.makepad-test] +path = "../libs/makepad_test" +optional = true + [features] default = [] voice = ["dep:makepad-voice"] maps = ["dep:makepad-mbtile-reader", "dep:makepad-fast-inflate", "dep:i_overlay"] -gltf = ["dep:makepad-gltf"] -csg = ["dep:makepad-csg"] -test = ["dep:makepad-test"] pdf = ["dep:makepad-pdf-parse"] cef = ["dep:makepad-cef"] ## Enables certain public-facing types to derive serde serialization traits. serde = ["dep:serde", "makepad-draw/serde", "makepad-derive-widget/serde"] +gltf = ["dep:makepad-gltf"] +csg = ["dep:makepad-csg"] +test = ["dep:makepad-test"] diff --git a/widgets/src/lib.rs b/widgets/src/lib.rs index 743954573..1cc294377 100644 --- a/widgets/src/lib.rs +++ b/widgets/src/lib.rs @@ -9,25 +9,22 @@ pub use makepad_script::script_eval; pub use makepad_script::{ScriptValue, ScriptVm}; pub use makepad_html; -#[cfg(feature = "pdf")] -pub use makepad_pdf_parse; -pub use makepad_draw::makepad_zune_jpeg; -pub use makepad_draw::makepad_zune_png; - -// Optional sibling Makepad workspace crates. These re-exports permit a -// downstream application to depend on makepad-widgets as the single Makepad -// source while keeping all extra APIs feature-gated. -#[cfg(feature = "maps")] -pub use makepad_fast_inflate; -#[cfg(feature = "maps")] -pub use makepad_mbtile_reader; +// Fork-local re-exports of the optional sibling crates, so an application +// can depend on makepad-widgets alone. Lost in the upstream sync at +// abd70f4; `makepad_test` in particular is imported as +// `makepad_widgets::makepad_test` by every UI test suite in nigig-org. #[cfg(feature = "gltf")] pub use makepad_gltf; #[cfg(feature = "csg")] pub use makepad_csg; #[cfg(feature = "test")] pub use makepad_test; +#[cfg(feature = "pdf")] +pub use makepad_pdf_parse; + +pub use makepad_draw::makepad_zune_jpeg; +pub use makepad_draw::makepad_zune_png; // Core modules (used internally first) pub mod animator;