1
0
Fork 0
forked from wry/wry

autocommit 2022-02-01 23:33:59 CET

This commit is contained in:
Julian Orth 2022-02-01 23:33:59 +01:00
parent 41c5f8cf89
commit 2dbe3ba732
21 changed files with 814 additions and 86 deletions

1
Cargo.lock generated
View file

@ -268,6 +268,7 @@ dependencies = [
"backtrace",
"bitflags",
"bstr",
"byteorder",
"env_logger",
"futures",
"isnt",

View file

@ -29,6 +29,7 @@ rand = "0.8.4"
renderdoc = "0.10.1"
smallvec = { version = "1.8.0", features = ["const_generics", "const_new", "union"] }
backtrace = "0.3.64"
byteorder = "1.4.3"
[build-dependencies]
repc = "0.1.1"

View file

@ -303,7 +303,7 @@ impl XorgBackend {
slf.query_devices(ffi::XCB_INPUT_DEVICE_ALL_MASTER as _)?;
slf.handle_events()?;
state.render_ctx.set(Some(ctx.clone()));
state.set_render_ctx(&ctx);
Ok(slf)
}

489
src/cursor.rs Normal file
View file

@ -0,0 +1,489 @@
use crate::rect::Rect;
use crate::render::{RenderContext, Renderer, Texture};
use crate::{ErrorFmt, NumCell, RenderError};
use ahash::AHashSet;
use bstr::{BStr, BString, ByteSlice, ByteVec};
use byteorder::{LittleEndian, ReadBytesExt};
use isnt::std_1::primitive::IsntSliceExt;
use std::cell::Cell;
use std::convert::TryInto;
use std::fmt::{Debug, Formatter};
use std::fs::File;
use std::io::{BufRead, BufReader, Seek, SeekFrom};
use std::rc::Rc;
use std::{env, io, slice, str};
use std::mem::MaybeUninit;
use thiserror::Error;
use uapi::c;
use crate::format::ARGB8888;
const XCURSOR_MAGIC: u32 = 0x72756358;
const XCURSOR_IMAGE_TYPE: u32 = 0xfffd0002;
const XCURSOR_PATH_DEFAULT: &[u8] =
b"~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons";
const XCURSOR_PATH: &str = "XCURSOR_PATH";
const HOME: &str = "HOME";
const HEADER_SIZE: u32 = 16;
pub trait Cursor {
fn set_position(&self, x: i32, y: i32);
fn render(&self, renderer: &mut Renderer, x: i32, y: i32);
fn extents(&self) -> Rect;
fn handle_unset(&self) { }
fn tick(&self) { }
}
pub struct ServerCursors {
pub default: ServerCursorTemplate,
pub resize_right: ServerCursorTemplate,
pub resize_left: ServerCursorTemplate,
pub resize_top: ServerCursorTemplate,
pub resize_bottom: ServerCursorTemplate,
pub resize_top_bottom: ServerCursorTemplate,
pub resize_left_right: ServerCursorTemplate,
pub resize_top_left: ServerCursorTemplate,
pub resize_top_right: ServerCursorTemplate,
pub resize_bottom_left: ServerCursorTemplate,
pub resize_bottom_right: ServerCursorTemplate,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum KnownCursor {
Default,
}
impl ServerCursors {
pub fn load(ctx: &Rc<RenderContext>) -> Result<Self, CursorError> {
let paths = find_cursor_paths();
log::debug!("Trying to load cursors from paths {:?}", paths);
let load = |name: &str| {
ServerCursorTemplate::load(name, None, 16, &paths, ctx)
};
Ok(Self {
// default: load("left_ptr")?,
default: load("left_ptr_watch")?,
resize_right: load("right_side")?,
resize_left: load("left_side")?,
resize_top: load("top_side")?,
resize_bottom: load("bottom_side")?,
resize_top_bottom: load("v_double_arrow")?,
resize_left_right: load("h_double_arrow")?,
resize_top_left: load("top_left_corner")?,
resize_top_right: load("top_right_corner")?,
resize_bottom_left: load("top_left_corner")?,
resize_bottom_right: load("bottom_right_corner")?,
})
}
}
pub struct ServerCursorTemplate {
var: ServerCursorTemplateVariant,
}
enum ServerCursorTemplateVariant {
Static(Rc<CursorImage>),
Animated(Rc<Vec<CursorImage>>),
}
impl ServerCursorTemplate {
fn load(
name: &str,
theme: Option<&BStr>,
size: u32,
paths: &[BString],
ctx: &Rc<RenderContext>,
) -> Result<Self, CursorError> {
match open_cursor(name, theme, size, paths) {
Ok(c) => {
if c.len() == 1 {
let c = &c[0];
let cursor = CursorImage::from_bytes(ctx, &c.pixels, 0, c.width, c.height, c.xhot, c.yhot)?;
Ok(ServerCursorTemplate {
var: ServerCursorTemplateVariant::Static(Rc::new(cursor)),
})
} else {
let mut images = vec!();
for c in c {
let img = CursorImage::from_bytes(ctx, &c.pixels, c.delay as _, c.width, c.height, c.xhot, c.yhot)?;
images.push(img);
}
Ok(ServerCursorTemplate {
var: ServerCursorTemplateVariant::Animated(Rc::new(images)),
})
}
}
Err(e) => {
log::warn!("Could not load cursor {}: {}", name, ErrorFmt(e));
let empty: [Cell<u8>; 4] = unsafe { MaybeUninit::zeroed().assume_init() };
let cursor = CursorImage::from_bytes(ctx, &empty, 0, 1, 1, 0, 0)?;
Ok(ServerCursorTemplate {
var: ServerCursorTemplateVariant::Static(Rc::new(cursor)),
})
}
}
}
pub fn instantiate(&self) -> Rc<dyn Cursor> {
match &self.var {
ServerCursorTemplateVariant::Static(s) => {
Rc::new(StaticCursor {
x: Cell::new(0),
y: Cell::new(0),
extents: Cell::new(s.extents),
image: s.clone(),
})
},
ServerCursorTemplateVariant::Animated(a) => {
let mut start = c::timespec {
tv_sec: 0,
tv_nsec: 0,
};
uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut start).unwrap();
Rc::new(AnimatedCursor {
start,
next: NumCell::new(a[0].delay_ns),
idx: Cell::new(0),
images: a.clone(),
x: Cell::new(0),
y: Cell::new(0),
extents: Cell::new(a[0].extents),
})
}
}
}
}
struct CursorImage {
extents: Rect,
xhot: i32,
yhot: i32,
delay_ns: u64,
tex: Rc<Texture>,
}
impl CursorImage {
fn from_bytes(ctx: &Rc<RenderContext>, data: &[Cell<u8>], delay_ms: u64, width: i32, height: i32, xhot: i32, yhot: i32) -> Result<Self, CursorError> {
Ok(Self {
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
xhot,
yhot,
delay_ns: delay_ms * 1_000_000,
tex: ctx.shmem_texture(data, ARGB8888, width, height, width * 4)?,
})
}
}
struct StaticCursor {
x: Cell<i32>,
y: Cell<i32>,
extents: Cell<Rect>,
image: Rc<CursorImage>,
}
impl Cursor for StaticCursor {
fn set_position(&self, x: i32, y: i32) {
let dx = x - self.x.replace(x);
let dy = y - self.y.replace(y);
self.extents.set(self.extents.get().move_(dx, dy));
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_texture(&self.image.tex, x, y, ARGB8888);
}
fn extents(&self) -> Rect {
self.extents.get()
}
}
struct AnimatedCursor {
start: c::timespec,
next: NumCell<u64>,
idx: Cell<usize>,
images: Rc<Vec<CursorImage>>,
x: Cell<i32>,
y: Cell<i32>,
extents: Cell<Rect>,
}
impl Cursor for AnimatedCursor {
fn set_position(&self, x: i32, y: i32) {
let dx = x - self.x.replace(x);
let dy = y - self.y.replace(y);
self.extents.set(self.extents.get().move_(dx, dy));
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
let img = &self.images[self.idx.get()];
renderer.render_texture(&img.tex, x, y, ARGB8888);
}
fn extents(&self) -> Rect {
self.extents.get()
}
fn tick(&self) {
let mut now = c::timespec {
tv_sec: 0,
tv_nsec: 0,
};
uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut now).unwrap();
let dist = (now.tv_sec.wrapping_sub(self.start.tv_sec)) as i64 * 1_000_000_000
+ now.tv_nsec.wrapping_sub(self.start.tv_nsec) as i64;
if (dist as u64) < self.next.get() {
return;
}
let idx = (self.idx.get() + 1) % self.images.len();
self.idx.set(idx);
let image = &self.images[idx];
self.extents.set(
Rect::new_sized(
self.x.get() - image.xhot,
self.y.get() - image.yhot,
image.extents.width(),
image.extents.height(),
)
.unwrap(),
);
self.next.fetch_add(image.delay_ns);
}
}
fn open_cursor(
name: &str,
theme: Option<&BStr>,
size: u32,
paths: &[BString],
) -> Result<Vec<XCursorImage>, CursorError> {
let name = name.as_bytes().as_bstr();
let mut file = None;
let mut themes_tested = AHashSet::new();
if let Some(theme) = theme {
file = open_cursor_file(&mut themes_tested, paths, theme, name);
}
if file.is_none() {
file = open_cursor_file(&mut themes_tested, paths, b"default".as_bstr(), name);
}
let file = match file {
Some(f) => f,
_ => return Err(CursorError::NotFound),
};
let mut file = BufReader::new(file);
let images = parser_cursor_file(&mut file, size)?;
if images.is_empty() {
return Err(CursorError::EmptyXcursorFile);
}
Ok(images)
}
fn open_cursor_file(
themes_tested: &mut AHashSet<BString>,
paths: &[BString],
theme: &BStr,
name: &BStr,
) -> Option<File> {
if !themes_tested.insert(theme.to_owned()) {
return None;
}
if paths.is_empty() {
return None;
}
let mut parents = None;
for cursor_path in paths {
let mut theme_dir = cursor_path.to_vec();
theme_dir.push(b'/');
theme_dir.extend_from_slice(theme.as_bytes());
let mut cursor_file = theme_dir.clone();
cursor_file.extend_from_slice(b"/cursors/");
cursor_file.extend_from_slice(name.as_bytes());
if let Ok(f) = File::open(cursor_file.to_os_str().unwrap()) {
return Some(f);
}
if parents.is_none() {
let mut index_file = theme_dir.clone();
index_file.extend_from_slice(b"/index.theme");
parents = find_parent_themes(&index_file);
}
}
if let Some(parents) = parents {
for parent in parents {
if let Some(file) = open_cursor_file(themes_tested, paths, parent.as_bstr(), name) {
return Some(file);
}
}
}
None
}
fn find_cursor_paths() -> Vec<BString> {
let home = env::var_os(HOME).map(|h| Vec::from_os_string(h).unwrap());
let cursor_paths = env::var_os(XCURSOR_PATH);
let cursor_paths = cursor_paths
.as_ref()
.map(|c| <[u8]>::from_os_str(c).unwrap())
.unwrap_or(XCURSOR_PATH_DEFAULT);
let mut paths = vec![];
for path in <[u8]>::split(cursor_paths, |b| *b == b':') {
if path.first() == Some(&b'~') {
if let Some(home) = home.as_ref() {
let mut full_path = home.clone();
full_path.extend_from_slice(&path[1..]);
paths.push(full_path.into());
} else {
log::warn!(
"`HOME` is not set. Cannot expand {}. Ignoring.",
path.as_bstr()
);
}
} else {
paths.push(path.as_bstr().to_owned());
}
}
paths
}
fn find_parent_themes(path: &[u8]) -> Option<Vec<BString>> {
// NOTE: The files we're reading here are really INI files with a hierarchy. This
// algorithm treats it as a flat list and is inherited from libxcursor.
let file = match File::open(path.to_os_str().unwrap()) {
Ok(f) => f,
_ => return None,
};
let mut buf_reader = BufReader::new(file);
let mut buf = vec![];
loop {
buf.clear();
match buf_reader.read_until(b'\n', &mut buf) {
Ok(n) if n > 0 => {}
_ => return None,
}
let mut suffix = match buf.strip_prefix(b"Inherits") {
Some(s) => s,
_ => continue,
};
while suffix.first() == Some(&b' ') {
suffix = &suffix[1..];
}
if suffix.first() != Some(&b'=') {
continue;
}
suffix = &suffix[1..];
let parents = suffix
.split(|b| matches!(*b, b' ' | b'\t' | b'\n' | b';' | b','))
.filter(|v| v.is_not_empty())
.map(|v| v.as_bstr().to_owned())
.collect();
return Some(parents);
}
}
#[derive(Debug, Error)]
pub enum CursorError {
#[error("An IO error occurred: {0}")]
Io(#[from] io::Error),
#[error("The file is not an Xcursor file")]
NotAnXcursorFile,
#[error("The Xcursor file contains more than 0x10000 images")]
OversizedXcursorFile,
#[error("The Xcursor file is empty")]
EmptyXcursorFile,
#[error("The Xcursor file is corrupt")]
CorruptXcursorFile,
#[error("The requested cursor could not be found")]
NotFound,
#[error("Could not import the cursor as a texture")]
ImportError(#[from] RenderError),
}
#[derive(Default, Clone)]
struct XCursorImage {
width: i32,
height: i32,
xhot: i32,
yhot: i32,
delay: u32,
pixels: Vec<Cell<u8>>,
}
impl Debug for XCursorImage {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("XcbCursorImage")
.field("width", &self.width)
.field("height", &self.height)
.field("xhot", &self.xhot)
.field("yhot", &self.yhot)
.field("delay", &self.delay)
.finish_non_exhaustive()
}
}
fn parser_cursor_file<R: BufRead + Seek>(
r: &mut R,
target: u32,
) -> Result<Vec<XCursorImage>, CursorError> {
let [magic, header] = read_u32_n(r)?;
if magic != XCURSOR_MAGIC || header < HEADER_SIZE {
return Err(CursorError::NotAnXcursorFile);
}
let [_version, ntoc] = read_u32_n(r)?;
r.seek(SeekFrom::Current((HEADER_SIZE - header) as i64))?;
if ntoc > 0x10000 {
return Err(CursorError::OversizedXcursorFile);
}
let mut images_positions = vec![];
let mut best_fit = i64::MAX;
for _ in 0..ntoc {
let [type_, size, position] = read_u32_n(r)?;
if type_ != XCURSOR_IMAGE_TYPE {
continue;
}
let fit = (size as i64 - target as i64).abs();
if fit < best_fit {
best_fit = fit;
images_positions.clear();
}
if fit == best_fit {
images_positions.push(position);
}
}
let mut images = Vec::with_capacity(images_positions.len());
for position in images_positions {
r.seek(SeekFrom::Start(position as u64))?;
let [_chunk_header, _type_, _size, _version, width, height, xhot, yhot, delay] =
read_u32_n(r)?;
let [width, height, xhot, yhot] = u32_to_i32([width, height, xhot, yhot])?;
let mut image = XCursorImage {
width,
height,
xhot,
yhot,
delay,
pixels: vec![],
};
let num_bytes = width as usize * height as usize * 4;
unsafe {
image.pixels.reserve_exact(num_bytes as usize);
image.pixels.set_len(num_bytes as usize);
r.read_exact(slice::from_raw_parts_mut(image.pixels.as_mut_ptr() as _, num_bytes))?;
}
images.push(image);
}
Ok(images)
}
fn read_u32_n<R: BufRead, const N: usize>(r: &mut R) -> Result<[u32; N], io::Error> {
let mut res = [0; N];
r.read_u32_into::<LittleEndian>(&mut res)?;
Ok(res)
}
fn u32_to_i32<const N: usize>(n: [u32; N]) -> Result<[i32; N], CursorError> {
let mut res = [0; N];
for i in 0..N {
res[i] = n[i]
.try_into()
.map_err(|_| CursorError::CorruptXcursorFile)?;
}
Ok(res)
}

View file

@ -350,6 +350,9 @@ impl WlSeatGlobal {
.enter(self, x.apply_fract(new.x), y.apply_fract(new.y));
stack.push(new.node);
}
if let Some(node) = stack.last() {
node.pointer_target(self);
}
}
found_tree.clear();
}
@ -397,7 +400,7 @@ impl WlSeatGlobal {
self.focus_toplevel(n);
}
pub fn enter_popup(self: &Rc<Self>, n: &Rc<XdgPopup>) {
pub fn enter_popup(self: &Rc<Self>, _n: &Rc<XdgPopup>) {
// self.focus_xdg_surface(&n.xdg);
}

View file

@ -12,7 +12,6 @@ use crate::ifs::wl_data_device::{WlDataDevice, WlDataDeviceId};
use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardId, REPEAT_INFO_SINCE};
use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerId};
use crate::ifs::wl_seat::wl_touch::WlTouch;
use crate::ifs::wl_surface::cursor::CursorSurface;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
use crate::object::{Interface, Object, ObjectId};
use crate::tree::{FloatNode, FoundNode, Node};
@ -31,6 +30,7 @@ use std::io::Write;
use std::rc::Rc;
pub use types::*;
use uapi::{c, OwnedFd};
use crate::cursor::{Cursor, KnownCursor};
id!(WlSeatId);
@ -74,7 +74,7 @@ pub struct WlSeatGlobal {
kb_state: RefCell<XkbState>,
layout: Rc<OwnedFd>,
layout_size: u32,
cursor: CloneCell<Option<Rc<CursorSurface>>>,
cursor: CloneCell<Option<Rc<dyn Cursor>>>,
serial: NumCell<u32>,
}
@ -121,7 +121,21 @@ impl WlSeatGlobal {
}
}
pub fn set_cursor(&self, cursor: Option<Rc<CursorSurface>>) {
pub fn set_known_cursor(&self, cursor: KnownCursor) {
let cursors = match self.state.cursors.get() {
Some(c) => c,
None => {
self.set_cursor(None);
return;
}
};
let tpl = match cursor {
KnownCursor::Default => &cursors.default,
};
self.set_cursor(Some(tpl.instantiate()));
}
pub fn set_cursor(&self, cursor: Option<Rc<dyn Cursor>>) {
if let Some(old) = self.cursor.get() {
if let Some(new) = cursor.as_ref() {
if Rc::ptr_eq(&old, new) {
@ -130,10 +144,15 @@ impl WlSeatGlobal {
}
old.handle_unset();
}
if let Some(cursor) = cursor.as_ref() {
log::info!("setting new cursor");
let (x, y) = self.pos.get();
cursor.set_position(x.round_down(), y.round_down());
}
self.cursor.set(cursor);
}
pub fn get_cursor(&self) -> Option<Rc<CursorSurface>> {
pub fn get_cursor(&self) -> Option<Rc<dyn Cursor>> {
self.cursor.get()
}

View file

@ -8,6 +8,7 @@ use crate::object::{Interface, Object, ObjectId};
use crate::utils::buffd::MsgParser;
use std::rc::Rc;
pub use types::*;
use crate::cursor::Cursor;
const SET_CURSOR: u32 = 0;
const RELEASE: u32 = 1;
@ -153,7 +154,7 @@ impl WlPointer {
let surface = self.seat.client.get_surface(req.surface)?;
let cursor = surface.get_cursor(&self.seat.global)?;
cursor.set_hotspot(req.hotspot_x, req.hotspot_y);
cursor_opt = Some(cursor);
cursor_opt = Some(cursor as Rc<dyn Cursor>);
}
let pointer_node = match self.seat.global.pointer_stack.borrow().last().cloned() {
Some(n) => n,

View file

@ -1,8 +1,10 @@
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wl_surface::WlSurface;
use crate::rect::Rect;
use crate::render::Renderer;
use std::cell::Cell;
use std::rc::Rc;
use crate::cursor::Cursor;
pub struct CursorSurface {
seat: Rc<WlSeatGlobal>,
@ -38,15 +40,6 @@ impl CursorSurface {
);
}
pub fn set_position(&self, x: i32, y: i32) {
self.pos.set((x, y));
self.update_extents();
}
pub fn handle_unset(&self) {
self.surface.cursors.remove(&self.seat.id());
}
pub fn handle_surface_destroy(&self) {
self.seat.set_cursor(None);
}
@ -71,12 +64,23 @@ impl CursorSurface {
self.hotspot.set((hot_x - hotspot_dx, hot_y - hotspot_dy));
self.update_extents();
}
}
pub fn surface(&self) -> &Rc<WlSurface> {
&self.surface
impl Cursor for CursorSurface {
fn set_position(&self, x: i32, y: i32) {
self.pos.set((x, y));
self.update_extents();
}
pub fn extents(&self) -> Rect {
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_surface(&self.surface, x, y);
}
fn extents(&self) -> Rect {
self.extents.get()
}
fn handle_unset(&self) {
self.surface.cursors.remove(&self.seat.id());
}
}

View file

@ -206,6 +206,7 @@ impl WlSurface {
}
self.set_role(SurfaceRole::Cursor)?;
let cursor = Rc::new(CursorSurface::new(seat, self));
cursor.handle_buffer_change();
self.cursors.insert(seat.id(), cursor.clone());
Ok(cursor)
}

View file

@ -4,7 +4,7 @@ use crate::client::{ClientId, DynEventFormatter};
use crate::fixed::Fixed;
use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal};
use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt};
use crate::ifs::xdg_positioner::{XdgPositioned, XdgPositioner};
use crate::ifs::xdg_positioner::{XdgPositioned, XdgPositioner, CA};
use crate::object::{Interface, Object, ObjectId};
use crate::rect::Rect;
use crate::render::Renderer;
@ -15,6 +15,7 @@ use crate::utils::linkedlist::LinkedNode;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
pub use types::*;
use crate::cursor::KnownCursor;
const DESTROY: u32 = 0;
const GRAB: u32 = 1;
@ -85,13 +86,94 @@ impl XdgPopup {
Box::new(PopupDone { obj: self.clone() })
}
fn update_relative_position(&self, parent: &XdgSurface) -> Result<(), XdgPopupError> {
let parent = parent.extents.get();
let positioner = self.pos.borrow();
if !parent.contains_rect(&positioner.ar) {
// return Err(XdgPopupError::AnchorRectOutside);
fn update_position(&self, parent: &XdgSurface) -> Result<(), XdgPopupError> {
// let parent = parent.extents.get();
let positioner = self.pos.borrow_mut();
// if !parent.contains_rect(&positioner.ar) {
// return Err(XdgPopupError::AnchorRectOutside);
// }
let parent_abs = parent.absolute_desired_extents.get();
let mut rel_pos = positioner.get_position(false, false);
let mut abs_pos = rel_pos.move_(parent_abs.x1(), parent_abs.y1());
if let Some(ws) = parent.workspace.get() {
let output_pos = ws.output.get().position.get();
let mut overflow = output_pos.get_overflow(&abs_pos);
if !overflow.is_contained() {
let mut flip_x = positioner.ca.contains(CA::FLIP_X) && overflow.x_overflow();
let mut flip_y = positioner.ca.contains(CA::FLIP_Y) && overflow.y_overflow();
if flip_x || flip_y {
let mut adj_rel = positioner.get_position(flip_x, flip_y);
let mut adj_abs = adj_rel.move_(parent_abs.x1(), parent_abs.y1());
let mut adj_overflow = output_pos.get_overflow(&adj_abs);
let mut recalculate = false;
if flip_x && adj_overflow.x_overflow() {
flip_x = false;
recalculate = true;
}
if flip_y && adj_overflow.y_overflow() {
flip_y = false;
recalculate = true;
}
if flip_x || flip_y {
if recalculate {
adj_rel = positioner.get_position(flip_x, flip_y);
adj_abs = adj_rel.move_(parent_abs.x1(), parent_abs.y1());
adj_overflow = output_pos.get_overflow(&adj_abs);
}
rel_pos = adj_rel;
abs_pos = adj_abs;
overflow = adj_overflow;
}
}
let (mut dx, mut dy) = (0, 0);
if positioner.ca.contains(CA::SLIDE_X) && overflow.x_overflow() {
dx = if overflow.left > 0 || overflow.left + overflow.right > 0 {
parent_abs.x1() - abs_pos.x1()
} else {
parent_abs.x2() - abs_pos.x2()
};
}
if positioner.ca.contains(CA::SLIDE_Y) && overflow.y_overflow() {
dy = if overflow.top > 0 || overflow.top + overflow.bottom > 0 {
parent_abs.y1() - abs_pos.y1()
} else {
parent_abs.y2() - abs_pos.y2()
};
}
if dx != 0 || dy != 0 {
rel_pos = rel_pos.move_(dx, dy);
abs_pos = rel_pos.move_(parent_abs.x1(), parent_abs.y1());
overflow = output_pos.get_overflow(&abs_pos);
}
let (mut dx1, mut dx2, mut dy1, mut dy2) = (0, 0, 0, 0);
if positioner.ca.contains(CA::RESIZE_X) {
dx1 = overflow.left.max(0);
dx2 = -overflow.right.max(0);
}
if positioner.ca.contains(CA::RESIZE_Y) {
dy1 = overflow.top.max(0);
dy2 = -overflow.bottom.max(0);
}
if dx1 > 0 || dx2 < 0 || dy1 > 0 || dy2 < 0 {
abs_pos = Rect::new(
abs_pos.x1() + dx1,
abs_pos.y1() + dy1,
abs_pos.x2() + dx2,
abs_pos.y2() + dy2,
)
.unwrap();
rel_pos = Rect::new_sized(
abs_pos.x1() - parent_abs.x1(),
abs_pos.y1() - parent_abs.y1(),
abs_pos.width(),
abs_pos.height(),
)
.unwrap();
}
}
}
self.relative_position.set(positioner.get_position());
self.relative_position.set(rel_pos);
self.xdg.absolute_desired_extents.set(abs_pos);
Ok(())
}
@ -134,7 +216,7 @@ impl XdgPopup {
.get_xdg_positioner(req.positioner)?
.value();
if let Some(parent) = self.parent.get() {
self.update_relative_position(&parent)?;
self.update_position(&parent)?;
let rel = self.relative_position.get();
self.xdg.surface.client.event(self.repositioned(req.token));
self.xdg.surface.client.event(self.configure(
@ -144,24 +226,6 @@ impl XdgPopup {
rel.height(),
));
self.xdg.send_configure();
let parent = parent.absolute_desired_extents.get();
self.xdg
.absolute_desired_extents
.set(rel.move_(parent.x1(), parent.y1()));
}
Ok(())
}
fn handle_request_(
self: &Rc<Self>,
request: u32,
parser: MsgParser<'_, '_>,
) -> Result<(), XdgPopupError> {
match request {
DESTROY => self.destroy(parser)?,
GRAB => self.grab(parser)?,
REPOSITION => self.reposition(parser)?,
_ => unreachable!(),
}
Ok(())
}
@ -171,7 +235,13 @@ impl XdgPopup {
}
}
handle_request!(XdgPopup);
handle_request! {
XdgPopup, XdgPopupError;
DESTROY => destroy,
GRAB => grab,
REPOSITION => reposition,
}
impl Object for XdgPopup {
fn id(&self) -> ObjectId {
@ -183,7 +253,11 @@ impl Object for XdgPopup {
}
fn num_requests(&self) -> u32 {
REPOSITION + 1
let last_req = match self.xdg.base.version {
0..=2 => GRAB,
_ => REPOSITION,
};
last_req + 1
}
fn break_loops(&self) {
@ -228,6 +302,10 @@ impl Node for XdgPopup {
seat.enter_popup(&self);
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_xdg_surface(&self.xdg, x, y)
}
@ -244,7 +322,7 @@ impl Node for XdgPopup {
impl XdgSurfaceExt for XdgPopup {
fn initial_configure(self: Rc<Self>) -> Result<(), XdgSurfaceError> {
if let Some(parent) = self.parent.get() {
self.update_relative_position(&parent)?;
self.update_position(&parent)?;
let rel = self.relative_position.get();
self.xdg.surface.client.event(self.configure(
rel.x1(),
@ -252,10 +330,6 @@ impl XdgSurfaceExt for XdgPopup {
rel.width(),
rel.height(),
));
let parent = parent.absolute_desired_extents.get();
self.xdg
.absolute_desired_extents
.set(rel.move_(parent.x1(), parent.y1()));
}
Ok(())
}

View file

@ -21,6 +21,7 @@ use std::cell::{Cell, RefCell};
use std::mem;
use std::rc::Rc;
pub use types::*;
use crate::cursor::KnownCursor;
const DESTROY: u32 = 0;
const SET_PARENT: u32 = 1;
@ -395,6 +396,10 @@ impl Node for XdgToplevel {
seat.enter_toplevel(&self);
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_xdg_surface(&self.xdg, x, y)
}

View file

@ -35,7 +35,7 @@ const BOTTOM_RIGHT: u32 = 8;
bitflags::bitflags! {
#[derive(Default)]
pub struct Square: u32 {
pub struct Edge: u32 {
const TOP = 1 << 0;
const BOTTOM = 1 << 1;
const LEFT = 1 << 2;
@ -43,18 +43,18 @@ bitflags::bitflags! {
}
}
impl Square {
impl Edge {
fn from_enum(e: u32) -> Option<Self> {
let s = match e {
NONE => Square::empty(),
TOP => Square::TOP,
BOTTOM => Square::BOTTOM,
LEFT => Square::LEFT,
RIGHT => Square::RIGHT,
TOP_LEFT => Square::TOP | Square::LEFT,
BOTTOM_LEFT => Square::BOTTOM | Square::LEFT,
TOP_RIGHT => Square::TOP | Square::RIGHT,
BOTTOM_RIGHT => Square::BOTTOM | Square::RIGHT,
NONE => Edge::empty(),
TOP => Edge::TOP,
BOTTOM => Edge::BOTTOM,
LEFT => Edge::LEFT,
RIGHT => Edge::RIGHT,
TOP_LEFT => Edge::TOP | Edge::LEFT,
BOTTOM_LEFT => Edge::BOTTOM | Edge::LEFT,
TOP_RIGHT => Edge::TOP | Edge::RIGHT,
BOTTOM_RIGHT => Edge::BOTTOM | Edge::RIGHT,
_ => return None,
};
Some(s)
@ -88,8 +88,8 @@ pub struct XdgPositioned {
pub size_width: i32,
pub size_height: i32,
pub ar: Rect,
pub anchor: Square,
pub gravity: Square,
pub anchor: Edge,
pub gravity: Edge,
pub ca: CA,
pub off_x: i32,
pub off_y: i32,
@ -104,35 +104,46 @@ impl XdgPositioned {
self.size_height != 0 && self.size_width != 0
}
pub fn get_position(&self) -> Rect {
pub fn get_position(&self, flip_x: bool, flip_y: bool) -> Rect {
let mut anchor = self.anchor;
let mut gravity = self.gravity;
if flip_x {
anchor ^= Edge::LEFT | Edge::RIGHT;
gravity ^= Edge::LEFT | Edge::RIGHT;
}
if flip_y {
anchor ^= Edge::TOP | Edge::BOTTOM;
gravity ^= Edge::TOP | Edge::BOTTOM;
}
let mut x1 = self.off_x;
let mut y1 = self.off_x;
if self.anchor.contains(Square::LEFT) {
if anchor.contains(Edge::LEFT) {
x1 += self.ar.x1();
} else if self.anchor.contains(Square::RIGHT) {
} else if anchor.contains(Edge::RIGHT) {
x1 += self.ar.x2();
} else {
x1 += self.ar.x1() + self.ar.width() / 2;
}
if self.anchor.contains(Square::TOP) {
if anchor.contains(Edge::TOP) {
y1 += self.ar.y1();
} else if self.anchor.contains(Square::BOTTOM) {
} else if anchor.contains(Edge::BOTTOM) {
y1 += self.ar.y2();
} else {
y1 += self.ar.y1() + self.ar.height() / 2;
}
if self.gravity.contains(Square::LEFT) {
if gravity.contains(Edge::LEFT) {
x1 -= self.size_width;
} else if !self.gravity.contains(Square::RIGHT) {
} else if !gravity.contains(Edge::RIGHT) {
x1 -= self.size_width / 2;
}
if self.gravity.contains(Square::TOP) {
if gravity.contains(Edge::TOP) {
y1 -= self.size_height;
} else if !self.gravity.contains(Square::BOTTOM) {
} else if !gravity.contains(Edge::BOTTOM) {
y1 -= self.size_height / 2;
}
@ -193,7 +204,7 @@ impl XdgPositioner {
fn set_anchor(&self, parser: MsgParser<'_, '_>) -> Result<(), SetAnchorError> {
let req: SetAnchor = self.client.parse(self, parser)?;
let anchor = match Square::from_enum(req.anchor) {
let anchor = match Edge::from_enum(req.anchor) {
Some(a) => a,
_ => return Err(SetAnchorError::UnknownAnchor(req.anchor)),
};
@ -203,7 +214,7 @@ impl XdgPositioner {
fn set_gravity(&self, parser: MsgParser<'_, '_>) -> Result<(), SetGravityError> {
let req: SetGravity = self.client.parse(self, parser)?;
let gravity = match Square::from_enum(req.gravity) {
let gravity = match Edge::from_enum(req.gravity) {
Some(a) => a,
_ => return Err(SetGravityError::UnknownGravity(req.gravity)),
};

View file

@ -24,6 +24,31 @@ macro_rules! handle_request {
}
}
};
($oname:ty, $ename:ty; $($code:ident => $f:ident,)*) => {
impl crate::object::ObjectHandleRequest for $oname {
fn handle_request(
self: std::rc::Rc<Self>,
request: u32,
parser: crate::utils::buffd::MsgParser<'_, '_>,
) -> Result<(), crate::client::ClientError> {
fn handle_request(
slf: std::rc::Rc<$oname>,
request: u32,
parser: crate::utils::buffd::MsgParser<'_, '_>,
) -> Result<(), $ename> {
match request {
$(
$code => slf.$f(parser)?,
)*
_ => unreachable!(),
}
Ok(())
}
handle_request(self, request, parser)?;
Ok(())
}
}
};
}
macro_rules! bind {

View file

@ -53,6 +53,7 @@ mod backend;
mod backends;
mod client;
mod clientmem;
mod cursor;
mod drm;
mod event_loop;
mod fixed;
@ -116,6 +117,7 @@ fn main_() -> Result<(), MainError> {
eng: engine.clone(),
el: el.clone(),
render_ctx: Default::default(),
cursors: Default::default(),
wheel,
clients: Clients::new(),
next_name: NumCell::new(1),

View file

@ -1,4 +1,6 @@
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
use std::fmt::{Debug, Formatter};
#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub struct Rect {
x1: i32,
y1: i32,
@ -6,6 +8,41 @@ pub struct Rect {
y2: i32,
}
impl Debug for Rect {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Rect")
.field("x1", &self.x1)
.field("y1", &self.y1)
.field("x2", &self.x2)
.field("y2", &self.y2)
.field("width", &(self.x2 - self.x1))
.field("height", &(self.y2 - self.y1))
.finish()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub struct RectOverflow {
pub left: i32,
pub right: i32,
pub top: i32,
pub bottom: i32,
}
impl RectOverflow {
pub fn is_contained(&self) -> bool {
self.left <= 0 && self.right <= 0 && self.top <= 0 && self.bottom <= 0
}
pub fn x_overflow(&self) -> bool {
self.left > 0 || self.right > 0
}
pub fn y_overflow(&self) -> bool {
self.top > 0 || self.bottom > 0
}
}
impl Rect {
pub fn new_empty(x: i32, y: i32) -> Self {
Self {
@ -64,6 +101,15 @@ impl Rect {
self.x1 <= rect.x1 && self.y1 <= rect.x1 && rect.x2 <= self.x2 && rect.y2 <= self.y2
}
pub fn get_overflow(&self, child: &Self) -> RectOverflow {
RectOverflow {
left: self.x1 - child.x1,
right: child.x2 - self.x2,
top: self.y1 - child.y1,
bottom: child.y2 - self.y2,
}
}
pub fn is_empty(&self) -> bool {
self.x1 == self.x2 || self.y1 == self.y2
}

View file

@ -39,10 +39,11 @@ impl Framebuffer {
let seats = state.globals.lock_seats();
for seat in seats.values() {
if let Some(cursor) = seat.get_cursor() {
cursor.tick();
let extents = cursor.extents();
if extents.intersects(&rect) {
let (x, y) = rect.translate(extents.x1(), extents.y1());
renderer.render_surface(cursor.surface(), x, y);
cursor.render(&mut renderer, x, y);
}
}
}

View file

@ -18,6 +18,8 @@ use crate::tree::{
use std::ops::Deref;
use std::rc::Rc;
use std::slice;
use crate::format::Format;
use crate::render::Texture;
const NON_COLOR: (f32, f32, f32) = (0.2, 0.2, 0.2);
const CHILD_COLOR: (f32, f32, f32) = (0.8, 0.8, 0.8);
@ -218,10 +220,12 @@ impl Renderer<'_> {
}
pub fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32) {
let texture = match buffer.texture.get() {
Some(t) => t,
_ => return,
};
if let Some(tex) = buffer.texture.get() {
self.render_texture(&tex, x, y, buffer.format);
}
}
pub fn render_texture(&mut self, texture: &Texture, x: i32, y: i32, format: &Format) {
assert!(Rc::ptr_eq(&self.ctx.ctx, &texture.ctx.ctx));
unsafe {
glActiveTexture(GL_TEXTURE0);
@ -229,7 +233,7 @@ impl Renderer<'_> {
glBindTexture(GL_TEXTURE_2D, texture.gl.tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
let prog = match buffer.format.has_alpha {
let prog = match format.has_alpha {
true => {
glEnable(GL_BLEND);
&self.ctx.tex_alpha_prog

View file

@ -14,15 +14,17 @@ use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::linkedlist::LinkedList;
use crate::utils::numcell::NumCell;
use crate::utils::queue::AsyncQueue;
use crate::Wheel;
use crate::{ErrorFmt, Wheel};
use ahash::AHashMap;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use crate::cursor::ServerCursors;
pub struct State {
pub eng: Rc<AsyncEngine>,
pub el: Rc<EventLoop>,
pub render_ctx: CloneCell<Option<Rc<RenderContext>>>,
pub cursors: CloneCell<Option<Rc<ServerCursors>>>,
pub wheel: Rc<Wheel>,
pub clients: Clients,
pub next_name: NumCell<u32>,
@ -47,6 +49,18 @@ pub struct SeatData {
}
impl State {
pub fn set_render_ctx(&self, ctx: &Rc<RenderContext>) {
let cursors = match ServerCursors::load(ctx) {
Ok(c) => Some(Rc::new(c)),
Err(e) => {
log::error!("Could not load the cursors: {}", ErrorFmt(e));
None
}
};
self.cursors.set(cursors);
self.render_ctx.set(Some(ctx.clone()));
}
pub fn add_global<T>(&self, global: &Rc<T>)
where
Globals: AddGlobal<T>,

View file

@ -1,4 +1,4 @@
use crate::ifs::wl_seat::NodeSeatState;
use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal};
use crate::rect::Rect;
use crate::render::Renderer;
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
@ -8,6 +8,7 @@ use crate::{NumCell, State};
use ahash::AHashMap;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use crate::cursor::KnownCursor;
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -354,6 +355,10 @@ impl Node for ContainerNode {
}
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_container(self, x, y);
}

View file

@ -16,6 +16,7 @@ use std::fmt::Display;
use std::ops::Deref;
use std::rc::Rc;
pub use workspace::*;
use crate::cursor::KnownCursor;
mod container;
mod workspace;
@ -126,6 +127,10 @@ pub trait Node {
let _ = y;
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
let _ = seat;
}
fn motion(&self, seat: &WlSeatGlobal, x: Fixed, y: Fixed) {
let _ = seat;
let _ = x;
@ -249,6 +254,10 @@ impl Node for DisplayNode {
}
FindTreeResult::AcceptsInput
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
}
tree_id!(OutputNodeId);
@ -298,6 +307,10 @@ impl Node for OutputNode {
self.workspace.set(None);
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_output(self, x, y);
}
@ -376,6 +389,10 @@ impl Node for FloatNode {
.set(Rect::new_sized(pos.x1(), pos.x2(), width, height).unwrap());
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_floating(self, x, y)
}

View file

@ -1,4 +1,4 @@
use crate::ifs::wl_seat::NodeSeatState;
use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal};
use crate::rect::Rect;
use crate::render::Renderer;
use crate::tree::container::ContainerNode;
@ -6,6 +6,7 @@ use crate::tree::{AbsoluteNode, FindTreeResult, FoundNode, Node, NodeId, OutputN
use crate::utils::clonecell::CloneCell;
use crate::utils::linkedlist::LinkedList;
use std::rc::Rc;
use crate::cursor::KnownCursor;
tree_id!(WorkspaceNodeId);
@ -61,6 +62,10 @@ impl Node for WorkspaceNode {
self.container.set(None);
}
fn pointer_target(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_workspace(self, x, y);
}