More text improvements, performance enhancements, input handling, text selection, wl cursors
This commit is contained in:
@@ -14,11 +14,12 @@ use tracing::{debug, info};
|
||||
use crate::interaction::PointerEvent;
|
||||
use crate::scene::{SceneSnapshot, UiSize};
|
||||
use crate::trace_targets;
|
||||
use crate::tree::CursorIcon;
|
||||
use crate::window::{WindowConfigured, WindowId, WindowLifecycle, WindowSpec, WindowUpdate};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlatformProxy {
|
||||
command_tx: mpsc::UnboundedSender<PlatformCommand>,
|
||||
command_tx: mpsc::UnboundedSender<PlatformRequest>,
|
||||
next_window_id: Arc<AtomicU64>,
|
||||
}
|
||||
|
||||
@@ -33,6 +34,11 @@ pub struct PlatformRuntime {
|
||||
_worker: WorkerHandle,
|
||||
}
|
||||
|
||||
pub struct PlatformEndpoint {
|
||||
pub commands: mpsc::Receiver<PlatformRequest>,
|
||||
pub events: mpsc::UnboundedSender<PlatformEvent>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PlatformEvent {
|
||||
Opened {
|
||||
@@ -75,7 +81,7 @@ impl fmt::Display for PlatformClosed {
|
||||
impl std::error::Error for PlatformClosed {}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum PlatformCommand {
|
||||
pub enum PlatformRequest {
|
||||
CreateWindow {
|
||||
window_id: WindowId,
|
||||
spec: WindowSpec,
|
||||
@@ -88,9 +94,21 @@ enum PlatformCommand {
|
||||
window_id: WindowId,
|
||||
scene: SceneSnapshot,
|
||||
},
|
||||
SetPrimarySelectionText {
|
||||
window_id: WindowId,
|
||||
text: String,
|
||||
},
|
||||
SetCursorIcon {
|
||||
window_id: WindowId,
|
||||
cursor: CursorIcon,
|
||||
},
|
||||
EmitCloseRequested {
|
||||
window_id: WindowId,
|
||||
},
|
||||
EmitPointerEvent {
|
||||
window_id: WindowId,
|
||||
event: PointerEvent,
|
||||
},
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
@@ -135,6 +153,25 @@ impl PlatformRuntime {
|
||||
start_headless()
|
||||
}
|
||||
|
||||
pub fn custom(start: impl FnOnce(PlatformEndpoint) -> WorkerHandle) -> Self {
|
||||
let (command_tx, command_rx) = mpsc::unbounded_channel::<PlatformRequest>();
|
||||
let (event_tx, event_rx) = mpsc::unbounded_channel::<PlatformEvent>();
|
||||
let proxy = PlatformProxy {
|
||||
command_tx,
|
||||
next_window_id: Arc::new(AtomicU64::new(1)),
|
||||
};
|
||||
let worker = start(PlatformEndpoint {
|
||||
commands: command_rx,
|
||||
events: event_tx,
|
||||
});
|
||||
|
||||
Self {
|
||||
proxy,
|
||||
events: event_rx,
|
||||
_worker: worker,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proxy(&self) -> PlatformProxy {
|
||||
self.proxy.clone()
|
||||
}
|
||||
@@ -142,12 +179,20 @@ impl PlatformRuntime {
|
||||
pub async fn next_event(&mut self) -> Option<PlatformEvent> {
|
||||
self.events.recv().await
|
||||
}
|
||||
|
||||
pub fn take_pending_events(&mut self) -> Vec<PlatformEvent> {
|
||||
let mut events = Vec::new();
|
||||
while let Ok(event) = self.events.try_recv() {
|
||||
events.push(event);
|
||||
}
|
||||
events
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformProxy {
|
||||
pub fn create_window(&self, spec: WindowSpec) -> Result<WindowId, PlatformClosed> {
|
||||
let window_id = WindowId::from_raw(self.next_window_id.fetch_add(1, Ordering::Relaxed));
|
||||
self.send(PlatformCommand::CreateWindow { window_id, spec })?;
|
||||
self.send(PlatformRequest::CreateWindow { window_id, spec })?;
|
||||
Ok(window_id)
|
||||
}
|
||||
|
||||
@@ -156,7 +201,7 @@ impl PlatformProxy {
|
||||
window_id: WindowId,
|
||||
update: WindowUpdate,
|
||||
) -> Result<(), PlatformClosed> {
|
||||
self.send(PlatformCommand::UpdateWindow { window_id, update })
|
||||
self.send(PlatformRequest::UpdateWindow { window_id, update })
|
||||
}
|
||||
|
||||
pub fn replace_scene(
|
||||
@@ -164,18 +209,45 @@ impl PlatformProxy {
|
||||
window_id: WindowId,
|
||||
scene: SceneSnapshot,
|
||||
) -> Result<(), PlatformClosed> {
|
||||
self.send(PlatformCommand::ReplaceScene { window_id, scene })
|
||||
self.send(PlatformRequest::ReplaceScene { window_id, scene })
|
||||
}
|
||||
|
||||
pub fn set_primary_selection_text(
|
||||
&self,
|
||||
window_id: WindowId,
|
||||
text: impl Into<String>,
|
||||
) -> Result<(), PlatformClosed> {
|
||||
self.send(PlatformRequest::SetPrimarySelectionText {
|
||||
window_id,
|
||||
text: text.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_cursor_icon(
|
||||
&self,
|
||||
window_id: WindowId,
|
||||
cursor: CursorIcon,
|
||||
) -> Result<(), PlatformClosed> {
|
||||
self.send(PlatformRequest::SetCursorIcon { window_id, cursor })
|
||||
}
|
||||
|
||||
pub fn emit_close_requested(&self, window_id: WindowId) -> Result<(), PlatformClosed> {
|
||||
self.send(PlatformCommand::EmitCloseRequested { window_id })
|
||||
self.send(PlatformRequest::EmitCloseRequested { window_id })
|
||||
}
|
||||
|
||||
pub fn emit_pointer_event(
|
||||
&self,
|
||||
window_id: WindowId,
|
||||
event: PointerEvent,
|
||||
) -> Result<(), PlatformClosed> {
|
||||
self.send(PlatformRequest::EmitPointerEvent { window_id, event })
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) -> Result<(), PlatformClosed> {
|
||||
self.send(PlatformCommand::Shutdown)
|
||||
self.send(PlatformRequest::Shutdown)
|
||||
}
|
||||
|
||||
fn send(&self, command: PlatformCommand) -> Result<(), PlatformClosed> {
|
||||
fn send(&self, command: PlatformRequest) -> Result<(), PlatformClosed> {
|
||||
self.command_tx.send(command).map_err(|_| PlatformClosed)
|
||||
}
|
||||
}
|
||||
@@ -186,69 +258,64 @@ impl PlatformProxy {
|
||||
/// scene for that window even if the scene version itself did not change. Visibility restoration is
|
||||
/// treated as a presentation-affecting state change.
|
||||
pub fn start_headless() -> PlatformRuntime {
|
||||
let (command_tx, mut command_rx) = mpsc::unbounded_channel::<PlatformCommand>();
|
||||
let (event_tx, event_rx) = mpsc::unbounded_channel::<PlatformEvent>();
|
||||
PlatformRuntime::custom(|mut endpoint| {
|
||||
spawn_worker(
|
||||
move || {
|
||||
let state = Rc::new(RefCell::new(HeadlessState {
|
||||
events: endpoint.events.clone(),
|
||||
windows: BTreeMap::new(),
|
||||
}));
|
||||
|
||||
let worker = spawn_worker(
|
||||
move || {
|
||||
let state = Rc::new(RefCell::new(HeadlessState {
|
||||
events: event_tx.clone(),
|
||||
windows: BTreeMap::new(),
|
||||
}));
|
||||
queue_future(async move {
|
||||
debug!(
|
||||
target: trace_targets::PLATFORM,
|
||||
event = "worker_started",
|
||||
backend = "headless",
|
||||
"starting headless platform worker"
|
||||
);
|
||||
|
||||
queue_future(async move {
|
||||
debug!(
|
||||
target: trace_targets::PLATFORM,
|
||||
event = "worker_started",
|
||||
backend = "headless",
|
||||
"starting headless platform worker"
|
||||
);
|
||||
|
||||
while let Some(command) = command_rx.recv().await {
|
||||
match command {
|
||||
PlatformCommand::CreateWindow { window_id, spec } => {
|
||||
handle_create_window(&state, window_id, spec);
|
||||
}
|
||||
PlatformCommand::UpdateWindow { window_id, update } => {
|
||||
handle_update_window(&state, window_id, update);
|
||||
}
|
||||
PlatformCommand::ReplaceScene { window_id, scene } => {
|
||||
handle_replace_scene(&state, window_id, scene);
|
||||
}
|
||||
PlatformCommand::EmitCloseRequested { window_id } => {
|
||||
let sender = state.borrow().events.clone();
|
||||
let _ = sender.send(PlatformEvent::CloseRequested { window_id });
|
||||
}
|
||||
PlatformCommand::Shutdown => {
|
||||
debug!(
|
||||
target: trace_targets::PLATFORM,
|
||||
event = "worker_shutdown_requested",
|
||||
"shutting down headless platform worker"
|
||||
);
|
||||
break;
|
||||
while let Some(command) = endpoint.commands.recv().await {
|
||||
match command {
|
||||
PlatformRequest::CreateWindow { window_id, spec } => {
|
||||
handle_create_window(&state, window_id, spec);
|
||||
}
|
||||
PlatformRequest::UpdateWindow { window_id, update } => {
|
||||
handle_update_window(&state, window_id, update);
|
||||
}
|
||||
PlatformRequest::ReplaceScene { window_id, scene } => {
|
||||
handle_replace_scene(&state, window_id, scene);
|
||||
}
|
||||
PlatformRequest::SetPrimarySelectionText { .. } => {}
|
||||
PlatformRequest::SetCursorIcon { .. } => {}
|
||||
PlatformRequest::EmitCloseRequested { window_id } => {
|
||||
let sender = state.borrow().events.clone();
|
||||
let _ = sender.send(PlatformEvent::CloseRequested { window_id });
|
||||
}
|
||||
PlatformRequest::EmitPointerEvent { window_id, event } => {
|
||||
handle_emit_pointer_event(&state, window_id, event);
|
||||
}
|
||||
PlatformRequest::Shutdown => {
|
||||
debug!(
|
||||
target: trace_targets::PLATFORM,
|
||||
event = "worker_shutdown_requested",
|
||||
"shutting down headless platform worker"
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|| {
|
||||
debug!(
|
||||
target: trace_targets::PLATFORM,
|
||||
event = "worker_exited",
|
||||
backend = "headless",
|
||||
"headless platform worker exited"
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
PlatformRuntime {
|
||||
proxy: PlatformProxy {
|
||||
command_tx,
|
||||
next_window_id: Arc::new(AtomicU64::new(1)),
|
||||
},
|
||||
events: event_rx,
|
||||
_worker: worker,
|
||||
}
|
||||
});
|
||||
},
|
||||
|| {
|
||||
debug!(
|
||||
target: trace_targets::PLATFORM,
|
||||
event = "worker_exited",
|
||||
backend = "headless",
|
||||
"headless platform worker exited"
|
||||
);
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_create_window(state: &Rc<RefCell<HeadlessState>>, window_id: WindowId, spec: WindowSpec) {
|
||||
@@ -450,6 +517,20 @@ fn set_visibility(state: &Rc<RefCell<HeadlessState>>, window_id: WindowId, visib
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_emit_pointer_event(
|
||||
state: &Rc<RefCell<HeadlessState>>,
|
||||
window_id: WindowId,
|
||||
event: PointerEvent,
|
||||
) {
|
||||
let should_emit = matches!(
|
||||
state.borrow().windows.get(&window_id),
|
||||
Some(window) if window.lifecycle == WindowLifecycle::OpenVisible
|
||||
);
|
||||
if should_emit {
|
||||
emit_event(state, PlatformEvent::Pointer { window_id, event });
|
||||
}
|
||||
}
|
||||
|
||||
fn present_latest_scene(state: &Rc<RefCell<HeadlessState>>, window_id: WindowId) {
|
||||
let presentation = {
|
||||
let mut state_ref = state.borrow_mut();
|
||||
|
||||
Reference in New Issue
Block a user