summaryrefslogtreecommitdiffstats
path: root/rsnix/src/cursor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src/cursor.rs')
-rw-r--r--rsnix/src/cursor.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/rsnix/src/cursor.rs b/rsnix/src/cursor.rs
new file mode 100644
index 0000000..18c9a1d
--- /dev/null
+++ b/rsnix/src/cursor.rs
@@ -0,0 +1,71 @@
1// ---------------------------------------------------------------------------
2// cursor — the mouse cursor task.
3//
4// One background task owns the PS/2 mouse: it polls the packet ring every
5// 10 ms, moves the on-screen cursor (gop's display overlay), tracks the
6// button state, and optionally *forwards* every event to another task's
7// message queue (`mouse <id>` sets the target) — the input-routing half
8// of the future window manager.
9//
10// The cursor itself never touches the text surface: `gop::push_rect`
11// composites it on top of every display write, so text scrolling and
12// cursor movement cannot corrupt each other (see gop.rs).
13// ---------------------------------------------------------------------------
14use core::sync::atomic::{AtomicUsize, Ordering};
15
16use crate::ps2::MouseEvent;
17use crate::sched::TaskMsg;
18
19/// Forward target task id, or `usize::MAX` = none.
20static FORWARD_TARGET: AtomicUsize = AtomicUsize::new(usize::MAX);
21
22/// Spawn the cursor task. Call after `sched::init`; only makes sense
23/// when the PS/2 mouse came up at boot. The cursor starts centred.
24pub fn spawn() -> Option<usize> {
25 let (w, h) = crate::gop::pixel_dims();
26 crate::gop::cursor_set(w / 2 - 6, h / 2 - 8);
27 // Draw it immediately: the vt diff only re-blits changed cells, so an
28 // idle centre cell would never push (and thus never composite) it.
29 crate::gop::cursor_redraw();
30 let id = crate::sched::spawn(task_main, "cursor")?;
31 crate::serial_println!(
32 "cursor: task {} — mouse poll + display overlay (forward target: none)",
33 id
34 );
35 Some(id)
36}
37
38/// Route decoded mouse events to a task's queue (the shell's `mouse
39/// <id>`). `None` stops forwarding.
40pub fn set_forward(target: Option<usize>) {
41 FORWARD_TARGET.store(target.unwrap_or(usize::MAX), Ordering::Relaxed);
42}
43
44fn forward_target() -> Option<usize> {
45 match FORWARD_TARGET.load(Ordering::Relaxed) {
46 usize::MAX => None,
47 id => Some(id),
48 }
49}
50
51fn task_main() {
52 loop {
53 while let Some(ev) = crate::ps2::try_poll_mouse() {
54 crate::gop::cursor_move(ev.dx, ev.dy);
55 crate::gop::cursor_set_buttons(btn_bits(&ev));
56 if let Some(id) = forward_target()
57 && crate::sched::send(id, TaskMsg::Mouse(ev)).is_err()
58 {
59 // The listener died mid-stream: stop routing (the
60 // cursor keeps tracking) rather than spam errors.
61 set_forward(None);
62 crate::serial_println!("cursor: forward target task {} gone — stopped routing", id);
63 }
64 }
65 crate::sched::sleep_ms(10);
66 }
67}
68
69fn btn_bits(ev: &MouseEvent) -> u8 {
70 (ev.left as u8) | ((ev.right as u8) << 1) | ((ev.middle as u8) << 2)
71}