portal: add a desktop portal
This commit is contained in:
parent
323a6ed953
commit
a162055f1d
38 changed files with 2389 additions and 27 deletions
876
src/portal/ptr_gui.rs
Normal file
876
src/portal/ptr_gui.rs
Normal file
|
|
@ -0,0 +1,876 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{Phase, SpawnedFuture},
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
format::ARGB8888,
|
||||
ifs::zwlr_layer_shell_v1::OVERLAY,
|
||||
portal::ptl_display::{PortalDisplay, PortalOutput, PortalSeat},
|
||||
render::{Framebuffer, RenderContext, RendererBase, Texture},
|
||||
text::{self, TextMeasurement},
|
||||
theme::Color,
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt, rc_eq::rc_eq,
|
||||
},
|
||||
video::{gbm::GBM_BO_USE_RENDERING, ModifiedFormat, INVALID_MODIFIER},
|
||||
wire::{
|
||||
wp_fractional_scale_v1::PreferredScale, zwlr_layer_surface_v1::Configure,
|
||||
ZwpLinuxBufferParamsV1Id,
|
||||
},
|
||||
wl_usr::usr_ifs::{
|
||||
usr_linux_buffer_params::{UsrLinuxBufferParams, UsrLinuxBufferParamsOwner},
|
||||
usr_wl_buffer::{UsrWlBuffer, UsrWlBufferOwner},
|
||||
usr_wl_surface::UsrWlSurface,
|
||||
usr_wlr_layer_surface::{UsrWlrLayerSurface, UsrWlrLayerSurfaceOwner},
|
||||
usr_wp_fractional_scale::{UsrWpFractionalScale, UsrWpFractionalScaleOwner},
|
||||
usr_wp_viewport::UsrWpViewport,
|
||||
},
|
||||
},
|
||||
ahash::AHashSet,
|
||||
std::{
|
||||
borrow::Cow,
|
||||
cell::{Cell, RefCell},
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GuiElementData {
|
||||
pub x: Cell<f32>,
|
||||
pub y: Cell<f32>,
|
||||
pub width: Cell<f32>,
|
||||
pub height: Cell<f32>,
|
||||
}
|
||||
|
||||
pub trait GuiElement {
|
||||
fn data(&self) -> &GuiElementData;
|
||||
fn layout(
|
||||
&self,
|
||||
ctx: &Rc<RenderContext>,
|
||||
scale: f32,
|
||||
max_width: f32,
|
||||
max_height: f32,
|
||||
) -> (f32, f32);
|
||||
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32);
|
||||
fn child_at(&self, x: f32, y: f32) -> Option<Rc<dyn GuiElement>>;
|
||||
|
||||
fn hover_cursor(&self) -> KnownCursor {
|
||||
KnownCursor::Default
|
||||
}
|
||||
|
||||
fn button(&self, seat: &PortalSeat, button: u32, state: u32) {
|
||||
let _ = seat;
|
||||
let _ = button;
|
||||
let _ = state;
|
||||
}
|
||||
|
||||
fn hover(&self, seat: &PortalSeat, hover: bool) -> bool {
|
||||
let _ = seat;
|
||||
let _ = hover;
|
||||
false
|
||||
}
|
||||
|
||||
fn destroy(&self) {}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct ButtonExtents {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
pub tex_off_x: f32,
|
||||
pub tex_off_y: f32,
|
||||
}
|
||||
|
||||
pub fn button_extents(
|
||||
msmt: &TextMeasurement,
|
||||
scale: f32,
|
||||
padding: f32,
|
||||
border: f32,
|
||||
) -> ButtonExtents {
|
||||
let above_baseline_height =
|
||||
(msmt.baseline - msmt.ink_rect.y1().max(msmt.logical_rect.y1())) as f32 / scale;
|
||||
let height = above_baseline_height + 2.0 * (padding + border);
|
||||
let width = msmt.ink_rect.width() as f32 / scale + 2.0 * (padding + border);
|
||||
let tex_off_x = padding + border;
|
||||
// let tex_off_y = ((msmt.ink_rect.y1() - msmt.logical_rect.y1()) as f64 / scale).round() as i32 + padding + border;
|
||||
let tex_off_y = padding + border;
|
||||
ButtonExtents {
|
||||
width,
|
||||
height,
|
||||
tex_off_x,
|
||||
tex_off_y,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Button {
|
||||
pub data: GuiElementData,
|
||||
pub tex_off_x: Cell<f32>,
|
||||
pub tex_off_y: Cell<f32>,
|
||||
pub hover: RefCell<AHashSet<u32>>,
|
||||
pub padding: Cell<f32>,
|
||||
pub border: Cell<f32>,
|
||||
pub border_color: Cell<Color>,
|
||||
pub bg_color: Cell<Color>,
|
||||
pub bg_hover_color: Cell<Color>,
|
||||
pub text: RefCell<String>,
|
||||
pub font: RefCell<Cow<'static, str>>,
|
||||
pub tex: CloneCell<Option<Rc<Texture>>>,
|
||||
pub owner: CloneCell<Option<Rc<dyn ButtonOwner>>>,
|
||||
}
|
||||
|
||||
pub trait ButtonOwner {
|
||||
fn button(&self, button: u32, state: u32);
|
||||
}
|
||||
|
||||
impl Default for Button {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: Default::default(),
|
||||
tex_off_x: Cell::new(0.0),
|
||||
tex_off_y: Cell::new(0.0),
|
||||
hover: Default::default(),
|
||||
padding: Default::default(),
|
||||
border: Default::default(),
|
||||
border_color: Cell::new(Color::from_gray(0)),
|
||||
bg_color: Cell::new(Color::from_gray(255)),
|
||||
bg_hover_color: Cell::new(Color::from_gray(255)),
|
||||
text: Default::default(),
|
||||
font: RefCell::new(DEFAULT_FONT.into()),
|
||||
tex: Default::default(),
|
||||
owner: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiElement for Button {
|
||||
fn hover_cursor(&self) -> KnownCursor {
|
||||
KnownCursor::Pointer
|
||||
}
|
||||
|
||||
fn data(&self) -> &GuiElementData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
ctx: &Rc<RenderContext>,
|
||||
scale: f32,
|
||||
_max_width: f32,
|
||||
_max_height: f32,
|
||||
) -> (f32, f32) {
|
||||
let font = self.font.borrow_mut();
|
||||
let text = self.text.borrow_mut();
|
||||
let tex = text::render_fitting2(
|
||||
ctx,
|
||||
None,
|
||||
&font,
|
||||
&text,
|
||||
Color::from_gray(0),
|
||||
false,
|
||||
Some(scale as _),
|
||||
true,
|
||||
)
|
||||
.ok();
|
||||
let (tex, msmt) = match tex {
|
||||
Some((a, b)) => (Some(a), Some(b)),
|
||||
_ => (None, None),
|
||||
};
|
||||
let extents = match msmt {
|
||||
Some(m) => button_extents(&m, scale, self.padding.get(), self.border.get()),
|
||||
_ => Default::default(),
|
||||
};
|
||||
self.tex.set(tex);
|
||||
self.tex_off_x.set(extents.tex_off_x);
|
||||
self.tex_off_y.set(extents.tex_off_y);
|
||||
(extents.width, extents.height)
|
||||
}
|
||||
|
||||
fn render_at(&self, r: &mut RendererBase, x1: f32, y1: f32) {
|
||||
let x2 = x1 + self.data.width.get();
|
||||
let y2 = y1 + self.data.height.get();
|
||||
let border = self.border.get();
|
||||
{
|
||||
let rects = [
|
||||
(x1, y1, x2, y1 + border),
|
||||
(x1, y2 - border, x2, y2),
|
||||
(x1, y1 + border, x1 + border, y2 - border),
|
||||
(x2 - border, y1 + border, x2, y2 - border),
|
||||
];
|
||||
r.fill_boxes_f(&rects, &self.border_color.get());
|
||||
}
|
||||
{
|
||||
let rects = [(x1 + border, y1 + border, x2 - border, y2 - border)];
|
||||
let color = match self.hover.borrow_mut().is_empty() {
|
||||
true => self.bg_color.get(),
|
||||
false => self.bg_hover_color.get(),
|
||||
};
|
||||
r.fill_boxes_f(&rects, &color);
|
||||
}
|
||||
if let Some(tex) = self.tex.get() {
|
||||
let (tx, ty) =
|
||||
r.scale_point_f(x1 + self.tex_off_x.get(), y1 as f32 + self.tex_off_y.get());
|
||||
r.render_texture(
|
||||
&tex,
|
||||
tx.round() as _,
|
||||
ty.round() as _,
|
||||
ARGB8888,
|
||||
None,
|
||||
None,
|
||||
r.scale(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn child_at(&self, _x: f32, _y: f32) -> Option<Rc<dyn GuiElement>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn hover(&self, seat: &PortalSeat, hover: bool) -> bool {
|
||||
let ret;
|
||||
let mut set = self.hover.borrow_mut();
|
||||
if hover {
|
||||
ret = set.is_empty();
|
||||
set.insert(seat.global_id);
|
||||
} else {
|
||||
set.remove(&seat.global_id);
|
||||
ret = set.is_empty();
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn destroy(&self) {
|
||||
self.owner.take();
|
||||
}
|
||||
|
||||
fn button(&self, _seat: &PortalSeat, button: u32, state: u32) {
|
||||
if let Some(owner) = self.owner.get() {
|
||||
owner.button(button, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_FONT: &str = "sans-serif 16";
|
||||
|
||||
pub struct Label {
|
||||
pub data: GuiElementData,
|
||||
pub font: RefCell<Cow<'static, str>>,
|
||||
pub text: RefCell<String>,
|
||||
pub tex: CloneCell<Option<Rc<Texture>>>,
|
||||
}
|
||||
|
||||
impl Default for Label {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: Default::default(),
|
||||
font: RefCell::new(DEFAULT_FONT.into()),
|
||||
text: RefCell::new("".to_string()),
|
||||
tex: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiElement for Label {
|
||||
fn data(&self) -> &GuiElementData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
ctx: &Rc<RenderContext>,
|
||||
scale: f32,
|
||||
_max_width: f32,
|
||||
_max_height: f32,
|
||||
) -> (f32, f32) {
|
||||
let text = self.text.borrow_mut();
|
||||
let font = self.font.borrow_mut();
|
||||
let tex = text::render_fitting2(
|
||||
ctx,
|
||||
None,
|
||||
&font,
|
||||
&text,
|
||||
Color::from_gray(255),
|
||||
false,
|
||||
Some(scale as _),
|
||||
false,
|
||||
)
|
||||
.ok();
|
||||
let (tex, width, height) = match tex {
|
||||
Some((t, _)) => (Some(t.clone()), t.width(), t.height()),
|
||||
_ => (None, 0, 0),
|
||||
};
|
||||
self.tex.set(tex);
|
||||
(width as f32 / scale, height as f32 / scale)
|
||||
}
|
||||
|
||||
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32) {
|
||||
if let Some(tex) = self.tex.get() {
|
||||
let (tx, ty) = r.scale_point_f(x, y);
|
||||
r.render_texture(
|
||||
&tex,
|
||||
tx.round() as _,
|
||||
ty.round() as _,
|
||||
ARGB8888,
|
||||
None,
|
||||
None,
|
||||
r.scale(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn child_at(&self, _x: f32, _y: f32) -> Option<Rc<dyn GuiElement>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub enum Align {
|
||||
#[allow(dead_code)]
|
||||
Left,
|
||||
#[default]
|
||||
Center,
|
||||
#[allow(dead_code)]
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub enum Orientation {
|
||||
#[default]
|
||||
Horizontal,
|
||||
#[allow(dead_code)]
|
||||
Vertical,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Flow {
|
||||
pub data: GuiElementData,
|
||||
pub in_margin: Cell<f32>,
|
||||
pub cross_margin: Cell<f32>,
|
||||
pub orientation: Cell<Orientation>,
|
||||
pub cross_align: Cell<Align>,
|
||||
pub elements: RefCell<Vec<Rc<dyn GuiElement>>>,
|
||||
}
|
||||
|
||||
impl GuiElement for Flow {
|
||||
fn data(&self) -> &GuiElementData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
ctx: &Rc<RenderContext>,
|
||||
scale: f32,
|
||||
max_width: f32,
|
||||
max_height: f32,
|
||||
) -> (f32, f32) {
|
||||
let elements = self.elements.borrow_mut();
|
||||
let orientation = self.orientation.get();
|
||||
let (max_in_size, _max_cross_size) = match orientation {
|
||||
Orientation::Horizontal => (max_width, max_height),
|
||||
Orientation::Vertical => (max_height, max_width),
|
||||
};
|
||||
let mut runs = vec![];
|
||||
let mut run = vec![];
|
||||
let cross_margin = self.cross_margin.get();
|
||||
let in_margin = self.in_margin.get();
|
||||
{
|
||||
let mut run_cross_size: f32 = 0.0;
|
||||
let mut in_pos = in_margin;
|
||||
for element in elements.deref() {
|
||||
let (w, h) = element.layout(ctx, scale, max_width, max_height);
|
||||
let (cur_in_size, cur_cross_size) = match orientation {
|
||||
Orientation::Horizontal => (w, h),
|
||||
Orientation::Vertical => (h, w),
|
||||
};
|
||||
if in_pos + cur_in_size > max_in_size && run.len() > 0 {
|
||||
runs.push((run, run_cross_size));
|
||||
run = vec![];
|
||||
in_pos = in_margin;
|
||||
run_cross_size = 0.0;
|
||||
}
|
||||
run_cross_size = run_cross_size.max(cur_cross_size);
|
||||
run.push((element, cur_in_size, cur_cross_size));
|
||||
in_pos += cur_in_size + in_margin;
|
||||
}
|
||||
if run.len() > 0 {
|
||||
runs.push((run, run_cross_size));
|
||||
}
|
||||
}
|
||||
let mut max_in_pos: f32 = 0.0;
|
||||
let mut cross_pos = cross_margin;
|
||||
for (run, run_cross_size) in runs {
|
||||
let mut in_pos = in_margin;
|
||||
for (element, cur_in_size, cur_cross_size) in run {
|
||||
let cur_cross_pos = cross_pos
|
||||
+ match self.cross_align.get() {
|
||||
Align::Left => 0.0,
|
||||
Align::Center => (run_cross_size - cur_cross_size) / 2.0,
|
||||
Align::Right => run_cross_size - cur_cross_size,
|
||||
};
|
||||
let (x, y, w, h) = match orientation {
|
||||
Orientation::Horizontal => (in_pos, cur_cross_pos, cur_in_size, cur_cross_size),
|
||||
Orientation::Vertical => (cur_cross_pos, in_pos, cur_cross_size, cur_in_size),
|
||||
};
|
||||
element.data().x.set(x);
|
||||
element.data().y.set(y);
|
||||
element.data().width.set(w);
|
||||
element.data().height.set(h);
|
||||
in_pos += in_margin + cur_in_size;
|
||||
}
|
||||
max_in_pos = max_in_pos.max(in_pos);
|
||||
cross_pos += cross_margin + run_cross_size;
|
||||
}
|
||||
let (w, h) = match orientation {
|
||||
Orientation::Horizontal => (max_in_pos, cross_pos),
|
||||
Orientation::Vertical => (cross_pos, max_in_pos),
|
||||
};
|
||||
(w.min(max_width), h.min(max_height))
|
||||
}
|
||||
|
||||
fn render_at(&self, r: &mut RendererBase, x: f32, y: f32) {
|
||||
for element in self.elements.borrow_mut().deref() {
|
||||
element.render_at(r, x + element.data().x.get(), y + element.data().y.get());
|
||||
}
|
||||
}
|
||||
|
||||
fn child_at(&self, x: f32, y: f32) -> Option<Rc<dyn GuiElement>> {
|
||||
for child in self.elements.borrow_mut().deref() {
|
||||
let data = child.data();
|
||||
let x1 = data.x.get();
|
||||
let y1 = data.y.get();
|
||||
if x >= x1 && x - x1 < data.width.get() && y >= y1 && y - y1 < data.height.get() {
|
||||
return Some(child.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn destroy(&self) {
|
||||
for element in self.elements.borrow_mut().drain(..) {
|
||||
element.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OverlayWindow {
|
||||
pub layer_surface: Rc<UsrWlrLayerSurface>,
|
||||
pub data: Rc<WindowData>,
|
||||
pub owner: CloneCell<Option<Rc<dyn OverlayWindowOwner>>>,
|
||||
}
|
||||
|
||||
pub trait OverlayWindowOwner {
|
||||
fn kill(&self, upwards: bool);
|
||||
}
|
||||
|
||||
pub struct WindowData {
|
||||
pub frame_missed: Cell<bool>,
|
||||
pub first_scale: Cell<bool>,
|
||||
pub have_frame: Cell<bool>,
|
||||
pub scale: Cell<Fixed>,
|
||||
pub render_trigger: AsyncEvent,
|
||||
pub render_task: Cell<Option<SpawnedFuture<()>>>,
|
||||
pub dpy: Rc<PortalDisplay>,
|
||||
pub content: CloneCell<Option<Rc<dyn GuiElement>>>,
|
||||
pub surface: Rc<UsrWlSurface>,
|
||||
pub viewport: Rc<UsrWpViewport>,
|
||||
pub fractional_scale: Rc<UsrWpFractionalScale>,
|
||||
pub bufs: RefCell<Vec<Rc<GuiBuffer>>>,
|
||||
pending_bufs: CopyHashMap<ZwpLinuxBufferParamsV1Id, Rc<GuiBufferPending>>,
|
||||
pub width: Cell<i32>,
|
||||
pub height: Cell<i32>,
|
||||
pub owner: CloneCell<Option<Rc<dyn WindowDataOwner>>>,
|
||||
pub seats: CopyHashMap<u32, Rc<GuiWindowSeatState>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GuiWindowSeatState {
|
||||
pub x: Cell<f32>,
|
||||
pub y: Cell<f32>,
|
||||
pub tree: RefCell<Vec<Rc<dyn GuiElement>>>,
|
||||
pub cursor: Cell<Option<KnownCursor>>,
|
||||
}
|
||||
|
||||
pub trait WindowDataOwner {
|
||||
fn post_layout(&self);
|
||||
fn kill(&self, upwards: bool);
|
||||
}
|
||||
|
||||
impl WindowDataOwner for OverlayWindow {
|
||||
fn post_layout(&self) {
|
||||
self.layer_surface
|
||||
.set_size(self.data.width.get(), self.data.height.get());
|
||||
self.data.surface.commit();
|
||||
}
|
||||
|
||||
fn kill(&self, upwards: bool) {
|
||||
if let Some(owner) = self.owner.take() {
|
||||
owner.kill(upwards);
|
||||
}
|
||||
self.layer_surface
|
||||
.con
|
||||
.remove_obj(self.layer_surface.deref());
|
||||
}
|
||||
}
|
||||
|
||||
const NUM_BUFFERS: usize = 2;
|
||||
|
||||
impl OverlayWindow {
|
||||
pub fn new(output: &Rc<PortalOutput>) -> Rc<Self> {
|
||||
let data = WindowData::new(&output.dpy);
|
||||
let layer_surface = output
|
||||
.dpy
|
||||
.ls
|
||||
.get_layer_surface(&data.surface, &output.wl, OVERLAY);
|
||||
layer_surface.set_size(1, 1);
|
||||
let slf = Rc::new(Self {
|
||||
layer_surface,
|
||||
data,
|
||||
owner: Default::default(),
|
||||
});
|
||||
slf.data.owner.set(Some(slf.clone()));
|
||||
slf.layer_surface.owner.set(Some(slf.clone()));
|
||||
slf.data.surface.commit();
|
||||
slf
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowData {
|
||||
pub fn schedule_render(&self) {
|
||||
self.render_trigger.trigger();
|
||||
}
|
||||
|
||||
pub fn new(dpy: &Rc<PortalDisplay>) -> Rc<Self> {
|
||||
let surface = dpy.comp.create_surface();
|
||||
let viewport = dpy.vp.get_viewport(&surface);
|
||||
let fractional_scale = dpy.fsm.get_fractional_scale(&surface);
|
||||
viewport.set_destination(1, 1);
|
||||
let data = Rc::new(WindowData {
|
||||
frame_missed: Cell::new(false),
|
||||
first_scale: Cell::new(true),
|
||||
have_frame: Cell::new(true),
|
||||
bufs: Default::default(),
|
||||
pending_bufs: Default::default(),
|
||||
width: Cell::new(0),
|
||||
height: Cell::new(0),
|
||||
owner: Default::default(),
|
||||
render_trigger: Default::default(),
|
||||
render_task: Cell::new(None),
|
||||
dpy: dpy.clone(),
|
||||
content: Default::default(),
|
||||
surface,
|
||||
viewport,
|
||||
scale: Cell::new(Fixed::from_int(1)),
|
||||
fractional_scale,
|
||||
seats: Default::default(),
|
||||
});
|
||||
data.render_task.set(Some(
|
||||
dpy.state
|
||||
.eng
|
||||
.spawn2(Phase::Present, data.clone().render_task()),
|
||||
));
|
||||
data.fractional_scale.owner.set(Some(data.clone()));
|
||||
data
|
||||
}
|
||||
|
||||
pub fn layout(&self) {
|
||||
let ctx = match self.dpy.render_ctx.get() {
|
||||
Some(ctx) => ctx,
|
||||
_ => return,
|
||||
};
|
||||
let scale = self.scale.get().to_f64() as f32;
|
||||
let content = match self.content.get() {
|
||||
Some(c) => c,
|
||||
_ => return,
|
||||
};
|
||||
let (mut width, mut height) = content.layout(&ctx.ctx, scale, f32::INFINITY, f32::INFINITY);
|
||||
content.data().width.set(width);
|
||||
content.data().height.set(height);
|
||||
width = width.max(1.0);
|
||||
height = height.max(1.0);
|
||||
self.width.set(width.round() as _);
|
||||
self.height.set(height.round() as _);
|
||||
self.viewport
|
||||
.set_destination(width.round() as _, height.round() as _);
|
||||
if let Some(owner) = self.owner.get() {
|
||||
owner.post_layout();
|
||||
}
|
||||
}
|
||||
|
||||
async fn render_task(self: Rc<Self>) {
|
||||
loop {
|
||||
self.render_trigger.triggered().await;
|
||||
self.render();
|
||||
}
|
||||
}
|
||||
|
||||
fn render(self: &Rc<Self>) {
|
||||
self.frame_missed.set(true);
|
||||
if !self.have_frame.get() {
|
||||
return;
|
||||
}
|
||||
let bufs = self.bufs.borrow_mut();
|
||||
let buf = 'get_buf: {
|
||||
for buf in bufs.deref() {
|
||||
if buf.free.get() {
|
||||
break 'get_buf buf;
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
self.frame_missed.set(false);
|
||||
|
||||
self.surface.frame({
|
||||
let slf = self.clone();
|
||||
move || {
|
||||
slf.have_frame.set(true);
|
||||
if slf.frame_missed.get() {
|
||||
slf.schedule_render();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.have_frame.set(false);
|
||||
buf.free.set(false);
|
||||
|
||||
buf.fb.render_custom(self.scale.get(), |r| {
|
||||
r.clear(&Color::from_gray(0));
|
||||
if let Some(content) = self.content.get() {
|
||||
content.render_at(r, 0.0, 0.0)
|
||||
}
|
||||
});
|
||||
|
||||
self.surface.attach(&buf.wl);
|
||||
self.surface.commit();
|
||||
}
|
||||
|
||||
pub fn kill(&self, upwards: bool) {
|
||||
if let Some(owner) = self.owner.take() {
|
||||
owner.kill(upwards);
|
||||
}
|
||||
self.render_task.take();
|
||||
for (_, pb) in self.pending_bufs.lock().drain() {
|
||||
pb.params.con.remove_obj(pb.params.deref());
|
||||
}
|
||||
for buf in self.bufs.borrow_mut().drain(..) {
|
||||
buf.wl.con.remove_obj(buf.wl.deref());
|
||||
}
|
||||
self.fractional_scale
|
||||
.con
|
||||
.remove_obj(self.fractional_scale.deref());
|
||||
self.viewport.con.remove_obj(self.viewport.deref());
|
||||
self.surface.con.remove_obj(self.surface.deref());
|
||||
if let Some(content) = self.content.take() {
|
||||
content.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_buffers(self: &Rc<Self>) {
|
||||
{
|
||||
for (_, buf) in self.pending_bufs.lock().drain() {
|
||||
buf.params.con.remove_obj(buf.params.deref());
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut bufs = self.bufs.borrow_mut();
|
||||
for buf in bufs.drain(..) {
|
||||
buf.wl.con.remove_obj(buf.wl.deref());
|
||||
}
|
||||
}
|
||||
let ctx = match self.dpy.render_ctx.get() {
|
||||
Some(ctx) => ctx,
|
||||
_ => return,
|
||||
};
|
||||
let dmabuf = match self.dpy.dmabuf.get() {
|
||||
Some(dmabuf) => dmabuf,
|
||||
_ => return,
|
||||
};
|
||||
self.frame_missed.set(true);
|
||||
let width = (self.width.get() as f64 * self.scale.get().to_f64()).round() as i32;
|
||||
let height = (self.height.get() as f64 * self.scale.get().to_f64()).round() as i32;
|
||||
for _ in 0..NUM_BUFFERS {
|
||||
let format = ModifiedFormat {
|
||||
format: ARGB8888,
|
||||
modifier: INVALID_MODIFIER,
|
||||
};
|
||||
let bo = match ctx
|
||||
.ctx
|
||||
.gbm
|
||||
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)
|
||||
{
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
log::error!("Could not allocate dmabuf: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
let img = match ctx.ctx.dmabuf_img(bo.dmabuf()) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
log::error!("Could not import dmabuf into EGL: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
let fb = match img.to_framebuffer() {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not turns EGL image into framebuffer: {}",
|
||||
ErrorFmt(e)
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let params = dmabuf.create_params();
|
||||
params.create(bo.dmabuf());
|
||||
let pending = Rc::new(GuiBufferPending {
|
||||
window: self.clone(),
|
||||
fb,
|
||||
params,
|
||||
size: (width, height),
|
||||
});
|
||||
pending.params.owner.set(Some(pending.clone()));
|
||||
self.pending_bufs.set(pending.params.id, pending.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn tree_at(&self, tree: &mut Vec<Rc<dyn GuiElement>>, mut x: f32, mut y: f32) {
|
||||
let mut element = match self.content.get() {
|
||||
Some(e) => e,
|
||||
_ => return,
|
||||
};
|
||||
tree.push(element.clone());
|
||||
while let Some(c) = element.child_at(x, y) {
|
||||
tree.push(c.clone());
|
||||
x -= c.data().x.get();
|
||||
y -= c.data().y.get();
|
||||
element = c;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn motion(&self, pseat: &PortalSeat, x: Fixed, y: Fixed, _enter: bool) {
|
||||
let x = x.to_f64() as f32;
|
||||
let y = y.to_f64() as f32;
|
||||
let seat = self
|
||||
.seats
|
||||
.lock()
|
||||
.entry(pseat.global_id)
|
||||
.or_default()
|
||||
.clone();
|
||||
seat.x.set(x);
|
||||
seat.y.set(y);
|
||||
|
||||
let mut tree = seat.tree.borrow_mut();
|
||||
let old_element = tree.last().cloned();
|
||||
self.tree_at(&mut tree, x, y);
|
||||
let new_element = tree.last().cloned();
|
||||
|
||||
let element_changed = match (&old_element, &new_element) {
|
||||
(Some(old), Some(new)) => !rc_eq(old, new),
|
||||
(None, None) => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if element_changed {
|
||||
if let Some(o) = &old_element {
|
||||
o.hover(pseat, false);
|
||||
}
|
||||
if let Some(o) = &new_element {
|
||||
o.hover(pseat, true);
|
||||
}
|
||||
}
|
||||
|
||||
if element_changed {
|
||||
self.schedule_render();
|
||||
}
|
||||
|
||||
let cursor = match &new_element {
|
||||
Some(e) => e.hover_cursor(),
|
||||
_ => KnownCursor::Default,
|
||||
};
|
||||
|
||||
if seat.cursor.replace(Some(cursor)) != Some(cursor) {
|
||||
pseat.jay_pointer.set_known_cursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn button(&self, pseat: &PortalSeat, button: u32, state: u32) {
|
||||
let seat = match self.seats.get(&pseat.global_id) {
|
||||
Some(s) => s,
|
||||
_ => return,
|
||||
};
|
||||
let element = seat.tree.borrow_mut().last().cloned();
|
||||
if let Some(e) = element {
|
||||
e.button(pseat, button, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GuiBuffer {
|
||||
pub wl: Rc<UsrWlBuffer>,
|
||||
pub window: Rc<WindowData>,
|
||||
pub fb: Rc<Framebuffer>,
|
||||
pub free: Cell<bool>,
|
||||
pub size: (i32, i32),
|
||||
}
|
||||
|
||||
struct GuiBufferPending {
|
||||
pub window: Rc<WindowData>,
|
||||
pub fb: Rc<Framebuffer>,
|
||||
pub params: Rc<UsrLinuxBufferParams>,
|
||||
pub size: (i32, i32),
|
||||
}
|
||||
|
||||
impl UsrWlBufferOwner for GuiBuffer {
|
||||
fn release(&self) {
|
||||
self.free.set(true);
|
||||
if self.window.frame_missed.get() {
|
||||
self.window.schedule_render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsrWpFractionalScaleOwner for WindowData {
|
||||
fn preferred_scale(self: Rc<Self>, ev: &PreferredScale) {
|
||||
let mut layout = self.first_scale.replace(false);
|
||||
layout |= self.scale.replace(ev.scale) != ev.scale;
|
||||
if layout {
|
||||
self.layout();
|
||||
self.allocate_buffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsrWlrLayerSurfaceOwner for OverlayWindow {
|
||||
fn configure(&self, _ev: &Configure) {
|
||||
self.data.schedule_render();
|
||||
}
|
||||
|
||||
fn closed(&self) {
|
||||
self.data.kill(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl UsrLinuxBufferParamsOwner for GuiBufferPending {
|
||||
fn created(&self, buffer: Rc<UsrWlBuffer>) {
|
||||
buffer.con.add_object(buffer.clone());
|
||||
let buf = Rc::new(GuiBuffer {
|
||||
wl: buffer,
|
||||
window: self.window.clone(),
|
||||
fb: self.fb.clone(),
|
||||
free: Cell::new(true),
|
||||
size: self.size,
|
||||
});
|
||||
buf.wl.owner.set(Some(buf.clone()));
|
||||
self.window.bufs.borrow_mut().push(buf);
|
||||
self.params.con.remove_obj(self.params.deref());
|
||||
self.window.pending_bufs.remove(&self.params.id);
|
||||
if self.window.frame_missed.get() {
|
||||
self.window.schedule_render();
|
||||
}
|
||||
}
|
||||
|
||||
fn failed(&self) {
|
||||
self.window.kill(true);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue