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