1
0
Fork 0
forked from wry/wry

autocommit 2022-04-01 01:44:10 CEST

This commit is contained in:
Julian Orth 2022-04-01 01:44:10 +02:00
parent ab4ac883ee
commit 2dd433aa04
32 changed files with 626 additions and 139 deletions

View file

@ -28,6 +28,7 @@ pub trait InputDevice {
fn set_accel_profile(&self, profile: InputDeviceAccelProfile);
fn set_accel_speed(&self, speed: f64);
fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]);
fn name(&self) -> Rc<String>;
}
#[derive(Debug, Copy, Clone)]

View file

@ -1,2 +1,3 @@
pub mod dummy;
pub mod metal;
pub mod x;

36
src/backends/dummy.rs Normal file
View file

@ -0,0 +1,36 @@
use crate::backend::{Backend, Output, OutputId};
use std::rc::Rc;
pub struct DummyBackend {}
impl Backend for DummyBackend {
fn switch_to(&self, vtnr: u32) {
let _ = vtnr;
}
}
pub struct DummyOutput {
pub id: OutputId,
}
impl Output for DummyOutput {
fn id(&self) -> OutputId {
self.id
}
fn removed(&self) -> bool {
false
}
fn width(&self) -> i32 {
100
}
fn height(&self) -> i32 {
100
}
fn on_change(&self, _cb: Rc<dyn Fn()>) {
// nothing
}
}

View file

@ -196,6 +196,7 @@ struct MetalInputDevice {
cb: CloneCell<Option<Rc<dyn Fn()>>>,
hscroll: Cell<f64>,
vscroll: Cell<f64>,
name: CloneCell<Rc<String>>,
// config
left_handed: Cell<Option<bool>>,
@ -223,16 +224,13 @@ impl LibInputAdapter for DeviceHolder {
Ok(s) => s,
Err(e) => return Err(LibInputError::Stat(e.into())),
};
match self.devices.get(&stat.st_rdev) {
Some(MetalDevice::Input(d)) => match d.fd.get() {
Some(fd) => match uapi::fcntl_dupfd_cloexec(fd.raw(), 0) {
Ok(fd) => Ok(fd),
Err(e) => Err(LibInputError::DupFd(e.into())),
},
_ => Err(LibInputError::DeviceUnavailable),
},
_ => Err(LibInputError::DeviceUnavailable),
if let Some(MetalDevice::Input(d)) = self.devices.get(&stat.st_rdev) {
if let Some(fd) = d.fd.get() {
return uapi::fcntl_dupfd_cloexec(fd.raw(), 0)
.map_err(|e| LibInputError::DupFd(e.into()));
}
}
Err(LibInputError::DeviceUnavailable)
}
}
@ -319,6 +317,10 @@ impl InputDevice for MetalInputDevice {
fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]) {
self.transform_matrix.set(Some(matrix));
}
fn name(&self) -> Rc<String> {
self.name.get()
}
}
impl MetalInputDevice {

View file

@ -281,6 +281,7 @@ impl MetalBackend {
cb: Default::default(),
hscroll: Cell::new(0.0),
vscroll: Cell::new(0.0),
name: Default::default(),
left_handed: Default::default(),
accel_profile: Default::default(),
accel_speed: Default::default(),
@ -323,6 +324,7 @@ impl MetalBackend {
Err(_) => return,
};
inputdev.device().set_slot(slot);
dev.name.set(Rc::new(inputdev.device().name()));
dev.inputdev.set(Some(inputdev));
dev.apply_config();
slf.state

View file

@ -532,6 +532,8 @@ impl XBackendData {
kb_events: RefCell::new(Default::default()),
mouse_events: RefCell::new(Default::default()),
button_map: Default::default(),
kb_name: Rc::new(format!("kb{}", info.deviceid)),
mouse_name: Rc::new(format!("mouse{}", info.deviceid)),
});
seat.update_button_map().await;
self.seats.set(info.deviceid, seat.clone());
@ -888,6 +890,8 @@ struct XSeat {
kb_events: RefCell<VecDeque<InputEvent>>,
mouse_events: RefCell<VecDeque<InputEvent>>,
button_map: CopyHashMap<u32, u32>,
kb_name: Rc<String>,
mouse_name: Rc<String>,
}
struct XSeatKeyboard(Rc<XSeat>);
@ -982,6 +986,10 @@ impl InputDevice for XSeatKeyboard {
fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]) {
let _ = matrix;
}
fn name(&self) -> Rc<String> {
self.0.kb_name.clone()
}
}
impl InputDevice for XSeatMouse {
@ -1027,4 +1035,8 @@ impl InputDevice for XSeatMouse {
fn set_transform_matrix(&self, matrix: [[f64; 2]; 2]) {
let _ = matrix;
}
fn name(&self) -> Rc<String> {
self.0.mouse_name.clone()
}
}

View file

@ -13,9 +13,7 @@ use crate::logger::Logger;
use crate::render::RenderError;
use crate::sighand::SighandError;
use crate::state::State;
use crate::tree::{
container_layout, container_render_data, float_layout, float_titles, DisplayNode, NodeIds,
};
use crate::tree::{container_layout, container_render_data, float_layout, float_titles, DisplayNode, NodeIds, OutputNode, WorkspaceNode};
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::fdcloser::FdCloser;
use crate::utils::numcell::NumCell;
@ -25,11 +23,14 @@ use crate::wheel::{Wheel, WheelError};
use crate::xkbcommon::XkbContext;
use crate::{clientmem, forker, leaks, render, sighand, tasks, xwayland};
use forker::ForkerProxy;
use std::cell::Cell;
use std::cell::{Cell};
use std::ops::Deref;
use std::rc::Rc;
use std::sync::Arc;
use thiserror::Error;
use crate::backends::dummy::DummyOutput;
use crate::ifs::wl_output::WlOutputGlobal;
use crate::utils::clonecell::CloneCell;
pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
let logger = Logger::install_compositor(global.log_level.into());
@ -90,6 +91,8 @@ fn main_(logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> {
globals: Globals::new(),
output_ids: Default::default(),
root: Rc::new(DisplayNode::new(node_ids.next())),
workspaces: Default::default(),
dummy_output: Default::default(),
node_ids,
backend_events: AsyncQueue::new(),
output_handlers: Default::default(),
@ -111,6 +114,33 @@ fn main_(logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> {
fdcloser: FdCloser::new(),
logger,
});
{
let dummy_output = Rc::new(OutputNode {
id: state.node_ids.next(),
position: Default::default(),
global: Rc::new(WlOutputGlobal::new(state.globals.name(), Rc::new(DummyOutput {
id: state.output_ids.next(),
}))),
workspaces: Default::default(),
workspace: Default::default(),
seat_state: Default::default(),
layers: Default::default(),
render_data: Default::default(),
state: state.clone(),
is_dummy: true,
});
let dummy_workspace = Rc::new(WorkspaceNode {
id: state.node_ids.next(),
output: CloneCell::new(dummy_output.clone()),
container: Default::default(),
stacked: Default::default(),
seat_state: Default::default(),
name: "dummy".to_string()
});
dummy_output.workspace.set(Some(dummy_workspace.clone()));
dummy_output.workspaces.borrow_mut().push(dummy_workspace);
state.dummy_output.set(Some(dummy_output));
}
forker.install(&state);
let config = ConfigProxy::default(&state);
state.config.set(Some(Rc::new(config)));

View file

@ -87,6 +87,9 @@ impl ConfigProxy {
next_id: NumCell::new(1),
keymaps: Default::default(),
bufs: Default::default(),
workspace_ids: NumCell::new(1),
workspaces_by_name: Default::default(),
workspaces_by_id: Default::default(),
});
let init_msg =
bincode::encode_to_vec(&InitMessage::V1(V1InitMessage {}), bincode_ops()).unwrap();

View file

@ -3,7 +3,7 @@ use crate::backend::{InputDeviceAccelProfile, InputDeviceCapability, InputDevice
use crate::ifs::wl_seat::{SeatId, WlSeatGlobal};
use crate::state::{DeviceHandlerData, State};
use crate::tree::walker::NodeVisitorBase;
use crate::tree::{ContainerNode, ContainerSplit, FloatNode, Node};
use crate::tree::{ContainerNode, ContainerSplit, FloatNode, Node, WorkspaceNode};
use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::debug_fn::debug_fn;
use crate::utils::errorfmt::ErrorFmt;
@ -20,12 +20,13 @@ use jay_config::input::{
use jay_config::keyboard::keymap::Keymap;
use jay_config::keyboard::mods::Modifiers;
use jay_config::keyboard::syms::KeySym;
use jay_config::{Axis, Direction, LogLevel, Seat};
use jay_config::{Axis, Direction, LogLevel, Seat, Workspace};
use libloading::Library;
use log::Level;
use std::cell::Cell;
use std::rc::Rc;
use thiserror::Error;
use crate::utils::clonecell::CloneCell;
pub(super) struct ConfigProxyHandler {
pub client_data: Cell<*const u8>,
@ -38,6 +39,9 @@ pub(super) struct ConfigProxyHandler {
pub next_id: NumCell<u64>,
pub keymaps: CopyHashMap<Keymap, Rc<XkbKeymap>>,
pub bufs: Stack<Vec<u8>>,
pub workspace_ids: NumCell<u64>,
pub workspaces_by_name: CopyHashMap<Rc<String>, u64>,
pub workspaces_by_id: CopyHashMap<u64, Rc<String>>,
}
impl ConfigProxyHandler {
@ -156,6 +160,13 @@ impl ConfigProxyHandler {
Ok(())
}
fn get_workspace(&self, ws: Workspace) -> Result<Rc<String>, CphError> {
match self.workspaces_by_id.get(&ws.0) {
Some(ws) => Ok(ws),
_ => Err(CphError::WorkspaceDoesNotExist(ws))
}
}
fn get_device_handler_data(
&self,
device: InputDevice,
@ -253,6 +264,64 @@ impl ConfigProxyHandler {
Ok(())
}
fn handle_get_workspace(&self, name: &str) {
let name = Rc::new(name.to_owned());
let ws = match self.workspaces_by_name.get(&name) {
Some(w) => w,
_ => {
let ws = self.workspace_ids.fetch_add(1);
self.workspaces_by_name.set(name.clone(), ws);
self.workspaces_by_id.set(ws, name);
ws
}
};
self.respond(Response::GetWorkspace {
workspace: Workspace(ws),
});
}
fn handle_show_workspace(&self, seat: Seat, ws: Workspace) -> Result<(), ShowWorkspaceError> {
let seat = self.get_seat(seat)?;
let name = self.get_workspace(ws)?;
let output = match self.state.workspaces.get(name.as_str()) {
Some(ws) => {
let output = ws.output.get();
output.workspace.set(Some(ws.clone()));
output
}
None => {
let output = seat.get_output();
if output.is_dummy {
return Ok(());
}
let ws = Rc::new(WorkspaceNode {
id: self.state.node_ids.next(),
output: CloneCell::new(output.clone()),
container: Default::default(),
stacked: Default::default(),
seat_state: Default::default(),
name: name.to_string(),
});
output.workspaces.borrow_mut().push(ws.clone());
output.workspace.set(Some(ws.clone()));
self.state.workspaces.set(name.to_string(), ws);
output
}
};
output.update_render_data();
self.state.tree_changed();
Ok(())
}
fn handle_get_device_name(&self, device: InputDevice) -> Result<(), GetDeviceNameError> {
let dev = self.get_device_handler_data(device)?;
let name = dev.device.name();
self.respond(Response::GetDeviceName {
name: name.to_string(),
});
Ok(())
}
fn handle_has_capability(
&self,
device: InputDevice,
@ -582,6 +651,9 @@ impl ConfigProxyHandler {
ClientMessage::SetTransformMatrix { device, matrix } => {
self.handle_set_transform_matrix(device, matrix)?
}
ClientMessage::GetDeviceName { device } => self.handle_get_device_name(device)?,
ClientMessage::GetWorkspace { name } => self.handle_get_workspace(name),
ClientMessage::ShowWorkspace { seat, workspace } => self.handle_show_workspace(seat, workspace)?,
}
Ok(())
}
@ -639,12 +711,18 @@ enum CphError {
SetAccelSpeedError(#[from] SetAccelSpeedError),
#[error("Could not process a `set_transform_matrix` request")]
SetTransformMatrixError(#[from] SetTransformMatrixError),
#[error("Could not process a `get_device_name` request")]
GetDeviceNameError(#[from] GetDeviceNameError),
#[error("Could not process a `show_workspace` request")]
ShowWorkspaceError(#[from] ShowWorkspaceError),
#[error("Device {0:?} does not exist")]
DeviceDoesNotExist(InputDevice),
#[error("Device {0:?} does not exist")]
KeymapDoesNotExist(Keymap),
#[error("Seat {0:?} does not exist")]
SeatDoesNotExist(Seat),
#[error("Workspace {0:?} does not exist")]
WorkspaceDoesNotExist(Workspace),
#[error("Keyboard {0:?} does not exist")]
KeyboardDoesNotExist(InputDevice),
#[error("Could not parse the message")]
@ -694,6 +772,20 @@ enum SetTransformMatrixError {
}
efrom!(SetTransformMatrixError, CphError);
#[derive(Debug, Error)]
enum GetDeviceNameError {
#[error(transparent)]
CphError(#[from] Box<CphError>),
}
efrom!(GetDeviceNameError, CphError);
#[derive(Debug, Error)]
enum ShowWorkspaceError {
#[error(transparent)]
CphError(#[from] Box<CphError>),
}
efrom!(ShowWorkspaceError, CphError);
#[derive(Debug, Error)]
enum HasCapabilityError {
#[error(transparent)]

View file

@ -59,7 +59,7 @@ pub struct WlOutputGlobal {
}
impl WlOutputGlobal {
pub fn new(name: GlobalName, output: &Rc<dyn Output>) -> Self {
pub fn new(name: GlobalName, output: Rc<dyn Output>) -> Self {
Self {
name,
output: output.clone(),

View file

@ -16,7 +16,6 @@ use crate::ifs::ipc::wl_data_source::WlDataSource;
use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1;
use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1;
use crate::ifs::ipc::IpcError;
use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_seat::kb_owner::KbOwnerHolder;
use crate::ifs::wl_seat::pointer_owner::PointerOwnerHolder;
use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE};
@ -27,7 +26,7 @@ use crate::leaks::Tracker;
use crate::object::{Object, ObjectId};
use crate::state::State;
use crate::tree::toplevel::ToplevelNode;
use crate::tree::{ContainerSplit, FloatNode, FoundNode, Node};
use crate::tree::{ContainerSplit, FloatNode, FoundNode, Node, OutputNode};
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError;
@ -49,7 +48,7 @@ use jay_config::Direction;
use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::ops::{DerefMut};
use std::rc::Rc;
use thiserror::Error;
use uapi::{c, Errno, OwnedFd};
@ -123,6 +122,7 @@ pub struct WlSeatGlobal {
shortcuts: CopyHashMap<(u32, u32), Modifiers>,
queue_link: Cell<Option<LinkedNode<Rc<Self>>>>,
tree_changed_handler: Cell<Option<SpawnedFuture<()>>>,
output: CloneCell<Rc<OutputNode>>,
}
impl WlSeatGlobal {
@ -158,6 +158,7 @@ impl WlSeatGlobal {
shortcuts: Default::default(),
queue_link: Cell::new(None),
tree_changed_handler: Cell::new(None),
output: CloneCell::new(state.dummy_output.get().unwrap()),
});
let seat = slf.clone();
let future = state.eng.spawn(async move {
@ -171,15 +172,8 @@ impl WlSeatGlobal {
slf
}
pub fn get_output(&self) -> Option<Rc<WlOutputGlobal>> {
let ps = self.pointer_stack.borrow_mut();
for node in ps.deref() {
if node.is_output() {
let on = node.clone().into_output().unwrap();
return Some(on.global.clone());
}
}
None
pub fn get_output(&self) -> Rc<OutputNode> {
self.output.get()
}
pub fn mark_last_active(self: &Rc<Self>) {

View file

@ -11,7 +11,7 @@ use crate::ifs::wl_surface::xdg_surface::xdg_popup::XdgPopup;
use crate::ifs::wl_surface::WlSurface;
use crate::object::ObjectId;
use crate::tree::toplevel::ToplevelNode;
use crate::tree::{FloatNode, Node};
use crate::tree::{FloatNode, Node, OutputNode};
use crate::utils::clonecell::CloneCell;
use crate::utils::smallmap::SmallMap;
use crate::wire::WlDataOfferId;
@ -413,6 +413,10 @@ impl WlSeatGlobal {
// self.focus_xdg_surface(&n.xdg);
}
pub fn enter_output(self: &Rc<Self>, output: &Rc<OutputNode>) {
self.output.set(output.clone());
}
pub fn enter_surface(&self, n: &WlSurface, x: Fixed, y: Fixed) {
let serial = self.serial.fetch_add(1);
self.surface_pointer_event(0, n, |p| p.send_enter(serial, n.id, x, y));
@ -427,6 +431,10 @@ impl WlSeatGlobal {
self.surface_pointer_event(0, n, |p| p.send_leave(serial, n.id));
self.surface_pointer_frame(n);
}
pub fn leave_output(&self) {
self.output.set(self.state.dummy_output.get().unwrap());
}
}
// Unfocus callbacks

View file

@ -70,8 +70,9 @@ impl ZwlrLayerShellV1 {
self.client.lookup(req.output)?.global.node.get().unwrap()
} else {
for seat in self.client.state.seat_queue.rev_iter() {
if let Some(output) = seat.get_output() {
break 'get_output output.node.get().unwrap();
let output = seat.get_output();
if !output.is_dummy {
break 'get_output output;
}
}
let outputs = self.client.state.outputs.lock();

View file

@ -106,6 +106,18 @@ xkb_keymap {
<125> = 133; # LEFTMETA
<126> = 134; # RIGHTMETA
<139> = 147; # MENU
<183> = 191; # F13
<184> = 192; # F14
<185> = 193; # F15
<186> = 194; # F16
<187> = 195; # F17
<188> = 196; # F18
<189> = 197; # F19
<190> = 198; # F20
<191> = 199; # F21
<192> = 200; # F22
<193> = 201; # F23
<194> = 202; # F24
<210> = 218; # PRINT
# We must include at least one indicator here. Otherwise Xwayland segfaults.
@ -188,6 +200,18 @@ xkb_keymap {
key <68> { [ F10 ] };
key <87> { [ F11 ] };
key <88> { [ F12 ] };
key <183> { [ F13 ] };
key <184> { [ F14 ] };
key <185> { [ F15 ] };
key <186> { [ F16 ] };
key <187> { [ F17 ] };
key <188> { [ F18 ] };
key <189> { [ F19 ] };
key <190> { [ F20 ] };
key <191> { [ F21 ] };
key <192> { [ F22 ] };
key <193> { [ F23 ] };
key <194> { [ F24 ] };
key <210> { [ Print ] };
key <70> { [ Scroll_Lock ] };
key <119> { [ Pause ] };

View file

@ -2,10 +2,12 @@ use crate::libinput::consts::{AccelProfile, DeviceCapability};
use crate::libinput::sys::{
libinput_device, libinput_device_config_accel_set_profile,
libinput_device_config_accel_set_speed, libinput_device_config_left_handed_set,
libinput_device_get_user_data, libinput_device_has_capability, libinput_device_set_user_data,
libinput_device_unref, libinput_path_remove_device,
libinput_device_get_name, libinput_device_get_user_data, libinput_device_has_capability,
libinput_device_set_user_data, libinput_device_unref, libinput_path_remove_device,
};
use crate::libinput::LibInput;
use bstr::ByteSlice;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::rc::Rc;
@ -65,6 +67,13 @@ impl<'a> LibInputDevice<'a> {
libinput_device_config_accel_set_speed(self.dev, speed);
}
}
pub fn name(&self) -> String {
unsafe {
let name = libinput_device_get_name(self.dev);
CStr::from_ptr(name).to_bytes().as_bstr().to_string()
}
}
}
impl RegisteredDevice {

View file

@ -54,6 +54,7 @@ extern "C" {
device: *mut libinput_device,
speed: f64,
) -> libinput_config_status;
pub fn libinput_device_get_name(device: *mut libinput_device) -> *const c::c_char;
pub fn libinput_event_destroy(event: *mut libinput_event);
pub fn libinput_event_get_type(event: *mut libinput_event) -> libinput_event_type;

View file

@ -1,7 +1,9 @@
#![allow(non_camel_case_types)]
use crate::pango::consts::{CairoFormat, CairoOperator, PangoEllipsizeMode};
use crate::rect::Rect;
use std::cell::Cell;
use std::ptr;
use std::rc::Rc;
use thiserror::Error;
use uapi::{c, IntoUstr};
@ -73,6 +75,13 @@ extern "C" {
width: *mut c::c_int,
height: *mut c::c_int,
);
fn pango_layout_get_extents(
layout: *mut PangoLayout_,
ink_rect: *mut PangoRectangle,
logical_rect: *mut PangoRectangle,
);
fn pango_extents_to_pixels(inclusive: *mut PangoRectangle, nearest: *mut PangoRectangle);
}
#[derive(Debug, Error)]
@ -89,6 +98,14 @@ pub enum PangoError {
GetData,
}
#[repr(C)]
struct PangoRectangle {
x: c::c_int,
y: c::c_int,
width: c::c_int,
height: c::c_int,
}
pub struct CairoImageSurface {
s: *mut cairo_surface_t,
}
@ -285,6 +302,20 @@ impl PangoLayout {
}
}
pub fn inc_pixel_rect(&self) -> Rect {
unsafe {
let mut rect = PangoRectangle {
x: 0,
y: 0,
width: 0,
height: 0,
};
pango_layout_get_extents(self.l, &mut rect, ptr::null_mut());
pango_extents_to_pixels(&mut rect, ptr::null_mut());
Rect::new_sized(rect.x, rect.y, rect.width, rect.height).unwrap()
}
}
pub fn show_layout(&self) {
unsafe {
pango_cairo_show_layout(self.c.c.c, self.l);

View file

@ -39,14 +39,26 @@ impl Renderer<'_> {
}
render_layer!(output.layers[0]);
render_layer!(output.layers[1]);
let theme = &self.state.theme;
let th = theme.title_height.get();
{
let rd = output.render_data.borrow_mut();
let c = theme.active_title_color.get();
self.fill_boxes(std::slice::from_ref(&rd.active_workspace), &c);
let c = theme.title_color.get();
self.fill_boxes(&rd.inactive_workspaces, &c);
for title in &rd.titles {
self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888);
}
}
if let Some(ws) = output.workspace.get() {
self.render_workspace(&ws, x, y);
self.render_workspace(&ws, x, y + th);
}
render_layer!(output.layers[2]);
render_layer!(output.layers[3]);
for stacked in output.display.xstacked.iter() {
for stacked in self.state.root.xstacked.iter() {
let pos = stacked.absolute_position();
stacked.render(self, pos.x1(), pos.y1());
stacked.render(self, x + pos.x1(), y + pos.y1());
}
}

View file

@ -6,3 +6,9 @@ pub struct Texture {
pub(super) ctx: Rc<RenderContext>,
pub(super) gl: GlTexture,
}
impl Texture {
pub fn width(&self) -> i32 {
self.gl.width
}
}

View file

@ -16,8 +16,9 @@ use crate::logger::Logger;
use crate::rect::Rect;
use crate::render::RenderContext;
use crate::theme::Theme;
use crate::tree::walker::NodeVisitorBase;
use crate::tree::{
ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, WorkspaceNode,
ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, OutputNode, WorkspaceNode,
};
use crate::utils::clonecell::CloneCell;
use crate::utils::copyhashmap::CopyHashMap;
@ -51,6 +52,8 @@ pub struct State {
pub input_device_ids: InputDeviceIds,
pub node_ids: NodeIds,
pub root: Rc<DisplayNode>,
pub workspaces: CopyHashMap<String, Rc<WorkspaceNode>>,
pub dummy_output: CloneCell<Option<Rc<OutputNode>>>,
pub backend_events: AsyncQueue<BackendEvent>,
pub output_handlers: RefCell<AHashMap<OutputId, SpawnedFuture<()>>>,
pub input_device_handlers: RefCell<AHashMap<InputDeviceId, InputDeviceData>>,
@ -92,6 +95,25 @@ impl State {
};
self.cursors.set(cursors);
self.render_ctx.set(Some(ctx.clone()));
struct Walker;
impl NodeVisitorBase for Walker {
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
node.schedule_compute_render_data();
node.visit_children(self);
}
fn visit_output(&mut self, node: &Rc<OutputNode>) {
node.update_render_data();
node.visit_children(self);
}
fn visit_float(&mut self, node: &Rc<FloatNode>) {
node.schedule_render_titles();
node.visit_children(self);
}
}
self.root.clone().visit(&mut Walker);
}
pub fn add_global<T: WaylandGlobal>(&self, global: &Rc<T>) {

View file

@ -2,7 +2,7 @@ use crate::backend::Output;
use crate::ifs::wl_output::WlOutputGlobal;
use crate::rect::Rect;
use crate::state::State;
use crate::tree::{Node, OutputNode, WorkspaceNode};
use crate::tree::{Node, OutputNode, OutputRenderData, WorkspaceNode};
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::clonecell::CloneCell;
use std::cell::{Cell, RefCell};
@ -21,9 +21,8 @@ impl OutputHandler {
self.output.on_change(Rc::new(move || ae.trigger()));
}
let name = self.state.globals.name();
let global = Rc::new(WlOutputGlobal::new(name, &self.output));
let global = Rc::new(WlOutputGlobal::new(name, self.output.clone()));
let on = Rc::new(OutputNode {
display: self.state.root.clone(),
id: self.state.node_ids.next(),
workspaces: RefCell::new(vec![]),
position: Cell::new(Default::default()),
@ -31,17 +30,36 @@ impl OutputHandler {
seat_state: Default::default(),
global: global.clone(),
layers: Default::default(),
render_data: RefCell::new(OutputRenderData {
active_workspace: Rect::new_empty(0, 0),
inactive_workspaces: Default::default(),
titles: Default::default(),
}),
state: self.state.clone(),
is_dummy: false,
});
global.node.set(Some(on.clone()));
let name = 'name: {
for i in 1.. {
let name = i.to_string();
if !self.state.workspaces.contains(&name) {
break 'name name;
}
}
unreachable!();
};
let workspace = Rc::new(WorkspaceNode {
id: self.state.node_ids.next(),
output: CloneCell::new(on.clone()),
container: Default::default(),
stacked: Default::default(),
seat_state: Default::default(),
name: name.clone(),
});
self.state.workspaces.set(name, workspace.clone());
on.workspaces.borrow_mut().push(workspace.clone());
on.workspace.set(Some(workspace));
on.update_render_data();
self.state.root.outputs.set(self.output.id(), on.clone());
self.state.add_global(&global);
self.state.outputs.set(self.output.id(), global.clone());

View file

@ -2,9 +2,14 @@ use crate::format::ARGB8888;
use crate::pango::consts::{
CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE, PANGO_ELLIPSIZE_END, PANGO_SCALE,
};
use crate::pango::{CairoImageSurface, PangoError, PangoFontDescription};
use crate::pango::{
CairoContext, CairoImageSurface, PangoCairoContext, PangoError, PangoFontDescription,
PangoLayout,
};
use crate::rect::Rect;
use crate::render::{RenderContext, RenderError, Texture};
use crate::theme::Color;
use std::ops::Neg;
use std::rc::Rc;
use thiserror::Error;
@ -24,14 +29,15 @@ pub enum TextError {
ImageData(#[source] PangoError),
}
pub fn render(
ctx: &Rc<RenderContext>,
width: i32,
height: i32,
font: &str,
text: &str,
color: Color,
) -> Result<Rc<Texture>, TextError> {
struct Data {
image: Rc<CairoImageSurface>,
cctx: Rc<CairoContext>,
_pctx: Rc<PangoCairoContext>,
_fd: PangoFontDescription,
layout: PangoLayout,
}
fn create_data(font: &str, width: i32, height: i32) -> Result<Data, TextError> {
let image = match CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height) {
Ok(s) => s,
Err(e) => return Err(TextError::CreateImage(e)),
@ -49,22 +55,86 @@ pub fn render(
Ok(l) => l,
Err(e) => return Err(TextError::CreateLayout(e)),
};
layout.set_width((width - 2).max(0) * PANGO_SCALE);
layout.set_ellipsize(PANGO_ELLIPSIZE_END);
layout.set_font_description(&fd);
layout.set_text(text);
let font_height = layout.pixel_size().1;
cctx.set_operator(CAIRO_OPERATOR_SOURCE);
cctx.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _);
cctx.move_to(1.0, ((height - font_height) / 2) as f64);
layout.show_layout();
image.flush();
let data = match image.data() {
Ok(Data {
image,
cctx,
_pctx: pctx,
_fd: fd,
layout,
})
}
pub fn measure(font: &str, text: &str) -> Result<Rect, TextError> {
let data = create_data(font, 1, 1)?;
data.layout.set_text(text);
Ok(data.layout.inc_pixel_rect())
}
pub fn render(
ctx: &Rc<RenderContext>,
width: i32,
height: i32,
font: &str,
text: &str,
color: Color,
) -> Result<Rc<Texture>, TextError> {
render2(ctx, 1, width, height, 1, font, text, color, true)
}
pub fn render2(
ctx: &Rc<RenderContext>,
x: i32,
width: i32,
height: i32,
padding: i32,
font: &str,
text: &str,
color: Color,
ellipsize: bool,
) -> Result<Rc<Texture>, TextError> {
let data = create_data(font, width, height)?;
if ellipsize {
data.layout
.set_width((width - 2 * padding).max(0) * PANGO_SCALE);
data.layout.set_ellipsize(PANGO_ELLIPSIZE_END);
}
data.layout.set_text(text);
let font_height = data.layout.pixel_size().1;
data.cctx.set_operator(CAIRO_OPERATOR_SOURCE);
data.cctx
.set_source_rgba(color.r as _, color.g as _, color.b as _, color.a as _);
data.cctx
.move_to(x as f64, ((height - font_height) / 2) as f64);
data.layout.show_layout();
data.image.flush();
let bytes = match data.image.data() {
Ok(d) => d,
Err(e) => return Err(TextError::ImageData(e)),
};
match ctx.shmem_texture(data, ARGB8888, width, height, image.stride()) {
match ctx.shmem_texture(bytes, ARGB8888, width, height, data.image.stride()) {
Ok(t) => Ok(t),
Err(e) => Err(TextError::RenderError(e)),
}
}
pub fn render_fitting(
ctx: &Rc<RenderContext>,
height: i32,
font: &str,
text: &str,
color: Color,
) -> Result<Rc<Texture>, TextError> {
let rect = measure(font, text)?;
render2(
ctx,
rect.x1().neg(),
rect.width(),
height,
0,
font,
text,
color,
false,
)
}

View file

@ -542,7 +542,7 @@ impl ContainerNode {
self.parent.get().child_title_changed(&**self, &title);
}
fn schedule_compute_render_data(self: &Rc<Self>) {
pub fn schedule_compute_render_data(self: &Rc<Self>) {
if !self.compute_render_data_scheduled.replace(true) {
self.state.pending_container_render_data.push(self.clone());
}

View file

@ -151,7 +151,7 @@ impl FloatNode {
self.schedule_render_titles();
}
fn schedule_render_titles(self: &Rc<Self>) {
pub fn schedule_render_titles(self: &Rc<Self>) {
if !self.render_titles_scheduled.replace(true) {
self.state.pending_float_titles.push(self.clone());
}

View file

@ -3,19 +3,23 @@ use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal};
use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1;
use crate::rect::Rect;
use crate::render::Renderer;
use crate::render::{Renderer, Texture};
use crate::state::State;
use crate::text;
use crate::theme::Color;
use crate::tree::walker::NodeVisitor;
use crate::tree::{DisplayNode, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode};
use crate::utils::clonecell::CloneCell;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::linkedlist::LinkedList;
use std::cell::{Cell, RefCell};
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use std::ops::{Deref, Sub};
use std::rc::Rc;
use crate::fixed::Fixed;
tree_id!(OutputNodeId);
pub struct OutputNode {
pub display: Rc<DisplayNode>,
pub id: OutputNodeId,
pub position: Cell<Rect>,
pub global: Rc<WlOutputGlobal>,
@ -23,6 +27,70 @@ pub struct OutputNode {
pub workspace: CloneCell<Option<Rc<WorkspaceNode>>>,
pub seat_state: NodeSeatState,
pub layers: [LinkedList<Rc<ZwlrLayerSurfaceV1>>; 4],
pub render_data: RefCell<OutputRenderData>,
pub state: Rc<State>,
pub is_dummy: bool,
}
impl OutputNode {
pub fn update_render_data(&self) {
let mut rd = self.render_data.borrow_mut();
rd.titles.clear();
rd.inactive_workspaces.clear();
let workspaces = self.workspaces.borrow_mut();
let mut pos = 0;
let font = self.state.theme.font.borrow_mut();
let th = self.state.theme.title_height.get();
let active_id = self.workspace.get().map(|w| w.id);
for ws in workspaces.deref() {
let mut title_width = th;
'create_texture: {
if let Some(ctx) = self.state.render_ctx.get() {
if th == 0 || ws.name.is_empty() {
break 'create_texture;
}
let title = match text::render_fitting(&ctx, th, &font, &ws.name, Color::GREY) {
Ok(t) => t,
Err(e) => {
log::error!("Could not render title {}: {}", ws.name, ErrorFmt(e));
break 'create_texture;
}
};
let mut x = pos + 1;
if title.width() + 2 > title_width {
title_width = title.width() + 2;
} else {
x = pos + (title_width - title.width()) / 2;
}
rd.titles.push(OutputTitle {
x,
y: 0,
tex: title,
});
}
}
let rect = Rect::new_sized(pos, 0, title_width, th).unwrap();
if Some(ws.id) == active_id {
rd.active_workspace = rect;
} else {
rd.inactive_workspaces.push(rect);
}
pos += title_width;
}
}
}
pub struct OutputTitle {
pub x: i32,
pub y: i32,
pub tex: Rc<Texture>,
}
#[derive(Default)]
pub struct OutputRenderData {
pub active_workspace: Rect,
pub inactive_workspaces: Vec<Rect>,
pub titles: Vec<OutputTitle>,
}
impl Debug for OutputNode {
@ -32,6 +100,14 @@ impl Debug for OutputNode {
}
impl Node for OutputNode {
fn pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
seat.enter_output(&self)
}
fn leave(&self, seat: &WlSeatGlobal) {
seat.leave_output();
}
fn id(&self) -> NodeId {
self.id.into()
}
@ -42,7 +118,7 @@ impl Node for OutputNode {
fn destroy_node(&self, detach: bool) {
if detach {
self.display.clone().remove_child(self);
self.state.root.clone().remove_child(self);
}
let mut workspaces = self.workspaces.borrow_mut();
for workspace in workspaces.drain(..) {
@ -104,9 +180,17 @@ impl Node for OutputNode {
}
fn change_extents(self: Rc<Self>, rect: &Rect) {
let th = self.state.theme.title_height.get();
self.position.set(*rect);
if let Some(c) = self.workspace.get() {
c.change_extents(rect);
let wrect = Rect::new_sized(
rect.x1(),
rect.y1() + th,
rect.width(),
rect.height().sub(th).max(0),
)
.unwrap();
c.change_extents(&wrect);
}
for layer in &self.layers {
for surface in layer.iter() {

View file

@ -18,6 +18,7 @@ pub struct WorkspaceNode {
pub container: CloneCell<Option<Rc<ContainerNode>>>,
pub stacked: LinkedList<Rc<dyn Node>>,
pub seat_state: NodeSeatState,
pub name: String,
}
impl WorkspaceNode {

View file

@ -1,4 +1,5 @@
use ahash::AHashMap;
use std::borrow::Borrow;
use std::cell::{RefCell, RefMut};
use std::fmt::{Debug, Formatter};
use std::hash::Hash;
@ -31,9 +32,11 @@ impl<K: Eq + Hash, V> CopyHashMap<K, V> {
self.map.borrow_mut().insert(k, v);
}
pub fn get(&self, k: &K) -> Option<V>
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<V>
where
V: Clone,
Q: Hash + Eq,
K: Borrow<Q>,
{
self.map.borrow_mut().get(k).cloned()
}
@ -42,7 +45,11 @@ impl<K: Eq + Hash, V> CopyHashMap<K, V> {
self.map.borrow_mut().remove(k)
}
pub fn contains(&self, k: &K) -> bool {
pub fn contains<Q: ?Sized>(&self, k: &Q) -> bool
where
Q: Hash + Eq,
K: Borrow<Q>,
{
self.map.borrow_mut().contains_key(k)
}