Skip to content

Commit 561e9ae

Browse files
committed
Merge branch 'compositor' into 'master'
Introduce a dedicated Compositor type See merge request redox-os/orbital!72
2 parents d1a6468 + 12b1260 commit 561e9ae

File tree

6 files changed

+676
-582
lines changed

6 files changed

+676
-582
lines changed

‎src/compositor.rs‎

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
use std::io::{Read, Write};
2+
use std::sync::Arc;
3+
use std::time::Instant;
4+
use std::{mem, slice};
5+
6+
use log::{error, info};
7+
8+
use crate::core::display::Display;
9+
use crate::core::image::Image;
10+
use crate::core::rect::Rect;
11+
12+
#[repr(C, packed)]
13+
struct CursorCommand {
14+
//header flag that indicates update_cursor or move_cursor
15+
header: u32,
16+
x: i32,
17+
y: i32,
18+
hot_x: i32,
19+
hot_y: i32,
20+
w: i32,
21+
h: i32,
22+
cursor_img: [u32; 4096],
23+
}
24+
25+
pub struct Compositor {
26+
displays: Vec<Display>,
27+
28+
redraws: Vec<Rect>,
29+
30+
popup: Option<Image>,
31+
32+
hw_cursor: bool,
33+
//QEMU UIs do not grab the pointer in case an absolute pointing device is present
34+
//and since releasing our gpu cursor makes it disappear, updating it every second fixes it
35+
update_cursor_timer: Instant,
36+
cursor: Arc<Image>,
37+
cursor_x: i32,
38+
cursor_y: i32,
39+
cursor_hot_x: i32,
40+
cursor_hot_y: i32,
41+
}
42+
43+
impl Compositor {
44+
pub fn new(mut displays: Vec<Display>) -> Self {
45+
let mut redraws = Vec::new();
46+
for display in displays.iter() {
47+
redraws.push(display.screen_rect());
48+
}
49+
50+
//Reading display file is only used to check if GPU cursor is supported
51+
let mut buf_array = [0; 1];
52+
let buf: &mut [u8] = &mut buf_array;
53+
let _ret = displays[0].file.read(buf);
54+
55+
let mut hw_cursor: bool = false;
56+
57+
if buf[0] == 1 {
58+
info!("Hardware cursor detected");
59+
hw_cursor = true;
60+
}
61+
62+
Compositor {
63+
displays,
64+
65+
redraws,
66+
67+
popup: None,
68+
69+
hw_cursor,
70+
update_cursor_timer: Instant::now(),
71+
cursor: Arc::new(Image::new(0, 0)),
72+
cursor_x: 0,
73+
cursor_y: 0,
74+
cursor_hot_x: 0,
75+
cursor_hot_y: 0,
76+
}
77+
}
78+
79+
pub fn displays(&self) -> &[Display] {
80+
&self.displays
81+
}
82+
83+
/// Return the screen rectangle
84+
pub fn screen_rect(&self) -> Rect {
85+
self.displays[0].screen_rect()
86+
}
87+
88+
/// Find the display that a window (`rect`) most overlaps and return it's screen_rect
89+
pub fn get_screen_rect_for_window(&self, rect: &Rect) -> Rect {
90+
let mut screen_rect = self.displays[0].screen_rect();
91+
let mut max_intersection_area = 0;
92+
for display in &self.displays {
93+
let intersect = display.screen_rect().intersection(rect);
94+
if intersect.area() > max_intersection_area {
95+
screen_rect = display.screen_rect();
96+
max_intersection_area = intersect.area();
97+
}
98+
}
99+
screen_rect
100+
}
101+
102+
/// Resize the inner image buffer.
103+
pub fn resize(&mut self, width: i32, height: i32) {
104+
//TODO: should other screens be moved after a resize?
105+
//TODO: support resizing other screens?
106+
self.displays[0].resize(width, height);
107+
108+
self.schedule(self.screen_rect());
109+
}
110+
111+
pub fn schedule(&mut self, request: Rect) {
112+
let mut push = true;
113+
for rect in self.redraws.iter_mut() {
114+
//If contained, ignore new redraw request
115+
let container = rect.container(&request);
116+
if container.area() <= rect.area() + request.area() {
117+
*rect = container;
118+
push = false;
119+
break;
120+
}
121+
}
122+
123+
if push {
124+
self.redraws.push(request);
125+
}
126+
}
127+
128+
/// Create a [Rect] that places a popup in the middle of the display
129+
fn popup_rect(&self, popup: &Image) -> Rect {
130+
Rect::new(
131+
self.screen_rect().width() / 2 - popup.width() / 2,
132+
self.screen_rect().height() / 2 - popup.height() / 2,
133+
popup.width(),
134+
popup.height(),
135+
)
136+
}
137+
138+
pub fn set_popup(&mut self, image: Option<Image>) {
139+
if let Some(popup) = &self.popup {
140+
// Ensure content behind the popup is redrawn
141+
self.schedule(self.popup_rect(popup));
142+
}
143+
144+
self.popup = image;
145+
}
146+
147+
fn cursor_rect(&self) -> Rect {
148+
Rect::new(
149+
self.cursor_x - self.cursor_hot_x,
150+
self.cursor_y - self.cursor_hot_y,
151+
self.cursor.width(),
152+
self.cursor.height(),
153+
)
154+
}
155+
156+
pub fn update_cursor(&mut self, x: i32, y: i32, hot_x: i32, hot_y: i32, cursor: &Arc<Image>) {
157+
if !self.hw_cursor {
158+
self.schedule(self.cursor_rect());
159+
}
160+
161+
if self.hw_cursor {
162+
if Arc::ptr_eq(&self.cursor, cursor)
163+
&& self.cursor_hot_x == hot_x
164+
&& self.cursor_hot_y == hot_y
165+
{
166+
self.send_cursor_command(&CursorCommand {
167+
header: 0,
168+
x,
169+
y,
170+
hot_x: 0,
171+
hot_y: 0,
172+
w: 0,
173+
h: 0,
174+
cursor_img: [0; 4096],
175+
});
176+
} else {
177+
self.send_cursor_command(&CursorCommand {
178+
header: 1,
179+
x,
180+
y,
181+
hot_x,
182+
hot_y,
183+
w: cursor.width(),
184+
h: cursor.height(),
185+
cursor_img: cursor.get_cursor_data(),
186+
});
187+
}
188+
}
189+
190+
self.cursor_x = x;
191+
self.cursor_y = y;
192+
self.cursor_hot_x = hot_x;
193+
self.cursor_hot_y = hot_y;
194+
self.cursor = cursor.clone();
195+
196+
if !self.hw_cursor {
197+
self.schedule(self.cursor_rect());
198+
}
199+
}
200+
201+
fn send_cursor_command(&mut self, cmd: &CursorCommand) {
202+
for (i, display) in self.displays.iter_mut().enumerate() {
203+
match display.file.write(unsafe {
204+
slice::from_raw_parts(
205+
cmd as *const CursorCommand as *const u8,
206+
mem::size_of::<CursorCommand>(),
207+
)
208+
}) {
209+
Ok(_) => (),
210+
Err(err) => error!("failed to sync display {}: {}", i, err),
211+
}
212+
}
213+
}
214+
215+
pub fn redraw_windows(
216+
&mut self,
217+
total_redraw_opt: &mut Option<Rect>,
218+
draw_windows: impl Fn(&mut Display, Rect),
219+
) {
220+
// go through the list of rectangles pending a redraw and expand the total redraw rectangle
221+
// to encompass all of them
222+
for original_rect in self.redraws.drain(..) {
223+
if !original_rect.is_empty() {
224+
*total_redraw_opt = Some(
225+
total_redraw_opt
226+
.unwrap_or(original_rect)
227+
.container(&original_rect),
228+
);
229+
}
230+
231+
for display in self.displays.iter_mut() {
232+
let rect = original_rect.intersection(&display.screen_rect());
233+
if rect.is_empty() {
234+
continue;
235+
}
236+
237+
draw_windows(display, rect);
238+
}
239+
}
240+
}
241+
242+
pub fn redraw_popup(&mut self, total_redraw_opt: &mut Option<Rect>) {
243+
if let Some(popup) = &self.popup {
244+
let popup_rect = self.popup_rect(popup);
245+
246+
*total_redraw_opt = Some(
247+
total_redraw_opt
248+
.unwrap_or(popup_rect)
249+
.container(&popup_rect),
250+
);
251+
252+
self.displays[0]
253+
.image
254+
.roi_mut(&popup_rect)
255+
.blit(&popup.roi(&Rect::new(0, 0, popup.width(), popup.height())));
256+
}
257+
}
258+
259+
pub fn redraw_cursor(&mut self, total_redraw: Option<Rect>) {
260+
if self.hw_cursor {
261+
if self.hw_cursor && self.update_cursor_timer.elapsed().as_millis() > 1000 {
262+
self.send_cursor_command(&CursorCommand {
263+
header: 1,
264+
x: self.cursor_x,
265+
y: self.cursor_y,
266+
hot_x: self.cursor_hot_x,
267+
hot_y: self.cursor_hot_y,
268+
w: self.cursor.width(),
269+
h: self.cursor.height(),
270+
cursor_img: self.cursor.get_cursor_data(),
271+
});
272+
self.update_cursor_timer = Instant::now();
273+
}
274+
275+
return;
276+
}
277+
278+
let Some(total_redraw) = total_redraw else {
279+
return;
280+
};
281+
282+
let cursor_rect = self.cursor_rect();
283+
284+
for display in self.displays.iter_mut() {
285+
let rect = total_redraw.intersection(&display.screen_rect());
286+
if !rect.is_empty() {
287+
let cursor_intersect = rect.intersection(&cursor_rect);
288+
if !cursor_intersect.is_empty() {
289+
display.roi_mut(&cursor_intersect).blend(
290+
&self
291+
.cursor
292+
.roi(&cursor_intersect.offset(-cursor_rect.left(), -cursor_rect.top())),
293+
);
294+
}
295+
}
296+
}
297+
}
298+
299+
pub fn sync_rect(&mut self, total_redraw: Rect) {
300+
// Sync any parts of displays that changed
301+
for (i, display) in self.displays.iter_mut().enumerate() {
302+
let display_redraw = total_redraw.intersection(&display.screen_rect());
303+
if !display_redraw.is_empty() {
304+
// Keep synced with vesad
305+
#[allow(dead_code)]
306+
#[repr(packed)]
307+
struct SyncRect {
308+
x: i32,
309+
y: i32,
310+
w: i32,
311+
h: i32,
312+
}
313+
314+
let sync_rect = SyncRect {
315+
x: display_redraw.left() - display.x,
316+
y: display_redraw.top() - display.y,
317+
w: display_redraw.width(),
318+
h: display_redraw.height(),
319+
};
320+
321+
match display.file.write(unsafe {
322+
slice::from_raw_parts(
323+
&sync_rect as *const SyncRect as *const u8,
324+
mem::size_of::<SyncRect>(),
325+
)
326+
}) {
327+
Ok(_) => (),
328+
Err(err) => error!("failed to sync display {}: {}", i, err),
329+
}
330+
}
331+
}
332+
}
333+
}

‎src/core/display.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use orbclient::{Color, Renderer};
44
use std::{convert::TryInto, fs::File, io, os::unix::io::AsRawFd, slice};
55

66
use crate::core::{
7-
image::{ImageRef, ImageRoi},
7+
image::{ImageRef, ImageRoiMut},
88
rect::Rect,
99
};
1010

@@ -83,8 +83,8 @@ impl Display {
8383
}
8484
}
8585

86-
pub fn roi(&mut self, rect: &Rect) -> ImageRoi {
87-
self.image.roi(&Rect::new(
86+
pub fn roi_mut(&mut self, rect: &Rect) -> ImageRoiMut {
87+
self.image.roi_mut(&Rect::new(
8888
rect.left() - self.x,
8989
rect.top() - self.y,
9090
rect.width(),

0 commit comments

Comments
 (0)