autocommit 2022-04-01 01:44:10 CEST
This commit is contained in:
parent
ab4ac883ee
commit
2dd433aa04
32 changed files with 626 additions and 139 deletions
|
|
@ -1,98 +1,65 @@
|
|||
use jay_config::embedded::grab_input_device;
|
||||
use jay_config::input::{
|
||||
InputDevice, CAP_KEYBOARD, CAP_POINTER,
|
||||
};
|
||||
use jay_config::input::{InputDevice, CAP_KEYBOARD, CAP_POINTER};
|
||||
use jay_config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT};
|
||||
use jay_config::keyboard::syms::{
|
||||
SYM_Super_L, SYM_b, SYM_comma, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_m, SYM_p,
|
||||
SYM_period, SYM_q, SYM_r, SYM_t, SYM_v, SYM_y, SYM_F1, SYM_F10, SYM_F11, SYM_F12, SYM_F2,
|
||||
SYM_F3, SYM_F4, SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9,
|
||||
SYM_Super_L, SYM_b, SYM_d, SYM_f, SYM_h, SYM_j, SYM_k, SYM_l, SYM_m, SYM_p, SYM_q, SYM_t,
|
||||
SYM_v, SYM_y, SYM_F1, SYM_F10, SYM_F11, SYM_F12, SYM_F13, SYM_F14, SYM_F15, SYM_F16, SYM_F17,
|
||||
SYM_F18, SYM_F19, SYM_F2, SYM_F20, SYM_F21, SYM_F22, SYM_F23, SYM_F24, SYM_F25, SYM_F3, SYM_F4,
|
||||
SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9,
|
||||
};
|
||||
use jay_config::theme::{get_title_height, set_title_color, set_title_height, Color};
|
||||
use jay_config::Axis::{Horizontal, Vertical};
|
||||
use jay_config::Direction::{Down, Left, Right, Up};
|
||||
use jay_config::{
|
||||
config, create_seat, input_devices, on_new_input_device, quit, switch_to_vt, Command, Seat,
|
||||
config, create_seat, get_workspace, input_devices, on_new_input_device, quit, switch_to_vt,
|
||||
Command, Seat,
|
||||
};
|
||||
use rand::Rng;
|
||||
|
||||
const MOD: Modifiers = ALT;
|
||||
|
||||
fn configure_seat(s: Seat) {
|
||||
log::info!("Configuring seat {:?}", s);
|
||||
|
||||
let change_rate = move |delta| {
|
||||
let (rate, delay) = s.repeat_rate();
|
||||
let new_rate = rate - delta;
|
||||
let new_delay = delay + 10 * delta;
|
||||
log::info!("Changing repeat rate to {}/{}", new_rate, new_delay);
|
||||
s.set_repeat_rate(new_rate, new_delay);
|
||||
};
|
||||
|
||||
s.bind(CTRL | SHIFT | SYM_l, move || change_rate(-1));
|
||||
s.bind(CTRL | SHIFT | SYM_r, move || change_rate(1));
|
||||
|
||||
s.bind(MOD | SYM_comma, move || {
|
||||
let mut rng = rand::thread_rng();
|
||||
set_title_color(Color {
|
||||
r: rng.gen(),
|
||||
g: rng.gen(),
|
||||
b: rng.gen(),
|
||||
a: rng.gen(),
|
||||
})
|
||||
});
|
||||
|
||||
s.bind(MOD | SYM_period, move || {
|
||||
set_title_height(get_title_height() + 1)
|
||||
});
|
||||
|
||||
s.bind(MOD | SYM_h, move || s.focus(Left));
|
||||
s.bind(MOD | SYM_j, move || s.focus(Down));
|
||||
s.bind(MOD | SYM_k, move || s.focus(Up));
|
||||
s.bind(MOD | SYM_l, move || s.focus(Right));
|
||||
|
||||
s.bind(MOD | SYM_d, move || s.create_split(Horizontal));
|
||||
s.bind(MOD | SYM_v, move || s.create_split(Vertical));
|
||||
|
||||
s.bind(MOD | SYM_t, move || {
|
||||
s.set_split(s.split().other());
|
||||
});
|
||||
|
||||
s.bind(MOD | SYM_m, move || {
|
||||
s.set_mono(!s.mono());
|
||||
});
|
||||
|
||||
s.bind(MOD | SYM_f, move || {
|
||||
s.focus_parent();
|
||||
});
|
||||
|
||||
s.bind(MOD | SHIFT | SYM_f, move || {
|
||||
s.toggle_floating();
|
||||
});
|
||||
|
||||
s.bind(MOD | SHIFT | SYM_h, move || s.move_(Left));
|
||||
s.bind(MOD | SHIFT | SYM_j, move || s.move_(Down));
|
||||
s.bind(MOD | SHIFT | SYM_k, move || s.move_(Up));
|
||||
s.bind(MOD | SHIFT | SYM_l, move || s.move_(Right));
|
||||
|
||||
s.bind(MOD | SYM_d, move || s.create_split(Horizontal));
|
||||
s.bind(MOD | SYM_v, move || s.create_split(Vertical));
|
||||
|
||||
s.bind(MOD | SYM_t, move || s.set_split(s.split().other()));
|
||||
|
||||
s.bind(MOD | SYM_m, move || s.set_mono(!s.mono()));
|
||||
|
||||
s.bind(MOD | SYM_f, move || s.focus_parent());
|
||||
|
||||
s.bind(MOD | SHIFT | SYM_f, move || s.toggle_floating());
|
||||
|
||||
s.bind(SYM_Super_L, || Command::new("alacritty").spawn());
|
||||
|
||||
s.bind(MOD | SYM_p, || Command::new("xeyes").spawn());
|
||||
|
||||
s.bind(MOD | SYM_q, quit);
|
||||
|
||||
s.bind(CTRL | ALT | SYM_F1, || switch_to_vt(1));
|
||||
s.bind(CTRL | ALT | SYM_F2, || switch_to_vt(2));
|
||||
s.bind(CTRL | ALT | SYM_F3, || switch_to_vt(3));
|
||||
s.bind(CTRL | ALT | SYM_F4, || switch_to_vt(4));
|
||||
s.bind(CTRL | ALT | SYM_F5, || switch_to_vt(5));
|
||||
s.bind(CTRL | ALT | SYM_F6, || switch_to_vt(6));
|
||||
s.bind(CTRL | ALT | SYM_F7, || switch_to_vt(7));
|
||||
s.bind(CTRL | ALT | SYM_F8, || switch_to_vt(8));
|
||||
s.bind(CTRL | ALT | SYM_F9, || switch_to_vt(9));
|
||||
s.bind(CTRL | ALT | SYM_F10, || switch_to_vt(10));
|
||||
s.bind(CTRL | ALT | SYM_F11, || switch_to_vt(11));
|
||||
s.bind(CTRL | ALT | SYM_F12, || switch_to_vt(12));
|
||||
let fnkeys = [
|
||||
SYM_F1, SYM_F2, SYM_F3, SYM_F4, SYM_F5, SYM_F6, SYM_F7, SYM_F8, SYM_F9, SYM_F10, SYM_F11,
|
||||
SYM_F12,
|
||||
];
|
||||
for (i, sym) in fnkeys.into_iter().enumerate() {
|
||||
s.bind(CTRL | ALT | sym, move || switch_to_vt(i as u32 + 1));
|
||||
}
|
||||
|
||||
let fnkeys2 = [
|
||||
SYM_F13, SYM_F14, SYM_F15, SYM_F16, SYM_F17, SYM_F18, SYM_F19, SYM_F20, SYM_F21, SYM_F22,
|
||||
SYM_F23, SYM_F24, SYM_F25,
|
||||
];
|
||||
for (i, sym) in fnkeys2.into_iter().enumerate() {
|
||||
let ws = get_workspace(&format!("{}", i + 1));
|
||||
s.bind(MOD | sym, move || s.show_workspace(ws));
|
||||
}
|
||||
|
||||
fn do_grab(s: Seat, grab: bool) {
|
||||
for device in s.input_devices() {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::_private::{bincode_ops, logging, Config, ConfigEntry, ConfigEntryGen,
|
|||
use crate::input::{AccelProfile, Capability, InputDevice};
|
||||
use crate::keyboard::keymap::Keymap;
|
||||
use crate::theme::Color;
|
||||
use crate::{Axis, Command, Direction, LogLevel, ModifiedKeySym, Seat};
|
||||
use crate::{Axis, Command, Direction, LogLevel, ModifiedKeySym, Seat, Workspace};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -199,6 +199,21 @@ impl Client {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_workspace(&self, name: &str) -> Workspace {
|
||||
let res = self.with_response(|| self.send(&ClientMessage::GetWorkspace { name }));
|
||||
match res {
|
||||
Response::GetWorkspace { workspace } => workspace,
|
||||
_ => {
|
||||
log::error!("Server did not send a response to a get_workspace request");
|
||||
Workspace(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_workspace(&self, seat: Seat, workspace: Workspace) {
|
||||
self.send(&ClientMessage::ShowWorkspace { seat, workspace });
|
||||
}
|
||||
|
||||
pub fn split(&self, seat: Seat) -> Axis {
|
||||
let res = self.with_response(|| self.send(&ClientMessage::GetSplit { seat }));
|
||||
match res {
|
||||
|
|
@ -337,6 +352,17 @@ impl Client {
|
|||
self.send(&ClientMessage::SetTransformMatrix { device, matrix })
|
||||
}
|
||||
|
||||
pub fn device_name(&self, device: InputDevice) -> String {
|
||||
let res = self.with_response(|| self.send(&ClientMessage::GetDeviceName { device }));
|
||||
match res {
|
||||
Response::GetDeviceName { name } => name,
|
||||
_ => {
|
||||
log::error!("Server did not send a response to a get_device_name request");
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_capability(&self, device: InputDevice, cap: Capability) -> bool {
|
||||
let res = self.with_response(|| self.send(&ClientMessage::HasCapability { device, cap }));
|
||||
match res {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::keyboard::keymap::Keymap;
|
|||
use crate::keyboard::mods::Modifiers;
|
||||
use crate::keyboard::syms::KeySym;
|
||||
use crate::theme::Color;
|
||||
use crate::{Axis, Direction, LogLevel, Seat};
|
||||
use crate::{Axis, Direction, LogLevel, Seat, Workspace};
|
||||
use bincode::{BorrowDecode, Decode, Encode};
|
||||
|
||||
#[derive(Encode, BorrowDecode, Debug)]
|
||||
|
|
@ -157,6 +157,16 @@ pub enum ClientMessage<'a> {
|
|||
device: InputDevice,
|
||||
matrix: [[f64; 2]; 2],
|
||||
},
|
||||
GetDeviceName {
|
||||
device: InputDevice,
|
||||
},
|
||||
GetWorkspace {
|
||||
name: &'a str,
|
||||
},
|
||||
ShowWorkspace {
|
||||
seat: Seat,
|
||||
workspace: Workspace,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Debug)]
|
||||
|
|
@ -172,6 +182,8 @@ pub enum Response {
|
|||
GetTitleHeight { height: i32 },
|
||||
GetBorderWidth { width: i32 },
|
||||
HasCapability { has: bool },
|
||||
GetDeviceName { name: String },
|
||||
GetWorkspace { workspace: Workspace },
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Debug)]
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ impl InputDevice {
|
|||
pub fn set_transform_matrix(self, matrix: [[f64; 2]; 2]) {
|
||||
get!().set_transform_matrix(self, matrix);
|
||||
}
|
||||
|
||||
pub fn name(self) -> String {
|
||||
get!(String::new()).device_name(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -134,6 +134,10 @@ impl Seat {
|
|||
pub fn toggle_floating(self) {
|
||||
get!().toggle_floating(self);
|
||||
}
|
||||
|
||||
pub fn show_workspace(self, workspace: Workspace) {
|
||||
get!().show_workspace(self, workspace)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_seats() -> Vec<Seat> {
|
||||
|
|
@ -201,3 +205,10 @@ impl Command {
|
|||
get!().spawn(self);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct Workspace(pub u64);
|
||||
|
||||
pub fn get_workspace(name: &str) -> Workspace {
|
||||
get!(Workspace(0)).get_workspace(name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod dummy;
|
||||
pub mod metal;
|
||||
pub mod x;
|
||||
|
|
|
|||
36
src/backends/dummy.rs
Normal file
36
src/backends/dummy.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 ] };
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
31
src/pango.rs
31
src/pango.rs
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
24
src/state.rs
24
src/state.rs
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
110
src/text.rs
110
src/text.rs
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue