///|
/// Cursor alpha is stored separately as one 0/1 integer per pixel.
pub(all) struct Cursor {
hotspot_x : Int
hotspot_y : Int
width : Int
height : Int
pixels : Array[Int]
alpha : Array[Int]
} derive(Debug, Eq)
///|
pub(all) enum Event {
Ready(Screen)
Painted(Rect)
Copied(Rect, Int, Int)
Resized(Int, Int)
CursorChanged(Cursor)
Bell
Clipboard(Array[Int])
UpdateComplete
} derive(Debug, Eq)
///|
priv enum Update {
Paint(Rect, Array[Int])
Copy(Rect, Int, Int)
Resize(Int, Int)
SetCursor(Cursor)
}
///|
priv enum ServerMessage {
Frame(Array[Update])
Ring
Text(Array[Int])
}
///|
fn read_rectangle(r : Reader) -> Rect raise RfbError {
let x = r.u16()
let y = r.u16()
let width = r.u16()
let height = r.u16()
{ x, y, width, height, }
}
///|
fn read_cursor(
r : Reader,
f : PixelFormat,
rect : Rect,
opt : Options,
) -> Cursor raise RfbError {
let w = rect.width
let h = rect.height
if w > 1024 || h > 1024 || (h > 0 && w > opt.max_pixels / h) {
raise Limit("cursor")
}
if (w == 0) != (h == 0) || (w > 0 && (rect.x >= w || rect.y >= h)) {
raise Invalid("cursor hotspot")
}
let pixels = raw_pixels(r, f, w, h)
let stride = (w + 7) / 8
let mask = r.take(stride * h)
let alpha = Array::makei(w * h, i => {
(mask[i / w * stride + i % w / 8] >> (7 - i % w % 8)) & 1
})
{ hotspot_x: rect.x, hotspot_y: rect.y, width: w, height: h, pixels, alpha, }
}
///|
/// Parse a whole update transaction before applying any rectangle.
fn server_message(
r : Reader,
f : PixelFormat,
width : Int,
height : Int,
opt : Options,
) -> ServerMessage raise RfbError {
match r.u8() {
0 => {
ignore(r.u8())
let count = r.u16()
if count > 4096 {
raise Limit("rectangle count")
}
let updates = []
let mut w = width
let mut h = height
let mut work = 0
for i in 0.. opt.max_pixels - work {
raise Limit("update pixel work")
}
work += n
updates.push(SetCursor(cursor))
} else {
check_rect(rect, w, h)
let n = rect.width * rect.height
if n > opt.max_pixels - work {
raise Limit("update pixel work")
}
work += n
match encoding {
0 =>
updates.push(
Paint(rect, raw_pixels(r, f, rect.width, rect.height)),
)
1 => {
let sx = r.u16()
let sy = r.u16()
check_rect({ ..rect, x: sx, y: sy, }, w, h)
updates.push(Copy(rect, sx, sy))
}
15 =>
updates.push(
Paint(rect, trle_pixels(r, f, rect.width, rect.height)),
)
_ => raise Unsupported(encoding)
}
}
}
Frame(updates)
}
2 => Ring
3 => {
ignore(r.take(3))
let n = r.i32()
if n < 0 || n > opt.max_text {
raise Limit("clipboard")
}
Text(r.take(n))
}
tag => raise Unsupported(tag)
}
}