wayland: implement scaling
This involves many subsystems:
- config:
- allow setting the connector scale
- allow setting the cursor size
- cursors:
- load server cursors for all requested sizes and scales
- wl_surface:
- track the output the surface belongs to
- send wl_surface.enter/leave
- wl_output:
- implement wl_output.scale
- text:
- pre-render texts for all used scales
- renderer:
- properly align scale textures and rectangles
- wp_fractional_scale:
- new interface for fractional scaling
This commit is contained in:
parent
16aec8f87e
commit
e52a60b3b6
41 changed files with 1417 additions and 364 deletions
|
|
@ -359,6 +359,10 @@ impl Client {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_size(&self, seat: Seat, size: i32) {
|
||||||
|
self.send(&ClientMessage::SetCursorSize { seat, size })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_size(&self, sized: Resizable, size: i32) {
|
pub fn set_size(&self, sized: Resizable, size: i32) {
|
||||||
self.send(&ClientMessage::SetSize { sized, size })
|
self.send(&ClientMessage::SetSize { sized, size })
|
||||||
}
|
}
|
||||||
|
|
@ -459,6 +463,16 @@ impl Client {
|
||||||
connected
|
connected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connector_set_scale(&self, connector: Connector, scale: f64) {
|
||||||
|
self.send(&ClientMessage::ConnectorSetScale { connector, scale });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connector_get_scale(&self, connector: Connector) -> f64 {
|
||||||
|
let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector });
|
||||||
|
get_response!(res, 1.0, ConnectorGetScale { scale });
|
||||||
|
scale
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connector_type(&self, connector: Connector) -> ConnectorType {
|
pub fn connector_type(&self, connector: Connector) -> ConnectorType {
|
||||||
let res = self.send_with_response(&ClientMessage::ConnectorType { connector });
|
let res = self.send_with_response(&ClientMessage::ConnectorType { connector });
|
||||||
get_response!(res, CON_UNKNOWN, ConnectorType { ty });
|
get_response!(res, CON_UNKNOWN, ConnectorType { ty });
|
||||||
|
|
@ -483,6 +497,12 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connector_size(&self, connector: Connector) -> (i32, i32) {
|
||||||
|
let res = self.send_with_response(&ClientMessage::ConnectorSize { connector });
|
||||||
|
get_response!(res, (0, 0), ConnectorSize { width, height });
|
||||||
|
(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn drm_devices(&self) -> Vec<DrmDevice> {
|
pub fn drm_devices(&self) -> Vec<DrmDevice> {
|
||||||
let res = self.send_with_response(&ClientMessage::GetDrmDevices);
|
let res = self.send_with_response(&ClientMessage::GetDrmDevices);
|
||||||
get_response!(res, vec![], GetDrmDevices { devices });
|
get_response!(res, vec![], GetDrmDevices { devices });
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,20 @@ pub enum ClientMessage<'a> {
|
||||||
device: InputDevice,
|
device: InputDevice,
|
||||||
px: f64,
|
px: f64,
|
||||||
},
|
},
|
||||||
|
ConnectorSetScale {
|
||||||
|
connector: Connector,
|
||||||
|
scale: f64,
|
||||||
|
},
|
||||||
|
ConnectorGetScale {
|
||||||
|
connector: Connector,
|
||||||
|
},
|
||||||
|
ConnectorSize {
|
||||||
|
connector: Connector,
|
||||||
|
},
|
||||||
|
SetCursorSize {
|
||||||
|
seat: Seat,
|
||||||
|
size: i32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Encode, Decode, Debug)]
|
#[derive(Encode, Decode, Debug)]
|
||||||
|
|
@ -360,6 +374,13 @@ pub enum Response {
|
||||||
GetFont {
|
GetFont {
|
||||||
font: String,
|
font: String,
|
||||||
},
|
},
|
||||||
|
ConnectorGetScale {
|
||||||
|
scale: f64,
|
||||||
|
},
|
||||||
|
ConnectorSize {
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Encode, Decode, Debug)]
|
#[derive(Encode, Decode, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,13 @@ impl Seat {
|
||||||
Self(raw)
|
Self(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the size of the cursor theme.
|
||||||
|
///
|
||||||
|
/// Default: 16.
|
||||||
|
pub fn set_cursor_size(self, size: i32) {
|
||||||
|
get!().set_cursor_size(self, size)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a compositor-wide hotkey.
|
/// Creates a compositor-wide hotkey.
|
||||||
///
|
///
|
||||||
/// The closure is invoked when the user presses the last key of the modified keysym.
|
/// The closure is invoked when the user presses the last key of the modified keysym.
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,23 @@ impl Connector {
|
||||||
get!(false).connector_connected(self)
|
get!(false).connector_connected(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the scale of the currently connected monitor.
|
||||||
|
pub fn scale(self) -> f64 {
|
||||||
|
if !self.exists() {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
get!(1.0).connector_get_scale(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the scale to use for the currently connected monitor.
|
||||||
|
pub fn set_scale(self, scale: f64) {
|
||||||
|
if !self.exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log::info!("setting scale to {}", scale);
|
||||||
|
get!().connector_set_scale(self, scale);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the connector type.
|
/// Returns the connector type.
|
||||||
pub fn ty(self) -> ConnectorType {
|
pub fn ty(self) -> ConnectorType {
|
||||||
if !self.exists() {
|
if !self.exists() {
|
||||||
|
|
@ -95,18 +112,18 @@ impl Connector {
|
||||||
get!(Mode::zeroed()).connector_mode(self)
|
get!(Mode::zeroed()).connector_mode(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the width of the current mode of the connector.
|
/// Returns the logical width of the connector.
|
||||||
///
|
///
|
||||||
/// This is a shortcut for `mode().width()`.
|
/// The returned value will be different from `mode().width()` if the scale is not 1.
|
||||||
pub fn width(self) -> i32 {
|
pub fn width(self) -> i32 {
|
||||||
self.mode().width
|
get!().connector_size(self).0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the height of the current mode of the connector.
|
/// Returns the logical height of the connector.
|
||||||
///
|
///
|
||||||
/// This is a shortcut for `mode().height()`.
|
/// The returned value will be different from `mode().height()` if the scale is not 1.
|
||||||
pub fn height(self) -> i32 {
|
pub fn height(self) -> i32 {
|
||||||
self.mode().height
|
get!().connector_size(self).1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the refresh rate in mhz of the current mode of the connector.
|
/// Returns the refresh rate in mhz of the current mode of the connector.
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,7 @@ impl MetalConnector {
|
||||||
Some(node.global.pos.get()),
|
Some(node.global.pos.get()),
|
||||||
true,
|
true,
|
||||||
&mut rr,
|
&mut rr,
|
||||||
|
node.preferred_scale.get(),
|
||||||
);
|
);
|
||||||
for fr in rr.frame_requests.drain(..) {
|
for fr in rr.frame_requests.drain(..) {
|
||||||
fr.send_done();
|
fr.send_done();
|
||||||
|
|
|
||||||
|
|
@ -699,6 +699,7 @@ impl XBackend {
|
||||||
Some(node.global.pos.get()),
|
Some(node.global.pos.get()),
|
||||||
true,
|
true,
|
||||||
rr.deref_mut(),
|
rr.deref_mut(),
|
||||||
|
node.preferred_scale.get(),
|
||||||
);
|
);
|
||||||
for fr in rr.frame_requests.drain(..) {
|
for fr in rr.frame_requests.drain(..) {
|
||||||
fr.send_done();
|
fr.send_done();
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use {
|
||||||
clientmem::{self, ClientMemError},
|
clientmem::{self, ClientMemError},
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
|
fixed::Fixed,
|
||||||
forker,
|
forker,
|
||||||
globals::Globals,
|
globals::Globals,
|
||||||
ifs::{wl_output::WlOutputGlobal, wl_surface::NoneSurfaceExt},
|
ifs::{wl_output::WlOutputGlobal, wl_surface::NoneSurfaceExt},
|
||||||
|
|
@ -31,7 +32,8 @@ use {
|
||||||
user_session::import_environment,
|
user_session::import_environment,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, errorfmt::ErrorFmt, fdcloser::FdCloser, numcell::NumCell,
|
clonecell::CloneCell, errorfmt::ErrorFmt, fdcloser::FdCloser, numcell::NumCell,
|
||||||
oserror::OsError, queue::AsyncQueue, run_toplevel::RunToplevel, tri::Try,
|
oserror::OsError, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||||
|
tri::Try,
|
||||||
},
|
},
|
||||||
wheel::{Wheel, WheelError},
|
wheel::{Wheel, WheelError},
|
||||||
xkbcommon::XkbContext,
|
xkbcommon::XkbContext,
|
||||||
|
|
@ -120,6 +122,8 @@ fn start_compositor2(
|
||||||
let wheel = Wheel::new(&engine, &ring)?;
|
let wheel = Wheel::new(&engine, &ring)?;
|
||||||
let (_run_toplevel_future, run_toplevel) = RunToplevel::install(&engine);
|
let (_run_toplevel_future, run_toplevel) = RunToplevel::install(&engine);
|
||||||
let node_ids = NodeIds::default();
|
let node_ids = NodeIds::default();
|
||||||
|
let scales = RefCounted::default();
|
||||||
|
scales.add(Fixed::from_int(1));
|
||||||
let state = Rc::new(State {
|
let state = Rc::new(State {
|
||||||
xkb_ctx,
|
xkb_ctx,
|
||||||
backend: CloneCell::new(Rc::new(DummyBackend)),
|
backend: CloneCell::new(Rc::new(DummyBackend)),
|
||||||
|
|
@ -187,6 +191,8 @@ fn start_compositor2(
|
||||||
locked: Cell::new(false),
|
locked: Cell::new(false),
|
||||||
lock: Default::default(),
|
lock: Default::default(),
|
||||||
},
|
},
|
||||||
|
scales,
|
||||||
|
cursor_sizes: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -376,6 +382,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
scroll: Default::default(),
|
scroll: Default::default(),
|
||||||
pointer_positions: Default::default(),
|
pointer_positions: Default::default(),
|
||||||
lock_surface: Default::default(),
|
lock_surface: Default::default(),
|
||||||
|
preferred_scale: Cell::new(Fixed::from_int(1)),
|
||||||
});
|
});
|
||||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||||
id: state.node_ids.next(),
|
id: state.node_ids.next(),
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use {
|
||||||
},
|
},
|
||||||
compositor::MAX_EXTENTS,
|
compositor::MAX_EXTENTS,
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
|
fixed::Fixed,
|
||||||
ifs::wl_seat::{SeatId, WlSeatGlobal},
|
ifs::wl_seat::{SeatId, WlSeatGlobal},
|
||||||
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
state::{ConnectorData, DeviceHandlerData, DrmDevData, OutputData, State},
|
||||||
theme::{Color, ThemeSized, DEFAULT_FONT},
|
theme::{Color, ThemeSized, DEFAULT_FONT},
|
||||||
|
|
@ -573,6 +574,47 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_cursor_size(&self, seat: Seat, size: i32) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
if size < 0 {
|
||||||
|
return Err(CphError::NegativeCursorSize);
|
||||||
|
}
|
||||||
|
seat.set_cursor_size(size as _);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_connector_size(&self, connector: Connector) -> Result<(), CphError> {
|
||||||
|
let connector = self.get_output(connector)?;
|
||||||
|
let pos = connector.node.global.pos.get();
|
||||||
|
self.respond(Response::ConnectorSize {
|
||||||
|
width: pos.width(),
|
||||||
|
height: pos.height(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_connector_get_scale(&self, connector: Connector) -> Result<(), CphError> {
|
||||||
|
let connector = self.get_output(connector)?;
|
||||||
|
self.respond(Response::ConnectorGetScale {
|
||||||
|
scale: connector.node.preferred_scale.get().to_f64(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_connector_set_scale(&self, connector: Connector, scale: f64) -> Result<(), CphError> {
|
||||||
|
if scale < 0.1 {
|
||||||
|
return Err(CphError::ScaleTooSmall(scale));
|
||||||
|
}
|
||||||
|
if scale > 1000.0 {
|
||||||
|
return Err(CphError::ScaleTooLarge(scale));
|
||||||
|
}
|
||||||
|
let scale = Fixed::from_f64(scale);
|
||||||
|
let connector = self.get_output(connector)?;
|
||||||
|
connector.node.set_preferred_scale(scale);
|
||||||
|
self.state.damage();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_connector_set_position(
|
fn handle_connector_set_position(
|
||||||
&self,
|
&self,
|
||||||
connector: Connector,
|
connector: Connector,
|
||||||
|
|
@ -1086,6 +1128,18 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SetPxPerWheelScroll { device, px } => self
|
ClientMessage::SetPxPerWheelScroll { device, px } => self
|
||||||
.handle_set_px_per_wheel_scroll(device, px)
|
.handle_set_px_per_wheel_scroll(device, px)
|
||||||
.wrn("set_px_per_wheel_scroll")?,
|
.wrn("set_px_per_wheel_scroll")?,
|
||||||
|
ClientMessage::ConnectorSetScale { connector, scale } => self
|
||||||
|
.handle_connector_set_scale(connector, scale)
|
||||||
|
.wrn("connector_set_scale")?,
|
||||||
|
ClientMessage::ConnectorGetScale { connector } => self
|
||||||
|
.handle_connector_get_scale(connector)
|
||||||
|
.wrn("connector_get_scale")?,
|
||||||
|
ClientMessage::ConnectorSize { connector } => self
|
||||||
|
.handle_connector_size(connector)
|
||||||
|
.wrn("connector_size")?,
|
||||||
|
ClientMessage::SetCursorSize { seat, size } => self
|
||||||
|
.handle_set_cursor_size(seat, size)
|
||||||
|
.wrn("set_cursor_size")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -1137,6 +1191,12 @@ enum CphError {
|
||||||
FailedRequest(&'static str, #[source] Box<Self>),
|
FailedRequest(&'static str, #[source] Box<Self>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
TimerError(#[from] TimerError),
|
TimerError(#[from] TimerError),
|
||||||
|
#[error("The requested monitor scale {0} is too small")]
|
||||||
|
ScaleTooSmall(f64),
|
||||||
|
#[error("The requested monitor scale {0} is too large")]
|
||||||
|
ScaleTooLarge(f64),
|
||||||
|
#[error("Tried to set a negative cursor size")]
|
||||||
|
NegativeCursorSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait WithRequestName {
|
trait WithRequestName {
|
||||||
|
|
|
||||||
323
src/cursor.rs
323
src/cursor.rs
|
|
@ -1,11 +1,14 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
fixed::Fixed,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
render::{RenderContext, RenderError, Renderer, Texture},
|
render::{RenderContext, RenderError, Renderer, Texture},
|
||||||
utils::{errorfmt::ErrorFmt, numcell::NumCell},
|
state::State,
|
||||||
|
tree::OutputNode,
|
||||||
|
utils::{errorfmt::ErrorFmt, numcell::NumCell, smallmap::SmallMapMut},
|
||||||
},
|
},
|
||||||
ahash::AHashSet,
|
ahash::{AHashMap, AHashSet},
|
||||||
bstr::{BStr, BString, ByteSlice, ByteVec},
|
bstr::{BStr, BString, ByteSlice, ByteVec},
|
||||||
byteorder::{LittleEndian, ReadBytesExt},
|
byteorder::{LittleEndian, ReadBytesExt},
|
||||||
isnt::std_1::primitive::IsntSliceExt,
|
isnt::std_1::primitive::IsntSliceExt,
|
||||||
|
|
@ -34,10 +37,10 @@ const HOME: &str = "HOME";
|
||||||
const HEADER_SIZE: u32 = 16;
|
const HEADER_SIZE: u32 = 16;
|
||||||
|
|
||||||
pub trait Cursor {
|
pub trait Cursor {
|
||||||
fn set_position(&self, x: i32, y: i32);
|
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed);
|
||||||
fn render(&self, renderer: &mut Renderer, x: i32, y: i32);
|
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
fn get_hotspot(&self) -> (i32, i32);
|
let _ = output;
|
||||||
fn extents(&self) -> Rect;
|
}
|
||||||
fn handle_unset(&self) {}
|
fn handle_unset(&self) {}
|
||||||
fn tick(&self) {}
|
fn tick(&self) {}
|
||||||
}
|
}
|
||||||
|
|
@ -68,11 +71,17 @@ pub enum KnownCursor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerCursors {
|
impl ServerCursors {
|
||||||
pub fn load(ctx: &Rc<RenderContext>) -> Result<Self, CursorError> {
|
pub fn load(ctx: &Rc<RenderContext>, state: &State) -> Result<Option<Self>, CursorError> {
|
||||||
let paths = find_cursor_paths();
|
let paths = find_cursor_paths();
|
||||||
log::debug!("Trying to load cursors from paths {:?}", paths);
|
log::debug!("Trying to load cursors from paths {:?}", paths);
|
||||||
let load = |name: &str| ServerCursorTemplate::load(name, None, 16, &paths, ctx);
|
let sizes = state.cursor_sizes.to_vec();
|
||||||
Ok(Self {
|
let scales = state.scales.to_vec();
|
||||||
|
if sizes.is_empty() || scales.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let load =
|
||||||
|
|name: &str| ServerCursorTemplate::load(name, None, &scales, &sizes, &paths, ctx);
|
||||||
|
Ok(Some(Self {
|
||||||
default: load("left_ptr")?,
|
default: load("left_ptr")?,
|
||||||
// default: load("left_ptr_watch")?,
|
// default: load("left_ptr_watch")?,
|
||||||
resize_right: load("right_side")?,
|
resize_right: load("right_side")?,
|
||||||
|
|
@ -85,13 +94,13 @@ impl ServerCursors {
|
||||||
resize_top_right: load("top_right_corner")?,
|
resize_top_right: load("top_right_corner")?,
|
||||||
resize_bottom_left: load("bottom_left_corner")?,
|
resize_bottom_left: load("bottom_left_corner")?,
|
||||||
resize_bottom_right: load("bottom_right_corner")?,
|
resize_bottom_right: load("bottom_right_corner")?,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ServerCursorTemplate {
|
pub struct ServerCursorTemplate {
|
||||||
var: ServerCursorTemplateVariant,
|
var: ServerCursorTemplateVariant,
|
||||||
pub xcursor: Vec<XCursorImage>,
|
pub xcursor: Vec<AHashMap<(Fixed, u32), Rc<XCursorImage>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ServerCursorTemplateVariant {
|
enum ServerCursorTemplateVariant {
|
||||||
|
|
@ -103,45 +112,64 @@ impl ServerCursorTemplate {
|
||||||
fn load(
|
fn load(
|
||||||
name: &str,
|
name: &str,
|
||||||
theme: Option<&BStr>,
|
theme: Option<&BStr>,
|
||||||
size: u32,
|
scales: &[Fixed],
|
||||||
|
sizes: &[u32],
|
||||||
paths: &[BString],
|
paths: &[BString],
|
||||||
ctx: &Rc<RenderContext>,
|
ctx: &Rc<RenderContext>,
|
||||||
) -> Result<Self, CursorError> {
|
) -> Result<Self, CursorError> {
|
||||||
match open_cursor(name, theme, size, paths) {
|
match open_cursor(name, theme, &scales, sizes, paths) {
|
||||||
Ok(cs) => {
|
Ok(cs) => {
|
||||||
if cs.len() == 1 {
|
if cs.images.len() == 1 {
|
||||||
let c = &cs[0];
|
let mut sizes = SmallMapMut::new();
|
||||||
let cursor = CursorImage::from_bytes(
|
for (k, c) in &cs.images[0] {
|
||||||
ctx, &c.pixels, 0, c.width, c.height, c.xhot, c.yhot,
|
sizes.insert(
|
||||||
)?;
|
*k,
|
||||||
|
CursorImageScaled::from_bytes(
|
||||||
|
ctx, &c.pixels, c.width, c.height, c.xhot, c.yhot,
|
||||||
|
)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let cursor = CursorImage::from_sizes(0, sizes)?;
|
||||||
Ok(ServerCursorTemplate {
|
Ok(ServerCursorTemplate {
|
||||||
var: ServerCursorTemplateVariant::Static(Rc::new(cursor)),
|
var: ServerCursorTemplateVariant::Static(Rc::new(cursor)),
|
||||||
xcursor: cs,
|
xcursor: cs.images,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let mut images = vec![];
|
let mut images = vec![];
|
||||||
for c in &cs {
|
for image in &cs.images {
|
||||||
let img = CursorImage::from_bytes(
|
let mut sizes = SmallMapMut::new();
|
||||||
ctx,
|
let mut delay_ms = 0;
|
||||||
&c.pixels,
|
for (k, c) in image {
|
||||||
c.delay as _,
|
delay_ms = c.delay;
|
||||||
c.width,
|
sizes.insert(
|
||||||
c.height,
|
*k,
|
||||||
c.xhot,
|
CursorImageScaled::from_bytes(
|
||||||
c.yhot,
|
ctx, &c.pixels, c.width, c.height, c.xhot, c.yhot,
|
||||||
)?;
|
)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let img = CursorImage::from_sizes(delay_ms as _, sizes)?;
|
||||||
images.push(img);
|
images.push(img);
|
||||||
}
|
}
|
||||||
Ok(ServerCursorTemplate {
|
Ok(ServerCursorTemplate {
|
||||||
var: ServerCursorTemplateVariant::Animated(Rc::new(images)),
|
var: ServerCursorTemplateVariant::Animated(Rc::new(images)),
|
||||||
xcursor: cs,
|
xcursor: cs.images,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Could not load cursor {}: {}", name, ErrorFmt(e));
|
log::warn!("Could not load cursor {}: {}", name, ErrorFmt(e));
|
||||||
let empty: [Cell<u8>; 4] = unsafe { MaybeUninit::zeroed().assume_init() };
|
let empty: [Cell<u8>; 4] = unsafe { MaybeUninit::zeroed().assume_init() };
|
||||||
let cursor = CursorImage::from_bytes(ctx, &empty, 0, 1, 1, 0, 0)?;
|
let mut img_sizes = SmallMapMut::new();
|
||||||
|
for scale in scales {
|
||||||
|
for size in sizes {
|
||||||
|
img_sizes.insert(
|
||||||
|
(*scale, *size),
|
||||||
|
CursorImageScaled::from_bytes(ctx, &empty, 1, 1, 0, 0)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let cursor = CursorImage::from_sizes(0, img_sizes)?;
|
||||||
Ok(ServerCursorTemplate {
|
Ok(ServerCursorTemplate {
|
||||||
var: ServerCursorTemplateVariant::Static(Rc::new(cursor)),
|
var: ServerCursorTemplateVariant::Static(Rc::new(cursor)),
|
||||||
xcursor: Default::default(),
|
xcursor: Default::default(),
|
||||||
|
|
@ -150,13 +178,10 @@ impl ServerCursorTemplate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instantiate(&self) -> Rc<dyn Cursor> {
|
pub fn instantiate(&self, size: u32) -> Rc<dyn Cursor> {
|
||||||
match &self.var {
|
match &self.var {
|
||||||
ServerCursorTemplateVariant::Static(s) => Rc::new(StaticCursor {
|
ServerCursorTemplateVariant::Static(s) => Rc::new(StaticCursor {
|
||||||
x: Cell::new(0),
|
image: s.for_size(size),
|
||||||
y: Cell::new(0),
|
|
||||||
extents: Cell::new(s.extents),
|
|
||||||
image: s.clone(),
|
|
||||||
}),
|
}),
|
||||||
ServerCursorTemplateVariant::Animated(a) => {
|
ServerCursorTemplateVariant::Animated(a) => {
|
||||||
let mut start = c::timespec {
|
let mut start = c::timespec {
|
||||||
|
|
@ -168,68 +193,103 @@ impl ServerCursorTemplate {
|
||||||
start,
|
start,
|
||||||
next: NumCell::new(a[0].delay_ns),
|
next: NumCell::new(a[0].delay_ns),
|
||||||
idx: Cell::new(0),
|
idx: Cell::new(0),
|
||||||
images: a.clone(),
|
images: a.iter().map(|c| c.for_size(size)).collect(),
|
||||||
x: Cell::new(0),
|
|
||||||
y: Cell::new(0),
|
|
||||||
extents: Cell::new(a[0].extents),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CursorImage {
|
struct CursorImageScaled {
|
||||||
extents: Rect,
|
extents: Rect,
|
||||||
xhot: i32,
|
|
||||||
yhot: i32,
|
|
||||||
delay_ns: u64,
|
|
||||||
tex: Rc<Texture>,
|
tex: Rc<Texture>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CursorImage {
|
struct CursorImage {
|
||||||
|
delay_ns: u64,
|
||||||
|
sizes: SmallMapMut<(Fixed, u32), Rc<CursorImageScaled>, 2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InstantiatedCursorImage {
|
||||||
|
delay_ns: u64,
|
||||||
|
scales: SmallMapMut<Fixed, Rc<CursorImageScaled>, 2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CursorImageScaled {
|
||||||
fn from_bytes(
|
fn from_bytes(
|
||||||
ctx: &Rc<RenderContext>,
|
ctx: &Rc<RenderContext>,
|
||||||
data: &[Cell<u8>],
|
data: &[Cell<u8>],
|
||||||
delay_ms: u64,
|
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
xhot: i32,
|
xhot: i32,
|
||||||
yhot: i32,
|
yhot: i32,
|
||||||
|
) -> Result<Rc<Self>, CursorError> {
|
||||||
|
Ok(Rc::new(Self {
|
||||||
|
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
|
||||||
|
tex: ctx.shmem_texture(data, ARGB8888, width, height, width * 4)?,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CursorImage {
|
||||||
|
fn from_sizes(
|
||||||
|
delay_ms: u64,
|
||||||
|
sizes: SmallMapMut<(Fixed, u32), Rc<CursorImageScaled>, 2>,
|
||||||
) -> Result<Self, CursorError> {
|
) -> Result<Self, CursorError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
extents: Rect::new_sized(-xhot, -yhot, width, height).unwrap(),
|
|
||||||
xhot,
|
|
||||||
yhot,
|
|
||||||
delay_ns: delay_ms * 1_000_000,
|
delay_ns: delay_ms * 1_000_000,
|
||||||
tex: ctx.shmem_texture(data, ARGB8888, width, height, width * 4)?,
|
sizes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn for_size(&self, size: u32) -> InstantiatedCursorImage {
|
||||||
|
let mut sizes = SmallMapMut::new();
|
||||||
|
for ((scale, isize), v) in &self.sizes {
|
||||||
|
if *isize == size {
|
||||||
|
sizes.insert(*scale, v.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InstantiatedCursorImage {
|
||||||
|
delay_ns: self.delay_ns,
|
||||||
|
scales: sizes,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StaticCursor {
|
struct StaticCursor {
|
||||||
x: Cell<i32>,
|
image: InstantiatedCursorImage,
|
||||||
y: Cell<i32>,
|
}
|
||||||
extents: Cell<Rect>,
|
|
||||||
image: Rc<CursorImage>,
|
fn render_img(image: &InstantiatedCursorImage, renderer: &mut Renderer, x: Fixed, y: Fixed) {
|
||||||
|
let scale = renderer.scale();
|
||||||
|
let img = match image.scales.get(&scale) {
|
||||||
|
Some(img) => img,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let extents = if scale != 1 {
|
||||||
|
let scalef = scale.to_f64();
|
||||||
|
let x = (x.to_f64() * scalef).round() as i32;
|
||||||
|
let y = (y.to_f64() * scalef).round() as i32;
|
||||||
|
img.extents.move_(x, y)
|
||||||
|
} else {
|
||||||
|
img.extents.move_(x.round_down(), y.round_down())
|
||||||
|
};
|
||||||
|
if extents.intersects(&renderer.physical_extents()) {
|
||||||
|
renderer.render_texture(
|
||||||
|
&img.tex,
|
||||||
|
extents.x1(),
|
||||||
|
extents.y1(),
|
||||||
|
ARGB8888,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
scale,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor for StaticCursor {
|
impl Cursor for StaticCursor {
|
||||||
fn set_position(&self, x: i32, y: i32) {
|
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed) {
|
||||||
let dx = x - self.x.replace(x);
|
render_img(&self.image, renderer, x, y);
|
||||||
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, None, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hotspot(&self) -> (i32, i32) {
|
|
||||||
(self.image.xhot, self.image.yhot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extents(&self) -> Rect {
|
|
||||||
self.extents.get()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,31 +297,13 @@ struct AnimatedCursor {
|
||||||
start: c::timespec,
|
start: c::timespec,
|
||||||
next: NumCell<u64>,
|
next: NumCell<u64>,
|
||||||
idx: Cell<usize>,
|
idx: Cell<usize>,
|
||||||
images: Rc<Vec<CursorImage>>,
|
images: Vec<InstantiatedCursorImage>,
|
||||||
x: Cell<i32>,
|
|
||||||
y: Cell<i32>,
|
|
||||||
extents: Cell<Rect>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor for AnimatedCursor {
|
impl Cursor for AnimatedCursor {
|
||||||
fn set_position(&self, x: i32, y: i32) {
|
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed) {
|
||||||
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()];
|
let img = &self.images[self.idx.get()];
|
||||||
renderer.render_texture(&img.tex, x, y, ARGB8888, None, None);
|
render_img(img, renderer, x, y);
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hotspot(&self) -> (i32, i32) {
|
|
||||||
let img = &self.images[self.idx.get()];
|
|
||||||
(img.xhot, img.yhot)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extents(&self) -> Rect {
|
|
||||||
self.extents.get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick(&self) {
|
fn tick(&self) {
|
||||||
|
|
@ -278,25 +320,21 @@ impl Cursor for AnimatedCursor {
|
||||||
let idx = (self.idx.get() + 1) % self.images.len();
|
let idx = (self.idx.get() + 1) % self.images.len();
|
||||||
self.idx.set(idx);
|
self.idx.set(idx);
|
||||||
let image = &self.images[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);
|
self.next.fetch_add(image.delay_ns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct OpenCursorResult {
|
||||||
|
images: Vec<AHashMap<(Fixed, u32), Rc<XCursorImage>>>,
|
||||||
|
}
|
||||||
|
|
||||||
fn open_cursor(
|
fn open_cursor(
|
||||||
name: &str,
|
name: &str,
|
||||||
theme: Option<&BStr>,
|
theme: Option<&BStr>,
|
||||||
size: u32,
|
scales: &[Fixed],
|
||||||
|
sizes: &[u32],
|
||||||
paths: &[BString],
|
paths: &[BString],
|
||||||
) -> Result<Vec<XCursorImage>, CursorError> {
|
) -> Result<OpenCursorResult, CursorError> {
|
||||||
let name = name.as_bytes().as_bstr();
|
let name = name.as_bytes().as_bstr();
|
||||||
let mut file = None;
|
let mut file = None;
|
||||||
let mut themes_tested = AHashSet::new();
|
let mut themes_tested = AHashSet::new();
|
||||||
|
|
@ -311,11 +349,7 @@ fn open_cursor(
|
||||||
_ => return Err(CursorError::NotFound),
|
_ => return Err(CursorError::NotFound),
|
||||||
};
|
};
|
||||||
let mut file = BufReader::new(file);
|
let mut file = BufReader::new(file);
|
||||||
let images = parser_cursor_file(&mut file, size)?;
|
parser_cursor_file(&mut file, scales, sizes)
|
||||||
if images.is_empty() {
|
|
||||||
return Err(CursorError::EmptyXcursorFile);
|
|
||||||
}
|
|
||||||
Ok(images)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_cursor_file(
|
fn open_cursor_file(
|
||||||
|
|
@ -461,8 +495,9 @@ impl Debug for XCursorImage {
|
||||||
|
|
||||||
fn parser_cursor_file<R: BufRead + Seek>(
|
fn parser_cursor_file<R: BufRead + Seek>(
|
||||||
r: &mut R,
|
r: &mut R,
|
||||||
target: u32,
|
scales: &[Fixed],
|
||||||
) -> Result<Vec<XCursorImage>, CursorError> {
|
sizes: &[u32],
|
||||||
|
) -> Result<OpenCursorResult, CursorError> {
|
||||||
let [magic, header] = read_u32_n(r)?;
|
let [magic, header] = read_u32_n(r)?;
|
||||||
if magic != XCURSOR_MAGIC || header < HEADER_SIZE {
|
if magic != XCURSOR_MAGIC || header < HEADER_SIZE {
|
||||||
return Err(CursorError::NotAnXcursorFile);
|
return Err(CursorError::NotAnXcursorFile);
|
||||||
|
|
@ -472,24 +507,54 @@ fn parser_cursor_file<R: BufRead + Seek>(
|
||||||
if ntoc > 0x10000 {
|
if ntoc > 0x10000 {
|
||||||
return Err(CursorError::OversizedXcursorFile);
|
return Err(CursorError::OversizedXcursorFile);
|
||||||
}
|
}
|
||||||
let mut images_positions = vec![];
|
struct Target {
|
||||||
let mut best_fit = i64::MAX;
|
positions: Vec<u32>,
|
||||||
|
effective_size: u32,
|
||||||
|
size: u32,
|
||||||
|
scale: Fixed,
|
||||||
|
best_fit: i64,
|
||||||
|
}
|
||||||
|
let mut targets = Vec::new();
|
||||||
|
for scale in scales {
|
||||||
|
let scalef = scale.to_f64();
|
||||||
|
for size in sizes {
|
||||||
|
let effective_size = (*size as f64 * scalef).round() as _;
|
||||||
|
targets.push(Target {
|
||||||
|
positions: vec![],
|
||||||
|
effective_size,
|
||||||
|
size: *size,
|
||||||
|
scale: *scale,
|
||||||
|
best_fit: i64::MAX,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut sizes = AHashSet::new();
|
||||||
for _ in 0..ntoc {
|
for _ in 0..ntoc {
|
||||||
let [type_, size, position] = read_u32_n(r)?;
|
let [type_, size, position] = read_u32_n(r)?;
|
||||||
if type_ != XCURSOR_IMAGE_TYPE {
|
if type_ != XCURSOR_IMAGE_TYPE {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let fit = (size as i64 - target as i64).abs();
|
sizes.insert(size);
|
||||||
if fit < best_fit {
|
for target in &mut targets {
|
||||||
best_fit = fit;
|
let fit = (size as i64 - target.effective_size as i64).abs();
|
||||||
images_positions.clear();
|
if fit < target.best_fit {
|
||||||
}
|
target.best_fit = fit;
|
||||||
if fit == best_fit {
|
target.positions.clear();
|
||||||
images_positions.push(position);
|
}
|
||||||
|
if fit == target.best_fit {
|
||||||
|
target.positions.push(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut images = Vec::with_capacity(images_positions.len());
|
let positions: AHashSet<_> = targets
|
||||||
for position in images_positions {
|
.iter()
|
||||||
|
.flat_map(|t| t.positions.iter().copied())
|
||||||
|
.collect();
|
||||||
|
if positions.is_empty() {
|
||||||
|
return Err(CursorError::EmptyXcursorFile);
|
||||||
|
}
|
||||||
|
let mut images = AHashMap::new();
|
||||||
|
for position in positions {
|
||||||
r.seek(SeekFrom::Start(position as u64))?;
|
r.seek(SeekFrom::Start(position as u64))?;
|
||||||
let [_chunk_header, _type_, _size, _version, width, height, xhot, yhot, delay] =
|
let [_chunk_header, _type_, _size, _version, width, height, xhot, yhot, delay] =
|
||||||
read_u32_n(r)?;
|
read_u32_n(r)?;
|
||||||
|
|
@ -511,9 +576,23 @@ fn parser_cursor_file<R: BufRead + Seek>(
|
||||||
num_bytes,
|
num_bytes,
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
images.push(image);
|
images.insert(position, Rc::new(image));
|
||||||
}
|
}
|
||||||
Ok(images)
|
let mut num = targets[0].positions.len();
|
||||||
|
if num > 1 && targets.iter().any(|t| t.positions.len() != num) {
|
||||||
|
log::warn!("Cursor file contains animated cursor but not all scales have the same number of images");
|
||||||
|
num = 1;
|
||||||
|
}
|
||||||
|
let mut res = vec![];
|
||||||
|
for i in 0..num {
|
||||||
|
let mut idx_images = AHashMap::new();
|
||||||
|
for target in &targets {
|
||||||
|
let image = images.get(&target.positions[i]).unwrap();
|
||||||
|
idx_images.insert((target.scale, target.size), image.clone());
|
||||||
|
}
|
||||||
|
res.push(idx_images);
|
||||||
|
}
|
||||||
|
Ok(OpenCursorResult { images: res })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_u32_n<R: BufRead, const N: usize>(r: &mut R) -> Result<[u32; N], io::Error> {
|
fn read_u32_n<R: BufRead, const N: usize>(r: &mut R) -> Result<[u32; N], io::Error> {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
ops::{Add, AddAssign, Sub, SubAssign},
|
ops::{Add, AddAssign, Sub, SubAssign},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Fixed(pub i32);
|
pub struct Fixed(pub i32);
|
||||||
|
|
||||||
|
|
@ -37,6 +37,10 @@ impl Fixed {
|
||||||
self.0 >> 8
|
self.0 >> 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn round_up(self) -> i32 {
|
||||||
|
(self.0 + 255) >> 8
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_fract(self, i: i32) -> Self {
|
pub fn apply_fract(self, i: i32) -> Self {
|
||||||
Self((i << 8) | (self.0 & 255))
|
Self((i << 8) | (self.0 & 255))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use {
|
||||||
},
|
},
|
||||||
wl_shm::WlShmGlobal,
|
wl_shm::WlShmGlobal,
|
||||||
wl_subcompositor::WlSubcompositorGlobal,
|
wl_subcompositor::WlSubcompositorGlobal,
|
||||||
|
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1Global,
|
||||||
wp_presentation::WpPresentationGlobal,
|
wp_presentation::WpPresentationGlobal,
|
||||||
wp_viewporter::WpViewporterGlobal,
|
wp_viewporter::WpViewporterGlobal,
|
||||||
xdg_wm_base::XdgWmBaseGlobal,
|
xdg_wm_base::XdgWmBaseGlobal,
|
||||||
|
|
@ -143,6 +144,7 @@ impl Globals {
|
||||||
add_singleton!(ZwpRelativePointerManagerV1Global);
|
add_singleton!(ZwpRelativePointerManagerV1Global);
|
||||||
add_singleton!(ExtSessionLockManagerV1Global);
|
add_singleton!(ExtSessionLockManagerV1Global);
|
||||||
add_singleton!(WpViewporterGlobal);
|
add_singleton!(WpViewporterGlobal);
|
||||||
|
add_singleton!(WpFractionalScaleManagerV1Global);
|
||||||
|
|
||||||
if backend.supports_idle() {
|
if backend.supports_idle() {
|
||||||
add_singleton!(ZwpIdleInhibitManagerV1Global);
|
add_singleton!(ZwpIdleInhibitManagerV1Global);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ pub mod wl_shm;
|
||||||
pub mod wl_shm_pool;
|
pub mod wl_shm_pool;
|
||||||
pub mod wl_subcompositor;
|
pub mod wl_subcompositor;
|
||||||
pub mod wl_surface;
|
pub mod wl_surface;
|
||||||
|
pub mod wp_fractional_scale_manager_v1;
|
||||||
pub mod wp_presentation;
|
pub mod wp_presentation;
|
||||||
pub mod wp_presentation_feedback;
|
pub mod wp_presentation_feedback;
|
||||||
pub mod wp_viewporter;
|
pub mod wp_viewporter;
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ use {
|
||||||
format::XRGB8888,
|
format::XRGB8888,
|
||||||
globals::{Global, GlobalName},
|
globals::{Global, GlobalName},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBufferStorage, zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
wl_buffer::WlBufferStorage, wl_surface::WlSurface,
|
||||||
zxdg_output_v1::ZxdgOutputV1,
|
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, zxdg_output_v1::ZxdgOutputV1,
|
||||||
},
|
},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::Object,
|
object::Object,
|
||||||
|
|
@ -72,6 +72,7 @@ pub struct WlOutputGlobal {
|
||||||
pub unused_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
|
pub unused_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
|
||||||
pub pending_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
|
pub pending_captures: LinkedList<Rc<ZwlrScreencopyFrameV1>>,
|
||||||
pub destroyed: Cell<bool>,
|
pub destroyed: Cell<bool>,
|
||||||
|
pub legacy_scale: Cell<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
#[derive(Eq, PartialEq)]
|
||||||
|
|
@ -117,6 +118,7 @@ impl WlOutputGlobal {
|
||||||
unused_captures: Default::default(),
|
unused_captures: Default::default(),
|
||||||
pending_captures: Default::default(),
|
pending_captures: Default::default(),
|
||||||
destroyed: Cell::new(false),
|
destroyed: Cell::new(false),
|
||||||
|
legacy_scale: Cell::new(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,6 +126,27 @@ impl WlOutputGlobal {
|
||||||
self.pos.get()
|
self.pos.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_each_binding<F: FnMut(&Rc<WlOutput>)>(&self, client: ClientId, mut f: F) {
|
||||||
|
let bindings = self.bindings.borrow_mut();
|
||||||
|
if let Some(bindings) = bindings.get(&client) {
|
||||||
|
for binding in bindings.values() {
|
||||||
|
f(binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_enter(&self, surface: &WlSurface) {
|
||||||
|
self.for_each_binding(surface.client.id, |b| {
|
||||||
|
surface.send_enter(b.id);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_leave(&self, surface: &WlSurface) {
|
||||||
|
self.for_each_binding(surface.client.id, |b| {
|
||||||
|
surface.send_leave(b.id);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn send_mode(&self) {
|
pub fn send_mode(&self) {
|
||||||
let bindings = self.bindings.borrow_mut();
|
let bindings = self.bindings.borrow_mut();
|
||||||
for binding in bindings.values() {
|
for binding in bindings.values() {
|
||||||
|
|
@ -293,7 +316,7 @@ impl WlOutput {
|
||||||
fn send_scale(self: &Rc<Self>) {
|
fn send_scale(self: &Rc<Self>) {
|
||||||
let event = Scale {
|
let event = Scale {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
factor: 1,
|
factor: self.global.legacy_scale.get(),
|
||||||
};
|
};
|
||||||
self.client.event(event);
|
self.client.event(event);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,11 +149,20 @@ pub struct WlSeatGlobal {
|
||||||
output: CloneCell<Rc<OutputNode>>,
|
output: CloneCell<Rc<OutputNode>>,
|
||||||
desired_known_cursor: Cell<Option<KnownCursor>>,
|
desired_known_cursor: Cell<Option<KnownCursor>>,
|
||||||
changes: NumCell<u32>,
|
changes: NumCell<u32>,
|
||||||
|
cursor_size: Cell<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
||||||
const CHANGE_TREE: u32 = 1 << 1;
|
const CHANGE_TREE: u32 = 1 << 1;
|
||||||
|
|
||||||
|
const DEFAULT_CURSOR_SIZE: u32 = 16;
|
||||||
|
|
||||||
|
impl Drop for WlSeatGlobal {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.state.remove_cursor_size(self.cursor_size.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl WlSeatGlobal {
|
impl WlSeatGlobal {
|
||||||
pub fn new(name: GlobalName, seat_name: &str, state: &Rc<State>) -> Rc<Self> {
|
pub fn new(name: GlobalName, seat_name: &str, state: &Rc<State>) -> Rc<Self> {
|
||||||
let slf = Rc::new(Self {
|
let slf = Rc::new(Self {
|
||||||
|
|
@ -192,7 +201,9 @@ impl WlSeatGlobal {
|
||||||
output: CloneCell::new(state.dummy_output.get().unwrap()),
|
output: CloneCell::new(state.dummy_output.get().unwrap()),
|
||||||
desired_known_cursor: Cell::new(None),
|
desired_known_cursor: Cell::new(None),
|
||||||
changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE),
|
changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE),
|
||||||
|
cursor_size: Cell::new(DEFAULT_CURSOR_SIZE),
|
||||||
});
|
});
|
||||||
|
state.add_cursor_size(DEFAULT_CURSOR_SIZE);
|
||||||
let seat = slf.clone();
|
let seat = slf.clone();
|
||||||
let future = state.eng.spawn(async move {
|
let future = state.eng.spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -207,6 +218,15 @@ impl WlSeatGlobal {
|
||||||
slf
|
slf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_size(&self, size: u32) {
|
||||||
|
let old = self.cursor_size.replace(size);
|
||||||
|
if size != old {
|
||||||
|
self.state.remove_cursor_size(old);
|
||||||
|
self.state.add_cursor_size(size);
|
||||||
|
self.reload_known_cursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_data_device(&self, device: &Rc<WlDataDevice>) {
|
pub fn add_data_device(&self, device: &Rc<WlDataDevice>) {
|
||||||
let mut dd = self.data_devices.borrow_mut();
|
let mut dd = self.data_devices.borrow_mut();
|
||||||
dd.entry(device.client.id)
|
dd.entry(device.client.id)
|
||||||
|
|
@ -342,15 +362,25 @@ impl WlSeatGlobal {
|
||||||
pub fn set_position(&self, x: i32, y: i32) {
|
pub fn set_position(&self, x: i32, y: i32) {
|
||||||
self.pos.set((Fixed::from_int(x), Fixed::from_int(y)));
|
self.pos.set((Fixed::from_int(x), Fixed::from_int(y)));
|
||||||
self.trigger_tree_changed();
|
self.trigger_tree_changed();
|
||||||
'set_output: {
|
let output = 'set_output: {
|
||||||
let outputs = self.state.outputs.lock();
|
let outputs = self.state.outputs.lock();
|
||||||
for output in outputs.values() {
|
for output in outputs.values() {
|
||||||
if output.node.global.pos.get().contains(x, y) {
|
if output.node.global.pos.get().contains(x, y) {
|
||||||
self.output.set(output.node.clone());
|
break 'set_output output.node.clone();
|
||||||
break 'set_output;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.output.set(self.state.dummy_output.get().unwrap());
|
self.state.dummy_output.get().unwrap()
|
||||||
|
};
|
||||||
|
self.set_output(&output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
|
self.output.set(output.clone());
|
||||||
|
if let Some(cursor) = self.cursor.get() {
|
||||||
|
cursor.set_output(output);
|
||||||
|
}
|
||||||
|
if let Some(dnd) = self.pointer_owner.dnd_icon() {
|
||||||
|
dnd.set_output(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -547,6 +577,9 @@ impl WlSeatGlobal {
|
||||||
icon: Option<Rc<WlSurface>>,
|
icon: Option<Rc<WlSurface>>,
|
||||||
serial: u32,
|
serial: u32,
|
||||||
) -> Result<(), WlSeatError> {
|
) -> Result<(), WlSeatError> {
|
||||||
|
if let Some(icon) = &icon {
|
||||||
|
icon.set_output(&self.output.get());
|
||||||
|
}
|
||||||
self.pointer_owner
|
self.pointer_owner
|
||||||
.start_drag(self, origin, source, icon, serial)
|
.start_drag(self, origin, source, icon, serial)
|
||||||
}
|
}
|
||||||
|
|
@ -604,6 +637,12 @@ impl WlSeatGlobal {
|
||||||
self.set_selection_::<PrimarySelectionIpc>(&self.primary_selection, selection)
|
self.set_selection_::<PrimarySelectionIpc>(&self.primary_selection, selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reload_known_cursor(&self) {
|
||||||
|
if let Some(kc) = self.desired_known_cursor.get() {
|
||||||
|
self.set_known_cursor(kc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_known_cursor(&self, cursor: KnownCursor) {
|
pub fn set_known_cursor(&self, cursor: KnownCursor) {
|
||||||
self.desired_known_cursor.set(Some(cursor));
|
self.desired_known_cursor.set(Some(cursor));
|
||||||
let cursors = match self.state.cursors.get() {
|
let cursors = match self.state.cursors.get() {
|
||||||
|
|
@ -622,7 +661,7 @@ impl WlSeatGlobal {
|
||||||
KnownCursor::ResizeBottomLeft => &cursors.resize_bottom_left,
|
KnownCursor::ResizeBottomLeft => &cursors.resize_bottom_left,
|
||||||
KnownCursor::ResizeBottomRight => &cursors.resize_bottom_right,
|
KnownCursor::ResizeBottomRight => &cursors.resize_bottom_right,
|
||||||
};
|
};
|
||||||
self.set_cursor2(Some(tpl.instantiate()));
|
self.set_cursor2(Some(tpl.instantiate(self.cursor_size.get())));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_app_cursor(&self, cursor: Option<Rc<dyn Cursor>>) {
|
pub fn set_app_cursor(&self, cursor: Option<Rc<dyn Cursor>>) {
|
||||||
|
|
@ -640,8 +679,7 @@ impl WlSeatGlobal {
|
||||||
old.handle_unset();
|
old.handle_unset();
|
||||||
}
|
}
|
||||||
if let Some(cursor) = cursor.as_ref() {
|
if let Some(cursor) = cursor.as_ref() {
|
||||||
let (x, y) = self.pos.get();
|
cursor.set_output(&self.output.get());
|
||||||
cursor.set_position(x.round_down(), y.round_down());
|
|
||||||
}
|
}
|
||||||
self.cursor.set(cursor);
|
self.cursor.set(cursor);
|
||||||
}
|
}
|
||||||
|
|
@ -654,6 +692,10 @@ impl WlSeatGlobal {
|
||||||
self.pointer_owner.remove_dnd_icon();
|
self.pointer_owner.remove_dnd_icon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_position(&self) -> (Fixed, Fixed) {
|
||||||
|
self.pos.get()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cursor(&self) -> Option<Rc<dyn Cursor>> {
|
pub fn get_cursor(&self) -> Option<Rc<dyn Cursor>> {
|
||||||
self.cursor.get()
|
self.cursor.get()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ impl WlSeatGlobal {
|
||||||
Some(o) => o,
|
Some(o) => o,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
self.output.set(output.node.clone());
|
self.set_output(&output.node);
|
||||||
let pos = output.node.global.pos.get();
|
let pos = output.node.global.pos.get();
|
||||||
x += Fixed::from_int(pos.x1());
|
x += Fixed::from_int(pos.x1());
|
||||||
y += Fixed::from_int(pos.y1());
|
y += Fixed::from_int(pos.y1());
|
||||||
|
|
@ -247,7 +247,7 @@ impl WlSeatGlobal {
|
||||||
let outputs = self.state.outputs.lock();
|
let outputs = self.state.outputs.lock();
|
||||||
for output in outputs.values() {
|
for output in outputs.values() {
|
||||||
if output.node.global.pos.get().contains(x_int, y_int) {
|
if output.node.global.pos.get().contains(x_int, y_int) {
|
||||||
self.output.set(output.node.clone());
|
self.set_output(&output.node);
|
||||||
break 'warp;
|
break 'warp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -261,8 +261,8 @@ impl WlSeatGlobal {
|
||||||
} else if y_int >= pos.y2() {
|
} else if y_int >= pos.y2() {
|
||||||
y_int = pos.y2() - 1;
|
y_int = pos.y2() - 1;
|
||||||
}
|
}
|
||||||
x = x.apply_fract(x_int);
|
x = Fixed::from_int(x_int);
|
||||||
y = y.apply_fract(y_int);
|
y = Fixed::from_int(y_int);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.set_new_position(time_usec, x, y);
|
self.set_new_position(time_usec, x, y);
|
||||||
|
|
@ -480,9 +480,6 @@ impl WlSeatGlobal {
|
||||||
fn set_new_position(self: &Rc<Self>, time_usec: u64, x: Fixed, y: Fixed) {
|
fn set_new_position(self: &Rc<Self>, time_usec: u64, x: Fixed, y: Fixed) {
|
||||||
self.pos_time_usec.set(time_usec);
|
self.pos_time_usec.set(time_usec);
|
||||||
self.pos.set((x, y));
|
self.pos.set((x, y));
|
||||||
if let Some(cursor) = self.cursor.get() {
|
|
||||||
cursor.set_position(x.round_down(), y.round_down());
|
|
||||||
}
|
|
||||||
self.changes.or_assign(CHANGE_CURSOR_MOVED);
|
self.changes.or_assign(CHANGE_CURSOR_MOVED);
|
||||||
self.apply_changes();
|
self.apply_changes();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
pub mod ext_session_lock_surface_v1;
|
pub mod ext_session_lock_surface_v1;
|
||||||
pub mod wl_subsurface;
|
pub mod wl_subsurface;
|
||||||
|
pub mod wp_fractional_scale_v1;
|
||||||
pub mod wp_viewport;
|
pub mod wp_viewport;
|
||||||
pub mod xdg_surface;
|
pub mod xdg_surface;
|
||||||
pub mod xwindow;
|
pub mod xwindow;
|
||||||
|
|
@ -21,7 +22,8 @@ use {
|
||||||
},
|
},
|
||||||
wl_seat::{wl_pointer::PendingScroll, Dnd, NodeSeatState, SeatId, WlSeatGlobal},
|
wl_seat::{wl_pointer::PendingScroll, Dnd, NodeSeatState, SeatId, WlSeatGlobal},
|
||||||
wl_surface::{
|
wl_surface::{
|
||||||
cursor::CursorSurface, wl_subsurface::WlSubsurface, wp_viewport::WpViewport,
|
cursor::CursorSurface, wl_subsurface::WlSubsurface,
|
||||||
|
wp_fractional_scale_v1::WpFractionalScaleV1, wp_viewport::WpViewport,
|
||||||
xdg_surface::XdgSurfaceError, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error,
|
xdg_surface::XdgSurfaceError, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error,
|
||||||
},
|
},
|
||||||
wp_presentation_feedback::WpPresentationFeedback,
|
wp_presentation_feedback::WpPresentationFeedback,
|
||||||
|
|
@ -31,7 +33,10 @@ use {
|
||||||
rect::{Rect, Region},
|
rect::{Rect, Region},
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
state::DeviceHandlerData,
|
state::DeviceHandlerData,
|
||||||
tree::{FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelNode},
|
tree::{
|
||||||
|
FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, NodeVisitorBase, OutputNode,
|
||||||
|
ToplevelNode,
|
||||||
|
},
|
||||||
utils::{
|
utils::{
|
||||||
buffd::{MsgParser, MsgParserError},
|
buffd::{MsgParser, MsgParserError},
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
|
|
@ -207,6 +212,14 @@ impl SurfaceRole {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SurfaceSendPreferredScaleVisitor(pub Fixed);
|
||||||
|
impl NodeVisitorBase for SurfaceSendPreferredScaleVisitor {
|
||||||
|
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||||
|
node.send_preferred_scale();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WlSurface {
|
pub struct WlSurface {
|
||||||
pub id: WlSurfaceId,
|
pub id: WlSurfaceId,
|
||||||
pub node_id: SurfaceNodeId,
|
pub node_id: SurfaceNodeId,
|
||||||
|
|
@ -239,6 +252,8 @@ pub struct WlSurface {
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
idle_inhibitors: CopyHashMap<ZwpIdleInhibitorV1Id, Rc<ZwpIdleInhibitorV1>>,
|
idle_inhibitors: CopyHashMap<ZwpIdleInhibitorV1Id, Rc<ZwpIdleInhibitorV1>>,
|
||||||
viewporter: CloneCell<Option<Rc<WpViewport>>>,
|
viewporter: CloneCell<Option<Rc<WpViewport>>>,
|
||||||
|
output: CloneCell<Rc<OutputNode>>,
|
||||||
|
fractional_scale: CloneCell<Option<Rc<WpFractionalScaleV1>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WlSurface {
|
impl Debug for WlSurface {
|
||||||
|
|
@ -370,6 +385,34 @@ impl WlSurface {
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
idle_inhibitors: Default::default(),
|
idle_inhibitors: Default::default(),
|
||||||
viewporter: Default::default(),
|
viewporter: Default::default(),
|
||||||
|
output: CloneCell::new(client.state.dummy_output.get().unwrap()),
|
||||||
|
fractional_scale: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
|
let old = self.output.set(output.clone());
|
||||||
|
if old.id == output.id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
output.global.send_enter(self);
|
||||||
|
old.global.send_leave(self);
|
||||||
|
if old.preferred_scale.get() != output.preferred_scale.get() {
|
||||||
|
if let Some(fs) = self.fractional_scale.get() {
|
||||||
|
fs.send_preferred_scale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let children = self.children.borrow_mut();
|
||||||
|
if let Some(children) = &*children {
|
||||||
|
for ss in children.subsurfaces.values() {
|
||||||
|
ss.surface.set_output(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_preferred_scale(&self) {
|
||||||
|
if let Some(fs) = self.fractional_scale.get() {
|
||||||
|
fs.send_preferred_scale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,13 +466,20 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_enter(&self, output: WlOutputId) {
|
pub fn send_enter(&self, output: WlOutputId) {
|
||||||
self.client.event(Enter {
|
self.client.event(Enter {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
output,
|
output,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_leave(&self, output: WlOutputId) {
|
||||||
|
self.client.event(Leave {
|
||||||
|
self_id: self.id,
|
||||||
|
output,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn set_toplevel(&self, tl: Option<Rc<dyn ToplevelNode>>) {
|
fn set_toplevel(&self, tl: Option<Rc<dyn ToplevelNode>>) {
|
||||||
let ch = self.children.borrow();
|
let ch = self.children.borrow();
|
||||||
if let Some(ch) = &*ch {
|
if let Some(ch) = &*ch {
|
||||||
|
|
@ -967,6 +1017,7 @@ impl Object for WlSurface {
|
||||||
self.pending.presentation_feedback.borrow_mut().clear();
|
self.pending.presentation_feedback.borrow_mut().clear();
|
||||||
self.presentation_feedback.borrow_mut().clear();
|
self.presentation_feedback.borrow_mut().clear();
|
||||||
self.viewporter.take();
|
self.viewporter.take();
|
||||||
|
self.fractional_scale.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cursor::Cursor,
|
cursor::Cursor,
|
||||||
|
fixed::Fixed,
|
||||||
ifs::{wl_seat::WlSeatGlobal, wl_surface::WlSurface},
|
ifs::{wl_seat::WlSeatGlobal, wl_surface::WlSurface},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
|
tree::OutputNode,
|
||||||
},
|
},
|
||||||
std::{cell::Cell, rc::Rc},
|
std::{cell::Cell, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
@ -13,7 +15,6 @@ pub struct CursorSurface {
|
||||||
seat: Rc<WlSeatGlobal>,
|
seat: Rc<WlSeatGlobal>,
|
||||||
surface: Rc<WlSurface>,
|
surface: Rc<WlSurface>,
|
||||||
hotspot: Cell<(i32, i32)>,
|
hotspot: Cell<(i32, i32)>,
|
||||||
pos: Cell<(i32, i32)>,
|
|
||||||
extents: Cell<Rect>,
|
extents: Cell<Rect>,
|
||||||
pub tracker: Tracker<Self>,
|
pub tracker: Tracker<Self>,
|
||||||
}
|
}
|
||||||
|
|
@ -24,25 +25,16 @@ impl CursorSurface {
|
||||||
seat: seat.clone(),
|
seat: seat.clone(),
|
||||||
surface: surface.clone(),
|
surface: surface.clone(),
|
||||||
hotspot: Cell::new((0, 0)),
|
hotspot: Cell::new((0, 0)),
|
||||||
pos: Cell::new((0, 0)),
|
|
||||||
extents: Cell::new(Default::default()),
|
extents: Cell::new(Default::default()),
|
||||||
tracker: Default::default(),
|
tracker: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_extents(&self) {
|
fn update_extents(&self) {
|
||||||
let (pos_x, pos_y) = self.pos.get();
|
|
||||||
let extents = self.extents.get();
|
let extents = self.extents.get();
|
||||||
let (hot_x, hot_y) = self.hotspot.get();
|
let (hot_x, hot_y) = self.hotspot.get();
|
||||||
self.extents.set(
|
self.extents
|
||||||
Rect::new_sized(
|
.set(Rect::new_sized(-hot_x, -hot_y, extents.width(), extents.height()).unwrap());
|
||||||
pos_x - hot_x,
|
|
||||||
pos_y - hot_y,
|
|
||||||
extents.width(),
|
|
||||||
extents.height(),
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_surface_destroy(&self) {
|
pub fn handle_surface_destroy(&self) {
|
||||||
|
|
@ -69,21 +61,25 @@ impl CursorSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor for CursorSurface {
|
impl Cursor for CursorSurface {
|
||||||
fn set_position(&self, x: i32, y: i32) {
|
fn render(&self, renderer: &mut Renderer, x: Fixed, y: Fixed) {
|
||||||
self.pos.set((x, y));
|
let extents = self.extents.get().move_(x.round_down(), y.round_down());
|
||||||
self.update_extents();
|
if extents.intersects(&renderer.logical_extents()) {
|
||||||
|
let scale = renderer.scale();
|
||||||
|
if scale != 1 {
|
||||||
|
let scale = scale.to_f64();
|
||||||
|
let (hot_x, hot_y) = self.hotspot.get();
|
||||||
|
let (hot_x, hot_y) = (Fixed::from_int(hot_x), Fixed::from_int(hot_y));
|
||||||
|
let x = ((x - hot_x).to_f64() * scale).round() as _;
|
||||||
|
let y = ((y - hot_y).to_f64() * scale).round() as _;
|
||||||
|
renderer.render_surface_scaled(&self.surface, x, y, None);
|
||||||
|
} else {
|
||||||
|
renderer.render_surface(&self.surface, extents.x1(), extents.y1());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
|
fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
renderer.render_surface(&self.surface, x, y);
|
self.surface.set_output(output);
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hotspot(&self) -> (i32, i32) {
|
|
||||||
self.hotspot.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extents(&self) -> Rect {
|
|
||||||
self.extents.get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_unset(&self) {
|
fn handle_unset(&self) {
|
||||||
|
|
|
||||||
78
src/ifs/wl_surface/wp_fractional_scale_v1.rs
Normal file
78
src/ifs/wl_surface/wp_fractional_scale_v1.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
ifs::wl_surface::WlSurface,
|
||||||
|
leaks::Tracker,
|
||||||
|
object::Object,
|
||||||
|
utils::buffd::{MsgParser, MsgParserError},
|
||||||
|
wire::{wp_fractional_scale_v1::*, WpFractionalScaleV1Id},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct WpFractionalScaleV1 {
|
||||||
|
pub id: WpFractionalScaleV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub surface: Rc<WlSurface>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WpFractionalScaleV1 {
|
||||||
|
pub fn new(id: WpFractionalScaleV1Id, surface: &Rc<WlSurface>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
client: surface.client.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: &Rc<Self>) -> Result<(), WpFractionalScaleError> {
|
||||||
|
if self.surface.fractional_scale.get().is_some() {
|
||||||
|
return Err(WpFractionalScaleError::Exists);
|
||||||
|
}
|
||||||
|
self.surface.fractional_scale.set(Some(self.clone()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_preferred_scale(&self) {
|
||||||
|
self.client.event(PreferredScale {
|
||||||
|
self_id: self.id,
|
||||||
|
scale: self.surface.output.get().preferred_scale.get(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpFractionalScaleError> {
|
||||||
|
let _req: Destroy = self.client.parse(self, msg)?;
|
||||||
|
self.surface.fractional_scale.take();
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
WpFractionalScaleV1;
|
||||||
|
|
||||||
|
DESTROY => destroy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for WpFractionalScaleV1 {
|
||||||
|
fn num_requests(&self) -> u32 {
|
||||||
|
DESTROY + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_add_obj!(WpFractionalScaleV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum WpFractionalScaleError {
|
||||||
|
#[error("Parsing failed")]
|
||||||
|
MsgParserError(#[source] Box<MsgParserError>),
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("The surface already has a fractional scale extension attached")]
|
||||||
|
Exists,
|
||||||
|
}
|
||||||
|
efrom!(WpFractionalScaleError, MsgParserError);
|
||||||
|
efrom!(WpFractionalScaleError, ClientError);
|
||||||
|
|
@ -124,6 +124,7 @@ impl XdgSurface {
|
||||||
|
|
||||||
fn set_workspace(&self, ws: &Rc<WorkspaceNode>) {
|
fn set_workspace(&self, ws: &Rc<WorkspaceNode>) {
|
||||||
self.workspace.set(Some(ws.clone()));
|
self.workspace.set(Some(ws.clone()));
|
||||||
|
self.surface.set_output(&ws.output.get());
|
||||||
let pu = self.popups.lock();
|
let pu = self.popups.lock();
|
||||||
for pu in pu.values() {
|
for pu in pu.values() {
|
||||||
pu.xdg.set_workspace(ws);
|
pu.xdg.set_workspace(ws);
|
||||||
|
|
|
||||||
|
|
@ -456,8 +456,7 @@ impl ToplevelNode for XdgToplevel {
|
||||||
Some(self.xdg.surface.clone())
|
Some(self.xdg.surface.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
fn tl_set_workspace_ext(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
||||||
self.toplevel_data.workspace.set(Some(ws.clone()));
|
|
||||||
self.xdg.set_workspace(ws);
|
self.xdg.set_workspace(ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -548,12 +547,7 @@ impl XdgSurfaceExt for XdgToplevel {
|
||||||
self.extents_changed();
|
self.extents_changed();
|
||||||
if let Some(workspace) = self.xdg.workspace.get() {
|
if let Some(workspace) = self.xdg.workspace.get() {
|
||||||
let output = workspace.output.get();
|
let output = workspace.output.get();
|
||||||
let bindings = output.global.bindings.borrow_mut();
|
surface.set_output(&output);
|
||||||
if let Some(binding) = bindings.get(&self.xdg.surface.client.id) {
|
|
||||||
for binding in binding.values() {
|
|
||||||
self.xdg.surface.send_enter(binding.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// {
|
// {
|
||||||
// let seats = surface.client.state.globals.lock_seats();
|
// let seats = surface.client.state.globals.lock_seats();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use {
|
||||||
state::State,
|
state::State,
|
||||||
tree::{
|
tree::{
|
||||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, StackedNode,
|
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, StackedNode,
|
||||||
ToplevelData, ToplevelNode,
|
ToplevelData, ToplevelNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
|
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
|
||||||
wire::WlSurfaceId,
|
wire::WlSurfaceId,
|
||||||
|
|
@ -401,6 +401,10 @@ impl ToplevelNode for Xwindow {
|
||||||
Some(self.surface.clone())
|
Some(self.surface.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_set_workspace_ext(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
||||||
|
self.surface.set_output(&ws.output.get());
|
||||||
|
}
|
||||||
|
|
||||||
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
||||||
// log::info!("xwin {} change_extents {:?}", self.data.window_id, rect);
|
// log::info!("xwin {} change_extents {:?}", self.data.window_id, rect);
|
||||||
let old = self.data.info.extents.replace(*rect);
|
let old = self.data.info.extents.replace(*rect);
|
||||||
|
|
|
||||||
112
src/ifs/wp_fractional_scale_manager_v1.rs
Normal file
112
src/ifs/wp_fractional_scale_manager_v1.rs
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
globals::{Global, GlobalName},
|
||||||
|
ifs::wl_surface::wp_fractional_scale_v1::{WpFractionalScaleError, WpFractionalScaleV1},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::Object,
|
||||||
|
utils::buffd::{MsgParser, MsgParserError},
|
||||||
|
wire::{wp_fractional_scale_manager_v1::*, WpFractionalScaleManagerV1Id},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct WpFractionalScaleManagerV1Global {
|
||||||
|
pub name: GlobalName,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WpFractionalScaleManagerV1 {
|
||||||
|
pub id: WpFractionalScaleManagerV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WpFractionalScaleManagerV1Global {
|
||||||
|
pub fn new(name: GlobalName) -> Self {
|
||||||
|
Self { name }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_(
|
||||||
|
self: Rc<Self>,
|
||||||
|
id: WpFractionalScaleManagerV1Id,
|
||||||
|
client: &Rc<Client>,
|
||||||
|
_version: u32,
|
||||||
|
) -> Result<(), WpFractionalScaleManagerError> {
|
||||||
|
let obj = Rc::new(WpFractionalScaleManagerV1 {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
});
|
||||||
|
track!(client, obj);
|
||||||
|
client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_base!(
|
||||||
|
WpFractionalScaleManagerV1Global,
|
||||||
|
WpFractionalScaleManagerV1,
|
||||||
|
WpFractionalScaleManagerError
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Global for WpFractionalScaleManagerV1Global {
|
||||||
|
fn singleton(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_add_global!(WpFractionalScaleManagerV1Global);
|
||||||
|
|
||||||
|
impl WpFractionalScaleManagerV1 {
|
||||||
|
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpFractionalScaleManagerError> {
|
||||||
|
let _req: Destroy = self.client.parse(self, msg)?;
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_fractional_scale(
|
||||||
|
&self,
|
||||||
|
msg: MsgParser<'_, '_>,
|
||||||
|
) -> Result<(), WpFractionalScaleManagerError> {
|
||||||
|
let req: GetFractionalScale = self.client.parse(self, msg)?;
|
||||||
|
let surface = self.client.lookup(req.surface)?;
|
||||||
|
let fs = Rc::new(WpFractionalScaleV1::new(req.id, &surface));
|
||||||
|
track!(self.client, fs);
|
||||||
|
fs.install()?;
|
||||||
|
self.client.add_client_obj(&fs)?;
|
||||||
|
fs.send_preferred_scale();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
WpFractionalScaleManagerV1;
|
||||||
|
|
||||||
|
DESTROY => destroy,
|
||||||
|
GET_FRACTIONAL_SCALE => get_fractional_scale,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for WpFractionalScaleManagerV1 {
|
||||||
|
fn num_requests(&self) -> u32 {
|
||||||
|
GET_FRACTIONAL_SCALE + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_add_obj!(WpFractionalScaleManagerV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum WpFractionalScaleManagerError {
|
||||||
|
#[error("Parsing failed")]
|
||||||
|
MsgParserError(#[source] Box<MsgParserError>),
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error(transparent)]
|
||||||
|
WpFractionalScaleError(#[from] WpFractionalScaleError),
|
||||||
|
}
|
||||||
|
efrom!(WpFractionalScaleManagerError, MsgParserError);
|
||||||
|
efrom!(WpFractionalScaleManagerError, ClientError);
|
||||||
12
src/pango.rs
12
src/pango.rs
|
|
@ -63,6 +63,8 @@ extern "C" {
|
||||||
|
|
||||||
fn pango_font_description_from_string(str: *const c::c_char) -> *mut PangoFontDescription_;
|
fn pango_font_description_from_string(str: *const c::c_char) -> *mut PangoFontDescription_;
|
||||||
fn pango_font_description_free(desc: *mut PangoFontDescription_);
|
fn pango_font_description_free(desc: *mut PangoFontDescription_);
|
||||||
|
fn pango_font_description_get_size(desc: *mut PangoFontDescription_) -> c::c_int;
|
||||||
|
fn pango_font_description_set_size(desc: *mut PangoFontDescription_, size: c::c_int);
|
||||||
|
|
||||||
fn pango_layout_new(context: *mut PangoContext_) -> *mut PangoLayout_;
|
fn pango_layout_new(context: *mut PangoContext_) -> *mut PangoLayout_;
|
||||||
fn pango_layout_set_width(layout: *mut PangoLayout_, width: c::c_int);
|
fn pango_layout_set_width(layout: *mut PangoLayout_, width: c::c_int);
|
||||||
|
|
@ -256,6 +258,16 @@ impl PangoFontDescription {
|
||||||
s: unsafe { pango_font_description_from_string(s.as_ptr()) },
|
s: unsafe { pango_font_description_from_string(s.as_ptr()) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> i32 {
|
||||||
|
unsafe { pango_font_description_get_size(self.s) as _ }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_size(&mut self, size: i32) {
|
||||||
|
unsafe {
|
||||||
|
pango_font_description_set_size(self.s, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for PangoFontDescription {
|
impl Drop for PangoFontDescription {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
fixed::Fixed,
|
||||||
format::{Format, XRGB8888},
|
format::{Format, XRGB8888},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
render::{
|
render::{
|
||||||
|
|
@ -55,14 +56,19 @@ impl Framebuffer {
|
||||||
glViewport(0, 0, self.gl.width, self.gl.height);
|
glViewport(0, 0, self.gl.width, self.gl.height);
|
||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
}
|
}
|
||||||
|
let scale = Fixed::from_int(1);
|
||||||
let mut renderer = Renderer {
|
let mut renderer = Renderer {
|
||||||
ctx: &self.ctx,
|
ctx: &self.ctx,
|
||||||
fb: &self.gl,
|
fb: &self.gl,
|
||||||
state,
|
state,
|
||||||
on_output: false,
|
on_output: false,
|
||||||
result: &mut RenderResult::default(),
|
result: &mut RenderResult::default(),
|
||||||
|
scaled: false,
|
||||||
|
scale,
|
||||||
|
scalef: 1.0,
|
||||||
|
logical_extents: Rect::new_sized(0, 0, self.gl.width, self.gl.height).unwrap(),
|
||||||
};
|
};
|
||||||
renderer.render_texture(texture, x, y, XRGB8888, None, None);
|
renderer.render_texture(texture, x, y, XRGB8888, None, None, scale);
|
||||||
unsafe {
|
unsafe {
|
||||||
glFlush();
|
glFlush();
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +112,7 @@ impl Framebuffer {
|
||||||
cursor_rect: Option<Rect>,
|
cursor_rect: Option<Rect>,
|
||||||
on_output: bool,
|
on_output: bool,
|
||||||
result: &mut RenderResult,
|
result: &mut RenderResult,
|
||||||
|
scale: Fixed,
|
||||||
) {
|
) {
|
||||||
let _ = self.ctx.ctx.with_current(|| {
|
let _ = self.ctx.ctx.with_current(|| {
|
||||||
let c = state.theme.colors.background.get();
|
let c = state.theme.colors.background.get();
|
||||||
|
|
@ -122,29 +129,31 @@ impl Framebuffer {
|
||||||
state,
|
state,
|
||||||
on_output,
|
on_output,
|
||||||
result,
|
result,
|
||||||
|
scaled: scale != 1,
|
||||||
|
scale,
|
||||||
|
scalef: scale.to_f64(),
|
||||||
|
logical_extents: node.node_absolute_position().at_point(0, 0),
|
||||||
};
|
};
|
||||||
node.node_render(&mut renderer, 0, 0);
|
node.node_render(&mut renderer, 0, 0);
|
||||||
if let Some(rect) = cursor_rect {
|
if let Some(rect) = cursor_rect {
|
||||||
let seats = state.globals.lock_seats();
|
let seats = state.globals.lock_seats();
|
||||||
for seat in seats.values() {
|
for seat in seats.values() {
|
||||||
if let Some(cursor) = seat.get_cursor() {
|
if let Some(cursor) = seat.get_cursor() {
|
||||||
cursor.tick();
|
let (mut x, mut y) = seat.get_position();
|
||||||
let extents = cursor.extents();
|
|
||||||
if let Some(dnd_icon) = seat.dnd_icon() {
|
if let Some(dnd_icon) = seat.dnd_icon() {
|
||||||
let (x_hot, y_hot) = cursor.get_hotspot();
|
|
||||||
let extents = dnd_icon.extents.get().move_(
|
let extents = dnd_icon.extents.get().move_(
|
||||||
extents.x1() + x_hot + dnd_icon.buf_x.get(),
|
x.round_down() + dnd_icon.buf_x.get(),
|
||||||
extents.y1() + y_hot + dnd_icon.buf_y.get(),
|
y.round_down() + dnd_icon.buf_y.get(),
|
||||||
);
|
);
|
||||||
if extents.intersects(&rect) {
|
if extents.intersects(&rect) {
|
||||||
let (x, y) = rect.translate(extents.x1(), extents.y1());
|
let (x, y) = rect.translate(extents.x1(), extents.y1());
|
||||||
renderer.render_surface(&dnd_icon, x, y);
|
renderer.render_surface(&dnd_icon, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if extents.intersects(&rect) {
|
cursor.tick();
|
||||||
let (x, y) = rect.translate(extents.x1(), extents.y1());
|
x -= Fixed::from_int(rect.x1());
|
||||||
cursor.render(&mut renderer, x, y);
|
y -= Fixed::from_int(rect.y1());
|
||||||
}
|
cursor.render(&mut renderer, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
fixed::Fixed,
|
||||||
format::{Format, ARGB8888},
|
format::{Format, ARGB8888},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
wl_buffer::WlBuffer,
|
||||||
|
|
@ -59,9 +60,44 @@ pub struct Renderer<'a> {
|
||||||
pub(super) state: &'a State,
|
pub(super) state: &'a State,
|
||||||
pub(super) on_output: bool,
|
pub(super) on_output: bool,
|
||||||
pub(super) result: &'a mut RenderResult,
|
pub(super) result: &'a mut RenderResult,
|
||||||
|
pub(super) scaled: bool,
|
||||||
|
pub(super) scale: Fixed,
|
||||||
|
pub(super) scalef: f64,
|
||||||
|
pub(super) logical_extents: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer<'_> {
|
impl Renderer<'_> {
|
||||||
|
pub fn scale(&self) -> Fixed {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn physical_extents(&self) -> Rect {
|
||||||
|
Rect::new_sized(0, 0, self.fb.width, self.fb.height).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn logical_extents(&self) -> Rect {
|
||||||
|
self.logical_extents
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn scale_point(&self, mut x: i32, mut y: i32) -> (i32, i32) {
|
||||||
|
if self.scaled {
|
||||||
|
x = (x as f64 * self.scalef).round() as _;
|
||||||
|
y = (y as f64 * self.scalef).round() as _;
|
||||||
|
}
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scale_rect(&self, mut rect: Rect) -> Rect {
|
||||||
|
if self.scaled {
|
||||||
|
let x1 = (rect.x1() as f64 * self.scalef).round() as _;
|
||||||
|
let y1 = (rect.y1() as f64 * self.scalef).round() as _;
|
||||||
|
let x2 = (rect.x2() as f64 * self.scalef).round() as _;
|
||||||
|
let y2 = (rect.y2() as f64 * self.scalef).round() as _;
|
||||||
|
rect = Rect::new(x1, y1, x2, y2).unwrap();
|
||||||
|
}
|
||||||
|
rect
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_display(&mut self, display: &DisplayNode, x: i32, y: i32) {
|
pub fn render_display(&mut self, display: &DisplayNode, x: i32, y: i32) {
|
||||||
let ext = display.extents.get();
|
let ext = display.extents.get();
|
||||||
let outputs = display.outputs.lock();
|
let outputs = display.outputs.lock();
|
||||||
|
|
@ -121,25 +157,14 @@ impl Renderer<'_> {
|
||||||
self.fill_boxes2(slice::from_ref(&rd.underline), &c, x, y);
|
self.fill_boxes2(slice::from_ref(&rd.underline), &c, x, y);
|
||||||
let c = theme.colors.unfocused_title_background.get();
|
let c = theme.colors.unfocused_title_background.get();
|
||||||
self.fill_boxes2(&rd.inactive_workspaces, &c, x, y);
|
self.fill_boxes2(&rd.inactive_workspaces, &c, x, y);
|
||||||
|
let scale = output.preferred_scale.get();
|
||||||
for title in &rd.titles {
|
for title in &rd.titles {
|
||||||
self.render_texture(
|
let (x, y) = self.scale_point(x + title.tex_x, y + title.tex_y);
|
||||||
&title.tex,
|
self.render_texture(&title.tex, x, y, ARGB8888, None, None, scale);
|
||||||
x + title.tex_x,
|
|
||||||
y + title.tex_y,
|
|
||||||
ARGB8888,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if let Some(status) = &rd.status {
|
if let Some(status) = &rd.status {
|
||||||
self.render_texture(
|
let (x, y) = self.scale_point(x + status.tex_x, y + status.tex_y);
|
||||||
&status.tex,
|
self.render_texture(&status.tex, x, y, ARGB8888, None, None, scale);
|
||||||
x + status.tex_x,
|
|
||||||
y + status.tex_y,
|
|
||||||
ARGB8888,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ws) = output.workspace.get() {
|
if let Some(ws) = output.workspace.get() {
|
||||||
|
|
@ -180,8 +205,10 @@ impl Renderer<'_> {
|
||||||
if boxes.is_empty() {
|
if boxes.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let (dx, dy) = self.scale_point(dx, dy);
|
||||||
let mut pos = Vec::with_capacity(boxes.len() * 12);
|
let mut pos = Vec::with_capacity(boxes.len() * 12);
|
||||||
for bx in boxes {
|
for bx in boxes {
|
||||||
|
let bx = self.scale_rect(*bx);
|
||||||
let x1 = self.x_to_f(bx.x1() + dx);
|
let x1 = self.x_to_f(bx.x1() + dx);
|
||||||
let y1 = self.y_to_f(bx.y1() + dy);
|
let y1 = self.y_to_f(bx.y1() + dy);
|
||||||
let x2 = self.x_to_f(bx.x2() + dx);
|
let x2 = self.x_to_f(bx.x2() + dx);
|
||||||
|
|
@ -220,10 +247,10 @@ impl Renderer<'_> {
|
||||||
std::slice::from_ref(&pos.at_point(x, y)),
|
std::slice::from_ref(&pos.at_point(x, y)),
|
||||||
&Color::from_rgba_straight(20, 20, 20, 255),
|
&Color::from_rgba_straight(20, 20, 20, 255),
|
||||||
);
|
);
|
||||||
if let Some(tex) = placeholder.texture.get() {
|
if let Some(tex) = placeholder.textures.get(&self.scale) {
|
||||||
let x = x + (pos.width() - tex.width()) / 2;
|
let x = x + (pos.width() - tex.width()) / 2;
|
||||||
let y = y + (pos.height() - tex.height()) / 2;
|
let y = y + (pos.height() - tex.height()) / 2;
|
||||||
self.render_texture(&tex, x, y, &ARGB8888, None, None);
|
self.render_texture(&tex, x, y, &ARGB8888, None, None, self.scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,13 +274,17 @@ impl Renderer<'_> {
|
||||||
.get();
|
.get();
|
||||||
self.fill_boxes2(std::slice::from_ref(lar), &c, x, y);
|
self.fill_boxes2(std::slice::from_ref(lar), &c, x, y);
|
||||||
}
|
}
|
||||||
for title in &rd.titles {
|
if let Some(titles) = rd.titles.get(&self.scale) {
|
||||||
self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888, None, None);
|
for title in titles {
|
||||||
|
let (x, y) = self.scale_point(x + title.x, y + title.y);
|
||||||
|
self.render_texture(&title.tex, x, y, ARGB8888, None, None, self.scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(child) = container.mono_child.get() {
|
if let Some(child) = container.mono_child.get() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let body = container.mono_body.get().move_(x, y);
|
let body = container.mono_body.get().move_(x, y);
|
||||||
|
let body = self.scale_rect(body);
|
||||||
with_scissor(&body, || {
|
with_scissor(&body, || {
|
||||||
let content = container.mono_content.get();
|
let content = container.mono_content.get();
|
||||||
child
|
child
|
||||||
|
|
@ -268,6 +299,7 @@ impl Renderer<'_> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let body = body.move_(x, y);
|
let body = body.move_(x, y);
|
||||||
|
let body = self.scale_rect(body);
|
||||||
unsafe {
|
unsafe {
|
||||||
with_scissor(&body, || {
|
with_scissor(&body, || {
|
||||||
let content = child.content.get();
|
let content = child.content.get();
|
||||||
|
|
@ -291,6 +323,17 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32) {
|
pub fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32) {
|
||||||
|
let (x, y) = self.scale_point(x, y);
|
||||||
|
self.render_surface_scaled(surface, x, y, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_surface_scaled(
|
||||||
|
&mut self,
|
||||||
|
surface: &WlSurface,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
pos_rel: Option<(i32, i32)>,
|
||||||
|
) {
|
||||||
let children = surface.children.borrow();
|
let children = surface.children.borrow();
|
||||||
let buffer = match surface.buffer.get() {
|
let buffer = match surface.buffer.get() {
|
||||||
Some(b) => b,
|
Some(b) => b,
|
||||||
|
|
@ -302,7 +345,14 @@ impl Renderer<'_> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let tpoints = surface.buffer_points_norm.borrow_mut();
|
let tpoints = surface.buffer_points_norm.borrow_mut();
|
||||||
let size = surface.buffer_abs_pos.get().size();
|
let mut size = surface.buffer_abs_pos.get().size();
|
||||||
|
if let Some((x_rel, y_rel)) = pos_rel {
|
||||||
|
let (x, y) = self.scale_point(x_rel, y_rel);
|
||||||
|
let (w, h) = self.scale_point(x_rel + size.0, y_rel + size.1);
|
||||||
|
size = (w - x, h - y);
|
||||||
|
} else {
|
||||||
|
size = self.scale_point(size.0, size.1);
|
||||||
|
}
|
||||||
if let Some(children) = children.deref() {
|
if let Some(children) = children.deref() {
|
||||||
macro_rules! render {
|
macro_rules! render {
|
||||||
($children:expr) => {
|
($children:expr) => {
|
||||||
|
|
@ -311,7 +361,13 @@ impl Renderer<'_> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let pos = child.sub_surface.position.get();
|
let pos = child.sub_surface.position.get();
|
||||||
self.render_surface(&child.sub_surface.surface, x + pos.x1(), y + pos.y1());
|
let (x1, y1) = self.scale_point(pos.x1(), pos.y1());
|
||||||
|
self.render_surface_scaled(
|
||||||
|
&child.sub_surface.surface,
|
||||||
|
x + x1,
|
||||||
|
y + y1,
|
||||||
|
Some((pos.x1(), pos.y1())),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -342,7 +398,15 @@ impl Renderer<'_> {
|
||||||
tsize: (i32, i32),
|
tsize: (i32, i32),
|
||||||
) {
|
) {
|
||||||
if let Some(tex) = buffer.texture.get() {
|
if let Some(tex) = buffer.texture.get() {
|
||||||
self.render_texture(&tex, x, y, buffer.format, Some(tpoints), Some(tsize));
|
self.render_texture(
|
||||||
|
&tex,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
buffer.format,
|
||||||
|
Some(tpoints),
|
||||||
|
Some(tsize),
|
||||||
|
self.scale,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -354,6 +418,7 @@ impl Renderer<'_> {
|
||||||
format: &Format,
|
format: &Format,
|
||||||
tpoints: Option<&[f32; 8]>,
|
tpoints: Option<&[f32; 8]>,
|
||||||
tsize: Option<(i32, i32)>,
|
tsize: Option<(i32, i32)>,
|
||||||
|
tscale: Fixed,
|
||||||
) {
|
) {
|
||||||
assert!(rc_eq(&self.ctx.ctx, &texture.ctx.ctx));
|
assert!(rc_eq(&self.ctx.ctx, &texture.ctx.ctx));
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -402,7 +467,13 @@ impl Renderer<'_> {
|
||||||
let (twidth, theight) = if let Some(size) = tsize {
|
let (twidth, theight) = if let Some(size) = tsize {
|
||||||
size
|
size
|
||||||
} else {
|
} else {
|
||||||
(texture.gl.width, texture.gl.height)
|
let (mut w, mut h) = (texture.gl.width, texture.gl.height);
|
||||||
|
if tscale != self.scale {
|
||||||
|
let tscale = tscale.to_f64();
|
||||||
|
w = (w as f64 * self.scalef / tscale).round() as _;
|
||||||
|
h = (h as f64 * self.scalef / tscale).round() as _;
|
||||||
|
}
|
||||||
|
(w, h)
|
||||||
};
|
};
|
||||||
|
|
||||||
let x1 = 2.0 * (x as f32 / f_width) - 1.0;
|
let x1 = 2.0 * (x as f32 / f_width) - 1.0;
|
||||||
|
|
@ -466,8 +537,9 @@ impl Renderer<'_> {
|
||||||
let title_underline =
|
let title_underline =
|
||||||
[Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap()];
|
[Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap()];
|
||||||
self.fill_boxes(&title_underline, &uc);
|
self.fill_boxes(&title_underline, &uc);
|
||||||
if let Some(title) = floating.title_texture.get() {
|
if let Some(title) = floating.title_textures.get(&self.scale) {
|
||||||
self.render_texture(&title, x + bw, y + bw, ARGB8888, None, None);
|
let (x, y) = self.scale_point(x + bw, y + bw);
|
||||||
|
self.render_texture(&title, x, y, ARGB8888, None, None, self.scale);
|
||||||
}
|
}
|
||||||
let body = Rect::new_sized(
|
let body = Rect::new_sized(
|
||||||
x + bw,
|
x + bw,
|
||||||
|
|
@ -476,8 +548,9 @@ impl Renderer<'_> {
|
||||||
pos.height() - 2 * bw - th - 1,
|
pos.height() - 2 * bw - th - 1,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let scissor_body = self.scale_rect(body);
|
||||||
unsafe {
|
unsafe {
|
||||||
with_scissor(&body, || {
|
with_scissor(&scissor_body, || {
|
||||||
child.node_render(self, body.x1(), body.y1());
|
child.node_render(self, body.x1(), body.y1());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -486,6 +559,7 @@ impl Renderer<'_> {
|
||||||
pub fn render_layer_surface(&mut self, surface: &ZwlrLayerSurfaceV1, x: i32, y: i32) {
|
pub fn render_layer_surface(&mut self, surface: &ZwlrLayerSurfaceV1, x: i32, y: i32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let body = surface.position().at_point(x, y);
|
let body = surface.position().at_point(x, y);
|
||||||
|
let body = self.scale_rect(body);
|
||||||
with_scissor(&body, || {
|
with_scissor(&body, || {
|
||||||
self.render_surface(&surface.surface, x, y);
|
self.render_surface(&surface.surface, x, y);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
fixed::Fixed,
|
||||||
format::XRGB8888,
|
format::XRGB8888,
|
||||||
render::RenderError,
|
render::RenderError,
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -59,6 +60,7 @@ pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError>
|
||||||
Some(state.root.extents.get()),
|
Some(state.root.extents.get()),
|
||||||
false,
|
false,
|
||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
|
Fixed::from_int(1),
|
||||||
);
|
);
|
||||||
let drm = ctx.gbm.drm.dup_render()?.fd().clone();
|
let drm = ctx.gbm.drm.dup_render()?.fd().clone();
|
||||||
Ok(Screenshot { drm, bo })
|
Ok(Screenshot { drm, bo })
|
||||||
|
|
|
||||||
112
src/state.rs
112
src/state.rs
|
|
@ -12,6 +12,7 @@ use {
|
||||||
config::ConfigProxy,
|
config::ConfigProxy,
|
||||||
cursor::ServerCursors,
|
cursor::ServerCursors,
|
||||||
dbus::Dbus,
|
dbus::Dbus,
|
||||||
|
fixed::Fixed,
|
||||||
forker::ForkerProxy,
|
forker::ForkerProxy,
|
||||||
globals::{Globals, GlobalsError, WaylandGlobal},
|
globals::{Globals, GlobalsError, WaylandGlobal},
|
||||||
ifs::{
|
ifs::{
|
||||||
|
|
@ -37,7 +38,7 @@ use {
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell,
|
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell,
|
||||||
queue::AsyncQueue, run_toplevel::RunToplevel,
|
queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||||
},
|
},
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
xkbcommon::{XkbContext, XkbKeymap},
|
xkbcommon::{XkbContext, XkbKeymap},
|
||||||
|
|
@ -110,6 +111,8 @@ pub struct State {
|
||||||
pub data_offer_ids: NumCell<u64>,
|
pub data_offer_ids: NumCell<u64>,
|
||||||
pub ring: Rc<IoUring>,
|
pub ring: Rc<IoUring>,
|
||||||
pub lock: ScreenlockState,
|
pub lock: ScreenlockState,
|
||||||
|
pub scales: RefCounted<Fixed>,
|
||||||
|
pub cursor_sizes: RefCounted<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -202,10 +205,64 @@ pub struct DrmDevData {
|
||||||
pub pci_id: Option<PciId>,
|
pub pci_id: Option<PciId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UpdateTextTexturesVisitor;
|
||||||
|
impl NodeVisitorBase for UpdateTextTexturesVisitor {
|
||||||
|
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
||||||
|
node.schedule_compute_render_data();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
||||||
|
node.update_render_data();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
|
node.schedule_render_titles();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||||
|
node.update_texture();
|
||||||
|
node.node_visit_children(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
pub fn add_output_scale(&self, scale: Fixed) {
|
||||||
|
if self.scales.add(scale) {
|
||||||
|
self.output_scales_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_output_scale(&self, scale: Fixed) {
|
||||||
|
if self.scales.remove(&scale) {
|
||||||
|
self.output_scales_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_cursor_size(&self, size: u32) {
|
||||||
|
if self.cursor_sizes.add(size) {
|
||||||
|
self.cursor_sizes_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_cursor_size(&self, size: u32) {
|
||||||
|
if self.cursor_sizes.remove(&size) {
|
||||||
|
self.cursor_sizes_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_scales_changed(&self) {
|
||||||
|
UpdateTextTexturesVisitor.visit_display(&self.root);
|
||||||
|
self.reload_cursors();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cursor_sizes_changed(&self) {
|
||||||
|
self.reload_cursors();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_render_ctx(&self, ctx: Option<&Rc<RenderContext>>) {
|
pub fn set_render_ctx(&self, ctx: Option<&Rc<RenderContext>>) {
|
||||||
self.render_ctx.set(ctx.cloned());
|
self.render_ctx.set(ctx.cloned());
|
||||||
self.render_ctx_version.fetch_add(1);
|
self.render_ctx_version.fetch_add(1);
|
||||||
|
self.cursors.set(None);
|
||||||
|
|
||||||
{
|
{
|
||||||
struct Walker;
|
struct Walker;
|
||||||
|
|
@ -220,11 +277,11 @@ impl State {
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
||||||
node.title_texture.set(None);
|
node.title_textures.clear();
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
||||||
node.texture.set(None);
|
node.textures.clear();
|
||||||
node.node_visit_children(self);
|
node.node_visit_children(self);
|
||||||
}
|
}
|
||||||
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
fn visit_surface(&mut self, node: &Rc<WlSurface>) {
|
||||||
|
|
@ -242,36 +299,9 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ctx) = ctx {
|
if ctx.is_some() {
|
||||||
let cursors = match ServerCursors::load(ctx) {
|
self.reload_cursors();
|
||||||
Ok(c) => Some(Rc::new(c)),
|
UpdateTextTexturesVisitor.visit_display(&self.root);
|
||||||
Err(e) => {
|
|
||||||
log::error!("Could not load the cursors: {}", ErrorFmt(e));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.cursors.set(cursors);
|
|
||||||
|
|
||||||
struct Walker;
|
|
||||||
impl NodeVisitorBase for Walker {
|
|
||||||
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
|
|
||||||
node.schedule_compute_render_data();
|
|
||||||
node.node_visit_children(self);
|
|
||||||
}
|
|
||||||
fn visit_output(&mut self, node: &Rc<OutputNode>) {
|
|
||||||
node.update_render_data();
|
|
||||||
node.node_visit_children(self);
|
|
||||||
}
|
|
||||||
fn visit_float(&mut self, node: &Rc<FloatNode>) {
|
|
||||||
node.schedule_render_titles();
|
|
||||||
node.node_visit_children(self);
|
|
||||||
}
|
|
||||||
fn visit_placeholder(&mut self, node: &Rc<PlaceholderNode>) {
|
|
||||||
node.update_texture();
|
|
||||||
node.node_visit_children(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Walker.visit_display(&self.root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let seats = self.globals.seats.lock();
|
let seats = self.globals.seats.lock();
|
||||||
|
|
@ -288,6 +318,22 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reload_cursors(&self) {
|
||||||
|
if let Some(ctx) = self.render_ctx.get() {
|
||||||
|
let cursors = match ServerCursors::load(&ctx, self) {
|
||||||
|
Ok(c) => c.map(Rc::new),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not load the cursors: {}", ErrorFmt(e));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.cursors.set(cursors);
|
||||||
|
for seat in self.globals.seats.lock().values() {
|
||||||
|
seat.reload_known_cursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_global<T: WaylandGlobal>(&self, global: &Rc<T>) {
|
pub fn add_global<T: WaylandGlobal>(&self, global: &Rc<T>) {
|
||||||
self.globals.add_global(self, global)
|
self.globals.add_global(self, global)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
|
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
|
||||||
|
fixed::Fixed,
|
||||||
ifs::wl_output::WlOutputGlobal,
|
ifs::wl_output::WlOutputGlobal,
|
||||||
state::{ConnectorData, OutputData, State},
|
state::{ConnectorData, OutputData, State},
|
||||||
tree::{OutputNode, OutputRenderData},
|
tree::{OutputNode, OutputRenderData},
|
||||||
|
|
@ -121,7 +122,9 @@ impl ConnectorHandler {
|
||||||
scroll: Default::default(),
|
scroll: Default::default(),
|
||||||
pointer_positions: Default::default(),
|
pointer_positions: Default::default(),
|
||||||
lock_surface: Default::default(),
|
lock_surface: Default::default(),
|
||||||
|
preferred_scale: Cell::new(Fixed::from_int(1)),
|
||||||
});
|
});
|
||||||
|
self.state.add_output_scale(on.preferred_scale.get());
|
||||||
let mode = info.initial_mode;
|
let mode = info.initial_mode;
|
||||||
let output_data = Rc::new(OutputData {
|
let output_data = Rc::new(OutputData {
|
||||||
connector: self.data.clone(),
|
connector: self.data.clone(),
|
||||||
|
|
@ -154,7 +157,7 @@ impl ConnectorHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for ws in ws_to_move {
|
for ws in ws_to_move {
|
||||||
ws.output.set(on.clone());
|
ws.set_output(&on);
|
||||||
on.workspaces.add_last_existing(&ws);
|
on.workspaces.add_last_existing(&ws);
|
||||||
if ws.visible_on_desired_output.get() && on.workspace.get().is_none() {
|
if ws.visible_on_desired_output.get() && on.workspace.get().is_none() {
|
||||||
on.show_workspace(&ws);
|
on.show_workspace(&ws);
|
||||||
|
|
@ -220,7 +223,7 @@ impl ConnectorHandler {
|
||||||
let is_visible =
|
let is_visible =
|
||||||
!target_is_dummy && target.workspaces.is_empty() && ws.visible.get();
|
!target_is_dummy && target.workspaces.is_empty() && ws.visible.get();
|
||||||
ws.visible_on_desired_output.set(ws.visible.get());
|
ws.visible_on_desired_output.set(ws.visible.get());
|
||||||
ws.output.set(target.clone());
|
ws.set_output(&target);
|
||||||
target.workspaces.add_last_existing(&ws);
|
target.workspaces.add_last_existing(&ws);
|
||||||
if is_visible {
|
if is_visible {
|
||||||
target.show_workspace(&ws);
|
target.show_workspace(&ws);
|
||||||
|
|
@ -243,5 +246,6 @@ impl ConnectorHandler {
|
||||||
if let Some(dev) = &self.data.drm_dev {
|
if let Some(dev) = &self.data.drm_dev {
|
||||||
dev.connectors.remove(&self.id);
|
dev.connectors.remove(&self.id);
|
||||||
}
|
}
|
||||||
|
self.state.remove_output_scale(on.preferred_scale.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
src/text.rs
28
src/text.rs
|
|
@ -40,7 +40,7 @@ struct Data {
|
||||||
layout: PangoLayout,
|
layout: PangoLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_data(font: &str, width: i32, height: i32) -> Result<Data, TextError> {
|
fn create_data(font: &str, width: i32, height: i32, scale: Option<f64>) -> Result<Data, TextError> {
|
||||||
let image = match CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height) {
|
let image = match CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return Err(TextError::CreateImage(e)),
|
Err(e) => return Err(TextError::CreateImage(e)),
|
||||||
|
|
@ -53,7 +53,10 @@ fn create_data(font: &str, width: i32, height: i32) -> Result<Data, TextError> {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => return Err(TextError::PangoContext(e)),
|
Err(e) => return Err(TextError::PangoContext(e)),
|
||||||
};
|
};
|
||||||
let fd = PangoFontDescription::from_string(font);
|
let mut fd = PangoFontDescription::from_string(font);
|
||||||
|
if let Some(scale) = scale {
|
||||||
|
fd.set_size((fd.size() as f64 * scale).round() as _);
|
||||||
|
}
|
||||||
let layout = match pctx.create_layout() {
|
let layout = match pctx.create_layout() {
|
||||||
Ok(l) => l,
|
Ok(l) => l,
|
||||||
Err(e) => return Err(TextError::CreateLayout(e)),
|
Err(e) => return Err(TextError::CreateLayout(e)),
|
||||||
|
|
@ -68,8 +71,13 @@ fn create_data(font: &str, width: i32, height: i32) -> Result<Data, TextError> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn measure(font: &str, text: &str, markup: bool) -> Result<Rect, TextError> {
|
pub fn measure(
|
||||||
let data = create_data(font, 1, 1)?;
|
font: &str,
|
||||||
|
text: &str,
|
||||||
|
markup: bool,
|
||||||
|
scale: Option<f64>,
|
||||||
|
) -> Result<Rect, TextError> {
|
||||||
|
let data = create_data(font, 1, 1, scale)?;
|
||||||
if markup {
|
if markup {
|
||||||
data.layout.set_markup(text);
|
data.layout.set_markup(text);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -85,8 +93,11 @@ pub fn render(
|
||||||
font: &str,
|
font: &str,
|
||||||
text: &str,
|
text: &str,
|
||||||
color: Color,
|
color: Color,
|
||||||
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<Texture>, TextError> {
|
) -> Result<Rc<Texture>, TextError> {
|
||||||
render2(ctx, 1, width, height, 1, font, text, color, true, false)
|
render2(
|
||||||
|
ctx, 1, width, height, 1, font, text, color, true, false, scale,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render2(
|
fn render2(
|
||||||
|
|
@ -100,8 +111,9 @@ fn render2(
|
||||||
color: Color,
|
color: Color,
|
||||||
ellipsize: bool,
|
ellipsize: bool,
|
||||||
markup: bool,
|
markup: bool,
|
||||||
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<Texture>, TextError> {
|
) -> Result<Rc<Texture>, TextError> {
|
||||||
let data = create_data(font, width, height)?;
|
let data = create_data(font, width, height, scale)?;
|
||||||
if ellipsize {
|
if ellipsize {
|
||||||
data.layout
|
data.layout
|
||||||
.set_width((width - 2 * padding).max(0) * PANGO_SCALE);
|
.set_width((width - 2 * padding).max(0) * PANGO_SCALE);
|
||||||
|
|
@ -137,8 +149,9 @@ pub fn render_fitting(
|
||||||
text: &str,
|
text: &str,
|
||||||
color: Color,
|
color: Color,
|
||||||
markup: bool,
|
markup: bool,
|
||||||
|
scale: Option<f64>,
|
||||||
) -> Result<Rc<Texture>, TextError> {
|
) -> Result<Rc<Texture>, TextError> {
|
||||||
let rect = measure(font, text, markup)?;
|
let rect = measure(font, text, markup, scale)?;
|
||||||
render2(
|
render2(
|
||||||
ctx,
|
ctx,
|
||||||
rect.x1().neg(),
|
rect.x1().neg(),
|
||||||
|
|
@ -150,5 +163,6 @@ pub fn render_fitting(
|
||||||
color,
|
color,
|
||||||
false,
|
false,
|
||||||
markup,
|
markup,
|
||||||
|
scale,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ use {
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
rc_eq::rc_eq,
|
rc_eq::rc_eq,
|
||||||
scroller::Scroller,
|
scroller::Scroller,
|
||||||
|
smallmap::SmallMapMut,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -84,7 +85,7 @@ pub struct ContainerRenderData {
|
||||||
pub last_active_rect: Option<Rect>,
|
pub last_active_rect: Option<Rect>,
|
||||||
pub border_rects: Vec<Rect>,
|
pub border_rects: Vec<Rect>,
|
||||||
pub underline_rects: Vec<Rect>,
|
pub underline_rects: Vec<Rect>,
|
||||||
pub titles: Vec<ContainerTitle>,
|
pub titles: SmallMapMut<Fixed, Vec<ContainerTitle>, 2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ContainerNode {
|
pub struct ContainerNode {
|
||||||
|
|
@ -634,7 +635,9 @@ impl ContainerNode {
|
||||||
let cwidth = self.width.get();
|
let cwidth = self.width.get();
|
||||||
let cheight = self.height.get();
|
let cheight = self.height.get();
|
||||||
let ctx = self.state.render_ctx.get();
|
let ctx = self.state.render_ctx.get();
|
||||||
rd.titles.clear();
|
for (_, v) in rd.titles.iter_mut() {
|
||||||
|
v.clear();
|
||||||
|
}
|
||||||
rd.title_rects.clear();
|
rd.title_rects.clear();
|
||||||
rd.active_title_rects.clear();
|
rd.active_title_rects.clear();
|
||||||
rd.border_rects.clear();
|
rd.border_rects.clear();
|
||||||
|
|
@ -644,6 +647,7 @@ impl ContainerNode {
|
||||||
let mono = self.mono_child.get().is_some();
|
let mono = self.mono_child.get().is_some();
|
||||||
let split = self.split.get();
|
let split = self.split.get();
|
||||||
let have_active = self.children.iter().any(|c| c.active.get());
|
let have_active = self.children.iter().any(|c| c.active.get());
|
||||||
|
let scales = self.state.scales.lock();
|
||||||
for (i, child) in self.children.iter().enumerate() {
|
for (i, child) in self.children.iter().enumerate() {
|
||||||
let rect = child.title_rect.get();
|
let rect = child.title_rect.get();
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
|
@ -670,20 +674,32 @@ impl ContainerNode {
|
||||||
let rect = Rect::new_sized(rect.x1(), rect.y2(), rect.width(), 1).unwrap();
|
let rect = Rect::new_sized(rect.x1(), rect.y2(), rect.width(), 1).unwrap();
|
||||||
rd.underline_rects.push(rect);
|
rd.underline_rects.push(rect);
|
||||||
}
|
}
|
||||||
'render_title: {
|
let title = child.title.borrow_mut();
|
||||||
let title = child.title.borrow_mut();
|
for (scale, _) in scales.iter() {
|
||||||
if th == 0 || rect.width() == 0 || title.is_empty() {
|
let titles = rd.titles.get_or_default_mut(*scale);
|
||||||
break 'render_title;
|
'render_title: {
|
||||||
}
|
let mut th = th;
|
||||||
if let Some(ctx) = &ctx {
|
let mut scalef = None;
|
||||||
match text::render(ctx, rect.width(), th, &font, title.deref(), color) {
|
let mut width = rect.width();
|
||||||
Ok(t) => rd.titles.push(ContainerTitle {
|
if *scale != 1 {
|
||||||
x: rect.x1(),
|
let scale = scale.to_f64();
|
||||||
y: rect.y1(),
|
th = (th as f64 * scale).round() as _;
|
||||||
tex: t,
|
width = (width as f64 * scale).round() as _;
|
||||||
}),
|
scalef = Some(scale);
|
||||||
Err(e) => {
|
}
|
||||||
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
if th == 0 || width == 0 || title.is_empty() {
|
||||||
|
break 'render_title;
|
||||||
|
}
|
||||||
|
if let Some(ctx) = &ctx {
|
||||||
|
match text::render(ctx, width, th, &font, title.deref(), color, scalef) {
|
||||||
|
Ok(t) => titles.push(ContainerTitle {
|
||||||
|
x: rect.x1(),
|
||||||
|
y: rect.y1(),
|
||||||
|
tex: t,
|
||||||
|
}),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -693,6 +709,7 @@ impl ContainerNode {
|
||||||
rd.underline_rects
|
rd.underline_rects
|
||||||
.push(Rect::new_sized(0, th, cwidth, 1).unwrap());
|
.push(Rect::new_sized(0, th, cwidth, 1).unwrap());
|
||||||
}
|
}
|
||||||
|
rd.titles.remove_if(|_, v| v.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate_child(self: &Rc<Self>, child: &NodeRef<ContainerChild>) {
|
fn activate_child(self: &Rc<Self>, child: &NodeRef<ContainerChild>) {
|
||||||
|
|
@ -1349,8 +1366,7 @@ impl ToplevelNode for ContainerNode {
|
||||||
self.parent.set(parent);
|
self.parent.set(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
fn tl_set_workspace_ext(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
||||||
self.toplevel_data.workspace.set(Some(ws.clone()));
|
|
||||||
for child in self.children.iter() {
|
for child in self.children.iter() {
|
||||||
child.node.clone().tl_set_workspace(ws);
|
child.node.clone().tl_set_workspace(ws);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@ use {
|
||||||
walker::NodeVisitor, ContainingNode, FindTreeResult, FoundNode, Node, NodeId,
|
walker::NodeVisitor, ContainingNode, FindTreeResult, FoundNode, Node, NodeId,
|
||||||
StackedNode, ToplevelNode, WorkspaceNode,
|
StackedNode, ToplevelNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{clonecell::CloneCell, errorfmt::ErrorFmt, linkedlist::LinkedNode},
|
utils::{
|
||||||
|
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
||||||
|
linkedlist::LinkedNode,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -39,7 +42,7 @@ pub struct FloatNode {
|
||||||
pub layout_scheduled: Cell<bool>,
|
pub layout_scheduled: Cell<bool>,
|
||||||
pub render_titles_scheduled: Cell<bool>,
|
pub render_titles_scheduled: Cell<bool>,
|
||||||
pub title: RefCell<String>,
|
pub title: RefCell<String>,
|
||||||
pub title_texture: CloneCell<Option<Rc<Texture>>>,
|
pub title_textures: CopyHashMap<Fixed, Rc<Texture>>,
|
||||||
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +109,7 @@ impl FloatNode {
|
||||||
layout_scheduled: Cell::new(false),
|
layout_scheduled: Cell::new(false),
|
||||||
render_titles_scheduled: Cell::new(false),
|
render_titles_scheduled: Cell::new(false),
|
||||||
title: Default::default(),
|
title: Default::default(),
|
||||||
title_texture: Default::default(),
|
title_textures: Default::default(),
|
||||||
seats: Default::default(),
|
seats: Default::default(),
|
||||||
});
|
});
|
||||||
floater
|
floater
|
||||||
|
|
@ -174,23 +177,38 @@ impl FloatNode {
|
||||||
let bw = theme.sizes.border_width.get();
|
let bw = theme.sizes.border_width.get();
|
||||||
let font = theme.font.borrow_mut();
|
let font = theme.font.borrow_mut();
|
||||||
let title = self.title.borrow_mut();
|
let title = self.title.borrow_mut();
|
||||||
self.title_texture.set(None);
|
self.title_textures.clear();
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
if pos.width() <= 2 * bw || th == 0 || title.is_empty() {
|
if pos.width() <= 2 * bw || title.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ctx = match self.state.render_ctx.get() {
|
let ctx = match self.state.render_ctx.get() {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let texture = match text::render(&ctx, pos.width() - 2 * bw, th, &font, &title, tc) {
|
let scales = self.state.scales.lock();
|
||||||
Ok(t) => t,
|
for (scale, _) in scales.iter() {
|
||||||
Err(e) => {
|
let mut th = th;
|
||||||
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
let mut scalef = None;
|
||||||
return;
|
let mut width = pos.width() - 2 * bw;
|
||||||
|
if *scale != 1 {
|
||||||
|
let scale = scale.to_f64();
|
||||||
|
th = (th as f64 * scale).round() as _;
|
||||||
|
width = (width as f64 * scale).round() as _;
|
||||||
|
scalef = Some(scale);
|
||||||
}
|
}
|
||||||
};
|
if th == 0 || width == 0 {
|
||||||
self.title_texture.set(Some(texture));
|
continue;
|
||||||
|
}
|
||||||
|
let texture = match text::render(&ctx, width, th, &font, &title, tc, scalef) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Could not render title {}: {}", title, ErrorFmt(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.title_textures.set(*scale, texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, x: i32, y: i32) {
|
fn pointer_move(self: &Rc<Self>, seat: &Rc<WlSeatGlobal>, x: i32, y: i32) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use {
|
||||||
},
|
},
|
||||||
wl_surface::{
|
wl_surface::{
|
||||||
ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
|
ext_session_lock_surface_v1::ExtSessionLockSurfaceV1,
|
||||||
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, SurfaceSendPreferredScaleVisitor,
|
||||||
},
|
},
|
||||||
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
||||||
},
|
},
|
||||||
|
|
@ -51,6 +51,7 @@ pub struct OutputNode {
|
||||||
pub scroll: Scroller,
|
pub scroll: Scroller,
|
||||||
pub pointer_positions: CopyHashMap<SeatId, (i32, i32)>,
|
pub pointer_positions: CopyHashMap<SeatId, (i32, i32)>,
|
||||||
pub lock_surface: CloneCell<Option<Rc<ExtSessionLockSurfaceV1>>>,
|
pub lock_surface: CloneCell<Option<Rc<ExtSessionLockSurfaceV1>>>,
|
||||||
|
pub preferred_scale: Cell<Fixed>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputNode {
|
impl OutputNode {
|
||||||
|
|
@ -72,6 +73,24 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_preferred_scale(&self, scale: Fixed) {
|
||||||
|
let old_scale = self.preferred_scale.replace(scale);
|
||||||
|
if scale == old_scale {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let legacy_scale = scale.round_up();
|
||||||
|
if self.global.legacy_scale.replace(legacy_scale) != legacy_scale {
|
||||||
|
self.global.send_mode();
|
||||||
|
}
|
||||||
|
self.state.remove_output_scale(old_scale);
|
||||||
|
self.state.add_output_scale(scale);
|
||||||
|
let rect = self.calculate_extents();
|
||||||
|
self.change_extents_(&rect);
|
||||||
|
let mut visitor = SurfaceSendPreferredScaleVisitor(scale);
|
||||||
|
self.node_visit_children(&mut visitor);
|
||||||
|
self.update_render_data();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_render_data(&self) {
|
pub fn update_render_data(&self) {
|
||||||
let mut rd = self.render_data.borrow_mut();
|
let mut rd = self.render_data.borrow_mut();
|
||||||
rd.titles.clear();
|
rd.titles.clear();
|
||||||
|
|
@ -82,9 +101,19 @@ impl OutputNode {
|
||||||
let font = self.state.theme.font.borrow_mut();
|
let font = self.state.theme.font.borrow_mut();
|
||||||
let theme = &self.state.theme;
|
let theme = &self.state.theme;
|
||||||
let th = theme.sizes.title_height.get();
|
let th = theme.sizes.title_height.get();
|
||||||
|
let scale = self.preferred_scale.get();
|
||||||
|
let scale = if scale != 1 {
|
||||||
|
Some(scale.to_f64())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let mut texture_height = th;
|
||||||
|
if let Some(scale) = scale {
|
||||||
|
texture_height = (th as f64 * scale).round() as _;
|
||||||
|
}
|
||||||
let active_id = self.workspace.get().map(|w| w.id);
|
let active_id = self.workspace.get().map(|w| w.id);
|
||||||
let width = self.global.pos.get().width();
|
let output_width = self.global.pos.get().width();
|
||||||
rd.underline = Rect::new_sized(0, th, width, 1).unwrap();
|
rd.underline = Rect::new_sized(0, th, output_width, 1).unwrap();
|
||||||
for ws in self.workspaces.iter() {
|
for ws in self.workspaces.iter() {
|
||||||
let mut title_width = th;
|
let mut title_width = th;
|
||||||
'create_texture: {
|
'create_texture: {
|
||||||
|
|
@ -96,7 +125,15 @@ impl OutputNode {
|
||||||
true => theme.colors.focused_title_text.get(),
|
true => theme.colors.focused_title_text.get(),
|
||||||
false => theme.colors.unfocused_title_text.get(),
|
false => theme.colors.unfocused_title_text.get(),
|
||||||
};
|
};
|
||||||
let title = match text::render_fitting(&ctx, th, &font, &ws.name, tc, false) {
|
let title = match text::render_fitting(
|
||||||
|
&ctx,
|
||||||
|
texture_height,
|
||||||
|
&font,
|
||||||
|
&ws.name,
|
||||||
|
tc,
|
||||||
|
false,
|
||||||
|
scale,
|
||||||
|
) {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Could not render title {}: {}", ws.name, ErrorFmt(e));
|
log::error!("Could not render title {}: {}", ws.name, ErrorFmt(e));
|
||||||
|
|
@ -104,10 +141,14 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut x = pos + 1;
|
let mut x = pos + 1;
|
||||||
if title.width() + 2 > title_width {
|
let mut width = title.width();
|
||||||
title_width = title.width() + 2;
|
if let Some(scale) = scale {
|
||||||
|
width = (width as f64 / scale).round() as _;
|
||||||
|
}
|
||||||
|
if width + 2 > title_width {
|
||||||
|
title_width = width + 2;
|
||||||
} else {
|
} else {
|
||||||
x = pos + (title_width - title.width()) / 2;
|
x = pos + (title_width - width) / 2;
|
||||||
}
|
}
|
||||||
rd.titles.push(OutputTitle {
|
rd.titles.push(OutputTitle {
|
||||||
x1: pos,
|
x1: pos,
|
||||||
|
|
@ -137,14 +178,19 @@ impl OutputNode {
|
||||||
break 'set_status;
|
break 'set_status;
|
||||||
}
|
}
|
||||||
let tc = self.state.theme.colors.bar_text.get();
|
let tc = self.state.theme.colors.bar_text.get();
|
||||||
let title = match text::render_fitting(&ctx, th, &font, &status, tc, true) {
|
let title =
|
||||||
Ok(t) => t,
|
match text::render_fitting(&ctx, texture_height, &font, &status, tc, true, scale) {
|
||||||
Err(e) => {
|
Ok(t) => t,
|
||||||
log::error!("Could not render status {}: {}", status, ErrorFmt(e));
|
Err(e) => {
|
||||||
break 'set_status;
|
log::error!("Could not render status {}: {}", status, ErrorFmt(e));
|
||||||
}
|
break 'set_status;
|
||||||
};
|
}
|
||||||
let pos = width - title.width() - 1;
|
};
|
||||||
|
let mut width = title.width();
|
||||||
|
if let Some(scale) = scale {
|
||||||
|
width = (width as f64 / scale).round() as _;
|
||||||
|
}
|
||||||
|
let pos = output_width - width - 1;
|
||||||
rd.status = Some(OutputStatus {
|
rd.status = Some(OutputStatus {
|
||||||
tex_x: pos,
|
tex_x: pos,
|
||||||
tex_y: 0,
|
tex_y: 0,
|
||||||
|
|
@ -266,11 +312,24 @@ impl OutputNode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.global.mode.set(mode);
|
self.global.mode.set(mode);
|
||||||
let pos = self.global.pos.get();
|
let rect = self.calculate_extents();
|
||||||
let rect = Rect::new_sized(pos.x1(), pos.y1(), mode.width, mode.height).unwrap();
|
|
||||||
self.change_extents_(&rect);
|
self.change_extents_(&rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_extents(&self) -> Rect {
|
||||||
|
let mode = self.global.mode.get();
|
||||||
|
let mut width = mode.width;
|
||||||
|
let mut height = mode.height;
|
||||||
|
let scale = self.preferred_scale.get();
|
||||||
|
if scale != 1 {
|
||||||
|
let scale = scale.to_f64();
|
||||||
|
width = (width as f64 / scale).round() as _;
|
||||||
|
height = (height as f64 / scale).round() as _;
|
||||||
|
}
|
||||||
|
let pos = self.global.pos.get();
|
||||||
|
pos.with_size(width, height).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_extents_(&self, rect: &Rect) {
|
fn change_extents_(&self, rect: &Rect) {
|
||||||
self.global.pos.set(*rect);
|
self.global.pos.set(*rect);
|
||||||
self.state.root.update_extents();
|
self.state.root.update_extents();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use {
|
||||||
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelData,
|
Direction, FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, ToplevelData,
|
||||||
ToplevelNode,
|
ToplevelNode,
|
||||||
},
|
},
|
||||||
utils::{clonecell::CloneCell, errorfmt::ErrorFmt},
|
utils::{errorfmt::ErrorFmt, smallmap::SmallMap},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, ops::Deref, rc::Rc},
|
std::{cell::Cell, ops::Deref, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
@ -23,7 +23,7 @@ pub struct PlaceholderNode {
|
||||||
id: PlaceholderNodeId,
|
id: PlaceholderNodeId,
|
||||||
toplevel: ToplevelData,
|
toplevel: ToplevelData,
|
||||||
destroyed: Cell<bool>,
|
destroyed: Cell<bool>,
|
||||||
pub texture: CloneCell<Option<Rc<Texture>>>,
|
pub textures: SmallMap<Fixed, Rc<Texture>, 2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlaceholderNode {
|
impl PlaceholderNode {
|
||||||
|
|
@ -36,7 +36,7 @@ impl PlaceholderNode {
|
||||||
node.node_client(),
|
node.node_client(),
|
||||||
),
|
),
|
||||||
destroyed: Default::default(),
|
destroyed: Default::default(),
|
||||||
texture: Default::default(),
|
textures: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,24 +45,35 @@ impl PlaceholderNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_texture(&self) {
|
pub fn update_texture(&self) {
|
||||||
self.texture.set(None);
|
self.textures.clear();
|
||||||
if let Some(ctx) = self.toplevel.state.render_ctx.get() {
|
if let Some(ctx) = self.toplevel.state.render_ctx.get() {
|
||||||
|
let scales = self.toplevel.state.scales.lock();
|
||||||
let rect = self.toplevel.pos.get();
|
let rect = self.toplevel.pos.get();
|
||||||
if rect.width() != 0 && rect.height() != 0 {
|
for (scale, _) in scales.iter() {
|
||||||
let font = format!("monospace {}", rect.width() / 10);
|
let mut width = rect.width();
|
||||||
match text::render_fitting(
|
let mut height = rect.height();
|
||||||
&ctx,
|
if *scale != 1 {
|
||||||
rect.height(),
|
let scale = scale.to_f64();
|
||||||
&font,
|
width = (width as f64 * scale).round() as _;
|
||||||
"Fullscreen",
|
height = (height as f64 * scale).round() as _;
|
||||||
self.toplevel.state.theme.colors.unfocused_title_text.get(),
|
}
|
||||||
false,
|
if width != 0 && height != 0 {
|
||||||
) {
|
let font = format!("monospace {}", width / 10);
|
||||||
Ok(t) => {
|
match text::render_fitting(
|
||||||
self.texture.set(Some(t));
|
&ctx,
|
||||||
}
|
height,
|
||||||
Err(e) => {
|
&font,
|
||||||
log::warn!("Could not render fullscreen texture: {}", ErrorFmt(e));
|
"Fullscreen",
|
||||||
|
self.toplevel.state.theme.colors.unfocused_title_text.get(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(t) => {
|
||||||
|
self.textures.insert(*scale, t);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not render fullscreen texture: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,11 @@ pub trait ToplevelNode: Node {
|
||||||
fn tl_set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
fn tl_set_workspace(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
||||||
let data = self.tl_data();
|
let data = self.tl_data();
|
||||||
data.workspace.set(Some(ws.clone()));
|
data.workspace.set(Some(ws.clone()));
|
||||||
|
self.tl_set_workspace_ext(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tl_set_workspace_ext(self: Rc<Self>, ws: &Rc<WorkspaceNode>) {
|
||||||
|
let _ = ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use {
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_output::OutputId,
|
wl_output::OutputId,
|
||||||
wl_seat::{NodeSeatState, WlSeatGlobal},
|
wl_seat::{NodeSeatState, WlSeatGlobal},
|
||||||
|
wl_surface::SurfaceSendPreferredScaleVisitor,
|
||||||
},
|
},
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
render::Renderer,
|
render::Renderer,
|
||||||
|
|
@ -16,7 +17,7 @@ use {
|
||||||
linkedlist::{LinkedList, LinkedNode},
|
linkedlist::{LinkedList, LinkedNode},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, fmt::Debug, rc::Rc},
|
std::{cell::Cell, fmt::Debug, ops::Deref, rc::Rc},
|
||||||
};
|
};
|
||||||
|
|
||||||
tree_id!(WorkspaceNodeId);
|
tree_id!(WorkspaceNodeId);
|
||||||
|
|
@ -44,6 +45,17 @@ impl WorkspaceNode {
|
||||||
self.fullscreen.set(None);
|
self.fullscreen.set(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
||||||
|
let old = self.output.set(output.clone());
|
||||||
|
if old.preferred_scale.get() != output.preferred_scale.get() {
|
||||||
|
let mut visitor = SurfaceSendPreferredScaleVisitor(output.preferred_scale.get());
|
||||||
|
self.node_visit_children(&mut visitor);
|
||||||
|
for stacked in self.stacked.iter() {
|
||||||
|
stacked.deref().clone().node_visit(&mut visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_container(self: &Rc<Self>, container: &Rc<ContainerNode>) {
|
pub fn set_container(self: &Rc<Self>, container: &Rc<ContainerNode>) {
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
container.clone().tl_change_extents(&pos);
|
container.clone().tl_change_extents(&pos);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ pub mod oserror;
|
||||||
pub mod ptr_ext;
|
pub mod ptr_ext;
|
||||||
pub mod queue;
|
pub mod queue;
|
||||||
pub mod rc_eq;
|
pub mod rc_eq;
|
||||||
|
pub mod refcounted;
|
||||||
pub mod run_toplevel;
|
pub mod run_toplevel;
|
||||||
pub mod scroller;
|
pub mod scroller;
|
||||||
pub mod smallmap;
|
pub mod smallmap;
|
||||||
|
|
|
||||||
90
src/utils/refcounted.rs
Normal file
90
src/utils/refcounted.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
use {
|
||||||
|
crate::utils::ptr_ext::{MutPtrExt, PtrExt},
|
||||||
|
std::{cell::UnsafeCell, mem, ops::Deref},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RefCounted<T> {
|
||||||
|
map: UnsafeCell<Vec<(T, usize)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for RefCounted<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
map: UnsafeCell::new(vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq> RefCounted<T> {
|
||||||
|
pub fn add(&self, t: T) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let map = self.map.get().deref_mut();
|
||||||
|
for (k, v) in &mut *map {
|
||||||
|
if k == &t {
|
||||||
|
*v += 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.push((t, 1));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&self, t: &T) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let map = self.map.get().deref_mut();
|
||||||
|
let idx = 'idx: {
|
||||||
|
for (idx, (k, v)) in map.iter_mut().enumerate() {
|
||||||
|
if k == t {
|
||||||
|
*v -= 1;
|
||||||
|
if *v > 0 {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
break 'idx idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let _v = map.swap_remove(idx);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_vec(&self) -> Vec<T>
|
||||||
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
|
unsafe { self.map.get().deref().iter().map(|k| k.0).collect() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock(&self) -> Locked<T> {
|
||||||
|
unsafe {
|
||||||
|
Locked {
|
||||||
|
vec: mem::take(self.map.get().deref_mut()),
|
||||||
|
rc: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Locked<'a, T> {
|
||||||
|
rc: &'a RefCounted<T>,
|
||||||
|
vec: Vec<(T, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Deref for Locked<'a, T> {
|
||||||
|
type Target = [(T, usize)];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.vec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Drop for Locked<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
*self.rc.map.get() = mem::take(&mut self.vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,16 +12,12 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SmallMap<K, V, const N: usize> {
|
pub struct SmallMap<K, V, const N: usize> {
|
||||||
m: UnsafeCell<SmallVec<[(K, V); N]>>,
|
m: UnsafeCell<SmallMapMut<K, V, N>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Debug, V: Debug, const N: usize> Debug for SmallMap<K, V, N> {
|
impl<K: Debug, V: Debug, const N: usize> Debug for SmallMap<K, V, N> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
unsafe {
|
unsafe { self.m.get().deref().fmt(f) }
|
||||||
f.debug_map()
|
|
||||||
.entries(self.m.get().deref().iter().map(|e| (&e.0, &e.1)))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,16 +31,14 @@ impl<K, V, const N: usize> Default for SmallMap<K, V, N> {
|
||||||
|
|
||||||
impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
|
impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
|
||||||
pub fn new_with(k: K, v: V) -> Self {
|
pub fn new_with(k: K, v: V) -> Self {
|
||||||
let mut sv = SmallVec::new();
|
|
||||||
sv.push((k, v));
|
|
||||||
Self {
|
Self {
|
||||||
m: UnsafeCell::new(sv),
|
m: UnsafeCell::new(SmallMapMut::new_with(k, v)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
m: UnsafeCell::new(SmallVec::new_const()),
|
m: UnsafeCell::new(SmallMapMut::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,42 +47,25 @@ impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&self, k: K, v: V) -> Option<V> {
|
pub fn insert(&self, k: K, v: V) -> Option<V> {
|
||||||
unsafe {
|
unsafe { self.m.get().deref_mut().insert(k, v) }
|
||||||
let m = self.m.get().deref_mut();
|
|
||||||
for (ek, ev) in &mut *m {
|
|
||||||
if ek == &k {
|
|
||||||
return Some(mem::replace(ev, v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.push((k, v));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
unsafe { self.m.get().deref_mut().is_empty() }
|
unsafe { self.m.get().deref().is_empty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&self, k: &K) -> Option<V> {
|
pub fn remove(&self, k: &K) -> Option<V> {
|
||||||
unsafe {
|
unsafe { self.m.get().deref_mut().remove(k) }
|
||||||
let m = self.m.get().deref_mut();
|
|
||||||
for (idx, (ek, _)) in m.iter_mut().enumerate() {
|
|
||||||
if ek == k {
|
|
||||||
return Some(m.swap_remove(idx).1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _v = mem::replace(self.m.get().deref_mut(), SmallVec::new());
|
let _v = self.m.get().deref_mut().clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take(&self) -> SmallVec<[(K, V); N]> {
|
pub fn take(&self) -> SmallVec<[(K, V); N]> {
|
||||||
unsafe { mem::take(self.m.get().deref_mut()) }
|
unsafe { self.m.get().deref_mut().take() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&self) -> Option<(K, V)> {
|
pub fn pop(&self) -> Option<(K, V)> {
|
||||||
|
|
@ -103,7 +80,7 @@ impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
|
||||||
impl<K: Eq, V: UnsafeCellCloneSafe, const N: usize> SmallMap<K, V, N> {
|
impl<K: Eq, V: UnsafeCellCloneSafe, const N: usize> SmallMap<K, V, N> {
|
||||||
pub fn get(&self, k: &K) -> Option<V> {
|
pub fn get(&self, k: &K) -> Option<V> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let m = self.m.get().deref();
|
let m = &self.m.get().deref().m;
|
||||||
for (ek, ev) in m {
|
for (ek, ev) in m {
|
||||||
if ek == k {
|
if ek == k {
|
||||||
return Some(ev.clone());
|
return Some(ev.clone());
|
||||||
|
|
@ -133,7 +110,7 @@ impl<'a, K: Copy, V: UnsafeCellCloneSafe, const N: usize> Iterator for SmallMapI
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let v = self.map.m.get().deref();
|
let v = &self.map.m.get().deref().m;
|
||||||
if self.pos >= v.len() {
|
if self.pos >= v.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -143,3 +120,163 @@ impl<'a, K: Copy, V: UnsafeCellCloneSafe, const N: usize> Iterator for SmallMapI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SmallMapMut<K, V, const N: usize> {
|
||||||
|
m: SmallVec<[(K, V); N]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Debug, V: Debug, const N: usize> Debug for SmallMapMut<K, V, N> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_map()
|
||||||
|
.entries(self.m.iter().map(|e| (&e.0, &e.1)))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V, const N: usize> Default for SmallMapMut<K, V, N> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
m: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Eq, V, const N: usize> SmallMapMut<K, V, N> {
|
||||||
|
pub fn new_with(k: K, v: V) -> Self {
|
||||||
|
let mut sv = SmallVec::new();
|
||||||
|
sv.push((k, v));
|
||||||
|
Self { m: sv }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
m: SmallVec::new_const(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.m.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||||
|
for (ek, ev) in &mut self.m {
|
||||||
|
if ek == &k {
|
||||||
|
return Some(mem::replace(ev, v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.m.push((k, v));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, k: &K) -> Option<&V> {
|
||||||
|
for (ek, ev) in &self.m {
|
||||||
|
if ek == k {
|
||||||
|
return Some(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_default_mut(&mut self, k: K) -> &mut V
|
||||||
|
where
|
||||||
|
V: Default,
|
||||||
|
{
|
||||||
|
for (ek, ev) in &mut self.m {
|
||||||
|
if ek == &k {
|
||||||
|
return unsafe { (ev as *mut V).deref_mut() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.m.push((k, V::default()));
|
||||||
|
&mut self.m.last_mut().unwrap().1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.m.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, k: &K) -> Option<V> {
|
||||||
|
for (idx, (ek, _)) in self.m.iter_mut().enumerate() {
|
||||||
|
if ek == k {
|
||||||
|
return Some(self.m.swap_remove(idx).1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
let _v = mem::replace(&mut self.m, SmallVec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(&mut self) -> SmallVec<[(K, V); N]> {
|
||||||
|
mem::take(&mut self.m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Option<(K, V)> {
|
||||||
|
self.m.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> SmallMapMutIter<'a, K, V, N> {
|
||||||
|
SmallMapMutIter { pos: 0, map: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_mut<'a>(&'a mut self) -> SmallMapMutIterMut<'a, K, V, N> {
|
||||||
|
SmallMapMutIterMut { pos: 0, map: self }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_if<F: FnMut(&K, &V) -> bool>(&mut self, mut f: F) {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < self.m.len() {
|
||||||
|
let (k, v) = &self.m[i];
|
||||||
|
if f(k, v) {
|
||||||
|
self.m.swap_remove(i);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K: Copy, V, const N: usize> IntoIterator for &'a SmallMapMut<K, V, N> {
|
||||||
|
type Item = (&'a K, &'a V);
|
||||||
|
type IntoIter = SmallMapMutIter<'a, K, V, N>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
SmallMapMutIter { pos: 0, map: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SmallMapMutIter<'a, K, V, const N: usize> {
|
||||||
|
pos: usize,
|
||||||
|
map: &'a SmallMapMut<K, V, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K, V, const N: usize> Iterator for SmallMapMutIter<'a, K, V, N> {
|
||||||
|
type Item = (&'a K, &'a V);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.pos >= self.map.m.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (k, v) = &self.map.m[self.pos];
|
||||||
|
self.pos += 1;
|
||||||
|
Some((k, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SmallMapMutIterMut<'a, K, V, const N: usize> {
|
||||||
|
pos: usize,
|
||||||
|
map: &'a mut SmallMapMut<K, V, N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K, V, const N: usize> Iterator for SmallMapMutIterMut<'a, K, V, N> {
|
||||||
|
type Item = (&'a mut K, &'a mut V);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.pos >= self.map.m.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (k, v) = &mut self.map.m[self.pos];
|
||||||
|
self.pos += 1;
|
||||||
|
unsafe { Some(((k as *mut K).deref_mut(), (v as *mut V).deref_mut())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -424,6 +424,10 @@ impl Wm {
|
||||||
Some(f) => f,
|
Some(f) => f,
|
||||||
_ => break 'set_root_cursor,
|
_ => break 'set_root_cursor,
|
||||||
};
|
};
|
||||||
|
let first = match first.iter().find(|i| i.0 .0 == 1) {
|
||||||
|
Some(f) => f.1,
|
||||||
|
_ => break 'set_root_cursor,
|
||||||
|
};
|
||||||
let cursor = match c
|
let cursor = match c
|
||||||
.create_cursor(
|
.create_cursor(
|
||||||
&first.pixels,
|
&first.pixels,
|
||||||
|
|
|
||||||
10
wire/wp_fractional_scale_manager_v1.txt
Normal file
10
wire/wp_fractional_scale_manager_v1.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# requests
|
||||||
|
|
||||||
|
msg destroy = 0 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
msg get_fractional_scale = 1 {
|
||||||
|
id: id(wp_fractional_scale_v1),
|
||||||
|
surface: id(wl_surface),
|
||||||
|
}
|
||||||
11
wire/wp_fractional_scale_v1.txt
Normal file
11
wire/wp_fractional_scale_v1.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# requests
|
||||||
|
|
||||||
|
msg destroy = 0 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# events
|
||||||
|
|
||||||
|
msg preferred_scale = 0 {
|
||||||
|
scale: fixed,
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue