autocommit 2022-04-10 18:26:13 CEST
This commit is contained in:
parent
af152f7f3e
commit
6b3316e920
26 changed files with 514 additions and 82 deletions
|
|
@ -3,11 +3,12 @@ pub use {
|
|||
fd::{AsyncFd, FdStatus},
|
||||
task::SpawnedFuture,
|
||||
timeout::Timeout,
|
||||
timer::Timer,
|
||||
};
|
||||
use {
|
||||
crate::{
|
||||
event_loop::{EventLoop, EventLoopError},
|
||||
utils::{copyhashmap::CopyHashMap, numcell::NumCell},
|
||||
utils::{copyhashmap::CopyHashMap, numcell::NumCell, oserror::OsError},
|
||||
wheel::{Wheel, WheelError},
|
||||
},
|
||||
fd::AsyncFdData,
|
||||
|
|
@ -19,15 +20,21 @@ use {
|
|||
},
|
||||
thiserror::Error,
|
||||
timeout::TimeoutData,
|
||||
uapi::OwnedFd,
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AsyncError {
|
||||
#[error("The timer wheel returned an error: {0}")]
|
||||
#[error("The timer wheel returned an error")]
|
||||
WheelError(#[from] WheelError),
|
||||
#[error("The event loop caused an error: {0}")]
|
||||
#[error("The event loop caused an error")]
|
||||
EventLoopError(#[from] EventLoopError),
|
||||
#[error("Could not read from a timer")]
|
||||
TimerReadError(#[source] OsError),
|
||||
#[error("Could not set a timer")]
|
||||
SetTimer(#[source] OsError),
|
||||
#[error("Could not create a timer")]
|
||||
CreateTimer(#[source] OsError),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
|
|
@ -71,6 +78,10 @@ impl AsyncEngine {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn timer(self: &Rc<Self>, clock_id: c::c_int) -> Result<Timer, AsyncError> {
|
||||
Timer::new(self, clock_id)
|
||||
}
|
||||
|
||||
pub fn spawn<T, F: Future<Output = T> + 'static>(&self, f: F) -> SpawnedFuture<T> {
|
||||
self.queue.spawn(Phase::EventHandling, f)
|
||||
}
|
||||
|
|
@ -146,6 +157,59 @@ mod yield_ {
|
|||
}
|
||||
}
|
||||
|
||||
mod timer {
|
||||
use {
|
||||
crate::async_engine::{AsyncEngine, AsyncError, AsyncFd},
|
||||
std::{rc::Rc, time::Duration},
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Timer {
|
||||
fd: AsyncFd,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub(super) fn new(eng: &Rc<AsyncEngine>, clock_id: c::c_int) -> Result<Self, AsyncError> {
|
||||
let fd = match uapi::timerfd_create(clock_id, c::TFD_CLOEXEC | c::TFD_NONBLOCK) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => return Err(AsyncError::CreateTimer(e.into())),
|
||||
};
|
||||
let afd = eng.fd(&Rc::new(fd))?;
|
||||
Ok(Self { fd: afd })
|
||||
}
|
||||
|
||||
pub async fn expired(&self) -> Result<u64, AsyncError> {
|
||||
self.fd.readable().await?;
|
||||
let mut buf = 0u64;
|
||||
if let Err(e) = uapi::read(self.fd.raw(), &mut buf) {
|
||||
return Err(AsyncError::TimerReadError(e.into()));
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub fn program(
|
||||
&self,
|
||||
initial: Option<Duration>,
|
||||
periodic: Option<Duration>,
|
||||
) -> Result<(), AsyncError> {
|
||||
let mut timerspec: c::itimerspec = uapi::pod_zeroed();
|
||||
if let Some(init) = initial {
|
||||
timerspec.it_value.tv_sec = init.as_secs() as _;
|
||||
timerspec.it_value.tv_nsec = init.subsec_nanos() as _;
|
||||
if let Some(per) = periodic {
|
||||
timerspec.it_interval.tv_sec = per.as_secs() as _;
|
||||
timerspec.it_interval.tv_nsec = per.subsec_nanos() as _;
|
||||
}
|
||||
}
|
||||
if let Err(e) = uapi::timerfd_settime(self.fd.raw(), 0, &timerspec) {
|
||||
return Err(AsyncError::SetTimer(e.into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod timeout {
|
||||
use {
|
||||
crate::wheel::{Wheel, WheelDispatcher, WheelId},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use {
|
|||
async_engine::FdStatus,
|
||||
backend::{AxisSource, InputEvent, KeyState, ScrollAxis},
|
||||
backends::metal::MetalBackend,
|
||||
ifs::wl_seat::PX_PER_SCROLL,
|
||||
libinput::{
|
||||
consts::{
|
||||
LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_KEY_STATE_PRESSED,
|
||||
|
|
@ -14,7 +15,6 @@ use {
|
|||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
use crate::ifs::wl_seat::PX_PER_SCROLL;
|
||||
|
||||
macro_rules! unpack {
|
||||
($slf:expr, $ev:expr) => {{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use {
|
|||
},
|
||||
fixed::Fixed,
|
||||
format::XRGB8888,
|
||||
ifs::wl_seat::PX_PER_SCROLL,
|
||||
render::{Framebuffer, RenderContext, RenderError},
|
||||
state::State,
|
||||
utils::{
|
||||
|
|
@ -54,7 +55,6 @@ use {
|
|||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
use crate::ifs::wl_seat::PX_PER_SCROLL;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum XBackendError {
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ fn main_(forker: Rc<ForkerProxy>, logger: Arc<Logger>, _args: &RunArgs) -> Resul
|
|||
logger,
|
||||
connectors: Default::default(),
|
||||
outputs: Default::default(),
|
||||
status: Default::default(),
|
||||
});
|
||||
{
|
||||
let dummy_output = Rc::new(OutputNode {
|
||||
|
|
@ -150,6 +151,7 @@ fn main_(forker: Rc<ForkerProxy>, logger: Arc<Logger>, _args: &RunArgs) -> Resul
|
|||
render_data: Default::default(),
|
||||
state: state.clone(),
|
||||
is_dummy: true,
|
||||
status: Default::default(),
|
||||
});
|
||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||
id: state.node_ids.next(),
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use {
|
|||
keyboard::ModifiedKeySym,
|
||||
},
|
||||
libloading::Library,
|
||||
std::{cell::Cell, ptr, rc::Rc},
|
||||
std::{cell::Cell, mem, ptr, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ impl ConfigProxy {
|
|||
impl Drop for ConfigProxy {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.handler.dropped.set(true);
|
||||
self.handler.do_drop();
|
||||
(self.handler.unref)(self.handler.client_data.get());
|
||||
}
|
||||
}
|
||||
|
|
@ -120,6 +120,9 @@ impl ConfigProxy {
|
|||
workspace_ids: NumCell::new(1),
|
||||
workspaces_by_name: Default::default(),
|
||||
workspaces_by_id: Default::default(),
|
||||
timer_ids: NumCell::new(1),
|
||||
timers_by_name: Default::default(),
|
||||
timers_by_id: Default::default(),
|
||||
});
|
||||
let init_msg =
|
||||
bincode::encode_to_vec(&InitMessage::V1(V1InitMessage {}), bincode_ops()).unwrap();
|
||||
|
|
@ -172,6 +175,8 @@ unsafe extern "C" fn handle_msg(data: *const u8, msg: *const u8, size: usize) {
|
|||
if server.dropped.get() {
|
||||
return;
|
||||
}
|
||||
let rc = Rc::from_raw(server);
|
||||
let msg = std::slice::from_raw_parts(msg, size);
|
||||
server.handle_request(msg);
|
||||
rc.handle_request(msg);
|
||||
mem::forget(rc);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use {
|
||||
crate::{
|
||||
backend,
|
||||
backend::{ConnectorId, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId},
|
||||
async_engine::{AsyncError, SpawnedFuture, Timer},
|
||||
backend::{
|
||||
self, ConnectorId, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId,
|
||||
},
|
||||
compositor::MAX_EXTENTS,
|
||||
ifs::wl_seat::{SeatId, WlSeatGlobal},
|
||||
state::{ConnectorData, DeviceHandlerData, OutputData, State},
|
||||
|
|
@ -32,8 +34,9 @@ use {
|
|||
},
|
||||
libloading::Library,
|
||||
log::Level,
|
||||
std::{cell::Cell, rc::Rc},
|
||||
std::{cell::Cell, rc::Rc, time::Duration},
|
||||
thiserror::Error,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
pub(super) struct ConfigProxyHandler {
|
||||
|
|
@ -47,12 +50,31 @@ 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>>,
|
||||
|
||||
pub timer_ids: NumCell<u64>,
|
||||
pub timers_by_name: CopyHashMap<Rc<String>, Rc<TimerData>>,
|
||||
pub timers_by_id: CopyHashMap<u64, Rc<TimerData>>,
|
||||
}
|
||||
|
||||
pub(super) struct TimerData {
|
||||
timer: Timer,
|
||||
id: u64,
|
||||
name: Rc<String>,
|
||||
_handler: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
impl ConfigProxyHandler {
|
||||
pub fn do_drop(&self) {
|
||||
self.dropped.set(true);
|
||||
|
||||
self.timers_by_name.clear();
|
||||
self.timers_by_id.clear();
|
||||
}
|
||||
|
||||
pub fn send(&self, msg: &ServerMessage) {
|
||||
let mut buf = self.bufs.pop().unwrap_or_default();
|
||||
buf.clear();
|
||||
|
|
@ -132,6 +154,79 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_status(&self, status: &str) {
|
||||
self.state.set_status(status);
|
||||
}
|
||||
|
||||
fn get_timer(&self, timer: jay_config::Timer) -> Result<Rc<TimerData>, CphError> {
|
||||
match self.timers_by_id.get(&timer.0) {
|
||||
Some(t) => Ok(t),
|
||||
_ => Err(CphError::TimerDoesNotExist(timer)),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_remove_timer(&self, timer: jay_config::Timer) -> Result<(), CphError> {
|
||||
let timer = self.get_timer(timer)?;
|
||||
self.timers_by_id.remove(&timer.id);
|
||||
self.timers_by_name.remove(&timer.name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_program_timer(
|
||||
&self,
|
||||
timer: jay_config::Timer,
|
||||
initial: Option<Duration>,
|
||||
periodic: Option<Duration>,
|
||||
) -> Result<(), CphError> {
|
||||
let timer = self.get_timer(timer)?;
|
||||
timer.timer.program(initial, periodic)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_timer(self: &Rc<Self>, name: &str) -> Result<(), CphError> {
|
||||
let name = Rc::new(name.to_owned());
|
||||
if let Some(t) = self.timers_by_name.get(&name) {
|
||||
self.respond(Response::GetTimer {
|
||||
timer: jay_config::Timer(t.id),
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
let id = self.timer_ids.fetch_add(1);
|
||||
let timer = self.state.eng.timer(c::CLOCK_BOOTTIME)?;
|
||||
let handler = {
|
||||
let timer = timer.clone();
|
||||
let slf = self.clone();
|
||||
self.state.eng.spawn(async move {
|
||||
loop {
|
||||
match timer.expired().await {
|
||||
Ok(_) => slf.send(&ServerMessage::TimerExpired {
|
||||
timer: jay_config::Timer(id),
|
||||
}),
|
||||
Err(e) => {
|
||||
log::error!("Could not wait for timer expiration: {}", ErrorFmt(e));
|
||||
if let Some(timer) = slf.timers_by_id.remove(&id) {
|
||||
slf.timers_by_name.remove(&timer.name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
let td = Rc::new(TimerData {
|
||||
timer,
|
||||
id,
|
||||
name: name.clone(),
|
||||
_handler: handler,
|
||||
});
|
||||
self.timers_by_name.set(name.clone(), td.clone());
|
||||
self.timers_by_id.set(id, td.clone());
|
||||
self.respond(Response::GetTimer {
|
||||
timer: jay_config::Timer(id),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_close(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.close();
|
||||
|
|
@ -361,6 +456,7 @@ impl ConfigProxyHandler {
|
|||
return Err(CphError::InvalidConnectorPosition(x, y));
|
||||
}
|
||||
let old_pos = connector.node.global.pos.get();
|
||||
connector.node.set_position(x, y);
|
||||
let seats = self.state.globals.seats.lock();
|
||||
for seat in seats.values() {
|
||||
if seat.get_output().id == connector.node.id {
|
||||
|
|
@ -371,7 +467,6 @@ impl ConfigProxyHandler {
|
|||
);
|
||||
}
|
||||
}
|
||||
connector.node.set_position(x, y);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -642,13 +737,13 @@ impl ConfigProxyHandler {
|
|||
self.state.theme.underline_color.set(color.into());
|
||||
}
|
||||
|
||||
pub fn handle_request(&self, msg: &[u8]) {
|
||||
pub fn handle_request(self: &Rc<Self>, msg: &[u8]) {
|
||||
if let Err(e) = self.handle_request_(msg) {
|
||||
log::error!("Could not handle client request: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request_(&self, msg: &[u8]) -> Result<(), CphError> {
|
||||
fn handle_request_(self: &Rc<Self>, msg: &[u8]) -> Result<(), CphError> {
|
||||
let (request, _) = match bincode::decode_from_slice::<ClientMessage, _>(msg, bincode_ops())
|
||||
{
|
||||
Ok(msg) => msg,
|
||||
|
|
@ -770,6 +865,18 @@ impl ConfigProxyHandler {
|
|||
.handle_connector_set_position(connector, x, y)
|
||||
.wrn("connector_set_position")?,
|
||||
ClientMessage::Close { seat } => self.handle_close(seat).wrn("close")?,
|
||||
ClientMessage::SetStatus { status } => self.handle_set_status(status),
|
||||
ClientMessage::GetTimer { name } => self.handle_get_timer(name).wrn("get_timer")?,
|
||||
ClientMessage::RemoveTimer { timer } => {
|
||||
self.handle_remove_timer(timer).wrn("remove_timer")?
|
||||
}
|
||||
ClientMessage::ProgramTimer {
|
||||
timer,
|
||||
initial,
|
||||
periodic,
|
||||
} => self
|
||||
.handle_program_timer(timer, initial, periodic)
|
||||
.wrn("program_timer")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -801,6 +908,8 @@ enum CphError {
|
|||
DeviceDoesNotExist(InputDevice),
|
||||
#[error("Connector {0:?} does not exist")]
|
||||
ConnectorDoesNotExist(Connector),
|
||||
#[error("Timer {0:?} does not exist")]
|
||||
TimerDoesNotExist(jay_config::Timer),
|
||||
#[error("Connector {0:?} does not exist or is not connected")]
|
||||
OutputDoesNotExist(Connector),
|
||||
#[error("{0}x{1} is not a valid connector position")]
|
||||
|
|
@ -817,6 +926,8 @@ enum CphError {
|
|||
ParsingFailed(#[source] DecodeError),
|
||||
#[error("Could not process a `{0}` request")]
|
||||
FailedRequest(&'static str, #[source] Box<Self>),
|
||||
#[error(transparent)]
|
||||
AsyncError(#[from] AsyncError),
|
||||
}
|
||||
|
||||
trait WithRequestName {
|
||||
|
|
|
|||
|
|
@ -217,6 +217,16 @@ impl WlSeatGlobal {
|
|||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
self.pos.set((Fixed::from_int(x), Fixed::from_int(y)));
|
||||
self.trigger_tree_changed();
|
||||
'set_output: {
|
||||
let outputs = self.state.outputs.lock();
|
||||
for output in outputs.values() {
|
||||
if output.node.global.pos.get().contains(x, y) {
|
||||
self.output.set(output.node.clone());
|
||||
break 'set_output;
|
||||
}
|
||||
}
|
||||
self.output.set(self.state.dummy_output.get().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position(&self) -> (Fixed, Fixed) {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use {
|
|||
wl_surface::{xdg_surface::xdg_popup::XdgPopup, WlSurface},
|
||||
},
|
||||
object::ObjectId,
|
||||
tree::{FloatNode, Node, OutputNode, ToplevelNode},
|
||||
tree::{FloatNode, Node, ToplevelNode},
|
||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||
wire::WlDataOfferId,
|
||||
xkbcommon::{ModifierState, XKB_KEY_DOWN, XKB_KEY_UP},
|
||||
|
|
@ -172,6 +172,7 @@ impl WlSeatGlobal {
|
|||
Some(o) => o,
|
||||
_ => return,
|
||||
};
|
||||
self.output.set(output.node.clone());
|
||||
let pos = output.node.global.pos.get();
|
||||
x += Fixed::from_int(pos.x1());
|
||||
y += Fixed::from_int(pos.y1());
|
||||
|
|
@ -191,6 +192,7 @@ impl WlSeatGlobal {
|
|||
let outputs = self.state.outputs.lock();
|
||||
for output in outputs.values() {
|
||||
if output.node.global.pos.get().contains(x_int, y_int) {
|
||||
self.output.set(output.node.clone());
|
||||
break 'warp;
|
||||
}
|
||||
}
|
||||
|
|
@ -517,10 +519,6 @@ 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));
|
||||
|
|
|
|||
|
|
@ -293,6 +293,7 @@ impl Node for XdgPopup {
|
|||
}
|
||||
|
||||
fn set_visible(&self, visible: bool) {
|
||||
log::info!("set visible = {}", visible);
|
||||
self.xdg.set_visible(visible);
|
||||
self.xdg.seat_state.set_visible(self, visible);
|
||||
}
|
||||
|
|
@ -362,11 +363,18 @@ impl XdgSurfaceExt for XdgPopup {
|
|||
*wl = Some(ws.stacked.add_last(self.clone()));
|
||||
*dl = Some(state.root.stacked.add_last(self.clone()));
|
||||
state.tree_changed();
|
||||
self.set_visible(
|
||||
self.parent
|
||||
.get()
|
||||
.map(|p| p.surface.visible.get())
|
||||
.unwrap_or(false),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if wl.take().is_some() {
|
||||
drop(wl);
|
||||
drop(dl);
|
||||
self.set_visible(false);
|
||||
self.destroy_node(true);
|
||||
self.send_popup_done();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -280,16 +280,7 @@ impl Xwindow {
|
|||
self.data.state.tree_changed();
|
||||
}
|
||||
Change::Map if self.data.info.wants_floating.get() => {
|
||||
let ws = match self.data.state.root.outputs.lock().values().cloned().next() {
|
||||
Some(output) => output.ensure_workspace(),
|
||||
_ => self
|
||||
.data
|
||||
.state
|
||||
.dummy_output
|
||||
.get()
|
||||
.unwrap()
|
||||
.ensure_workspace(),
|
||||
};
|
||||
let ws = self.data.state.float_map_ws();
|
||||
let ext = self.data.info.extents.get();
|
||||
self.data
|
||||
.state
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ extern "C" {
|
|||
desc: *const PangoFontDescription_,
|
||||
);
|
||||
fn pango_layout_set_text(layout: *mut PangoLayout_, text: *const c::c_char, length: c::c_int);
|
||||
fn pango_layout_set_markup(layout: *mut PangoLayout_, text: *const c::c_char, length: c::c_int);
|
||||
fn pango_layout_get_pixel_size(
|
||||
layout: *mut PangoLayout_,
|
||||
width: *mut c::c_int,
|
||||
|
|
@ -295,6 +296,12 @@ impl PangoLayout {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_markup(&self, text: &str) {
|
||||
unsafe {
|
||||
pango_layout_set_markup(self.l, text.as_ptr() as _, text.len() as _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pixel_size(&self) -> (i32, i32) {
|
||||
unsafe {
|
||||
let mut w = 0;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ impl Renderer<'_> {
|
|||
for title in &rd.titles {
|
||||
self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888);
|
||||
}
|
||||
if let Some(status) = &rd.status {
|
||||
self.render_texture(&status.tex, x + status.x, y + status.y, ARGB8888);
|
||||
}
|
||||
}
|
||||
if let Some(ws) = output.workspace.get() {
|
||||
self.render_workspace(&ws, x, y + th);
|
||||
|
|
|
|||
23
src/state.rs
23
src/state.rs
|
|
@ -75,6 +75,7 @@ pub struct State {
|
|||
pub logger: Arc<Logger>,
|
||||
pub connectors: CopyHashMap<ConnectorId, Rc<ConnectorData>>,
|
||||
pub outputs: CopyHashMap<ConnectorId, Rc<OutputData>>,
|
||||
pub status: CloneCell<Rc<String>>,
|
||||
}
|
||||
|
||||
pub struct InputDeviceData {
|
||||
|
|
@ -265,4 +266,26 @@ impl State {
|
|||
seat.workspace_changed(&output);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn float_map_ws(&self) -> Rc<WorkspaceNode> {
|
||||
if let Some(seat) = self.seat_queue.last() {
|
||||
let output = seat.get_output();
|
||||
if !output.is_dummy {
|
||||
return output.ensure_workspace();
|
||||
}
|
||||
}
|
||||
if let Some(output) = self.root.outputs.lock().values().cloned().next() {
|
||||
return output.ensure_workspace();
|
||||
}
|
||||
self.dummy_output.get().unwrap().ensure_workspace()
|
||||
}
|
||||
|
||||
pub fn set_status(&self, status: &str) {
|
||||
let status = Rc::new(status.to_owned());
|
||||
self.status.set(status.clone());
|
||||
let outputs = self.root.outputs.lock();
|
||||
for output in outputs.values() {
|
||||
output.set_status(&status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,9 +97,11 @@ impl ConnectorHandler {
|
|||
active_workspace: Rect::new_empty(0, 0),
|
||||
inactive_workspaces: Default::default(),
|
||||
titles: Default::default(),
|
||||
status: None,
|
||||
}),
|
||||
state: self.state.clone(),
|
||||
is_dummy: false,
|
||||
status: self.state.status.clone(),
|
||||
});
|
||||
let mode = info.initial_mode;
|
||||
let output_data = Rc::new(OutputData {
|
||||
|
|
|
|||
21
src/text.rs
21
src/text.rs
|
|
@ -68,9 +68,13 @@ fn create_data(font: &str, width: i32, height: i32) -> Result<Data, TextError> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn measure(font: &str, text: &str) -> Result<Rect, TextError> {
|
||||
pub fn measure(font: &str, text: &str, markup: bool) -> Result<Rect, TextError> {
|
||||
let data = create_data(font, 1, 1)?;
|
||||
data.layout.set_text(text);
|
||||
if markup {
|
||||
data.layout.set_markup(text);
|
||||
} else {
|
||||
data.layout.set_text(text);
|
||||
}
|
||||
Ok(data.layout.inc_pixel_rect())
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +86,7 @@ pub fn render(
|
|||
text: &str,
|
||||
color: Color,
|
||||
) -> Result<Rc<Texture>, TextError> {
|
||||
render2(ctx, 1, width, height, 1, font, text, color, true)
|
||||
render2(ctx, 1, width, height, 1, font, text, color, true, false)
|
||||
}
|
||||
|
||||
pub fn render2(
|
||||
|
|
@ -95,6 +99,7 @@ pub fn render2(
|
|||
text: &str,
|
||||
color: Color,
|
||||
ellipsize: bool,
|
||||
markup: bool,
|
||||
) -> Result<Rc<Texture>, TextError> {
|
||||
let data = create_data(font, width, height)?;
|
||||
if ellipsize {
|
||||
|
|
@ -102,7 +107,11 @@ pub fn render2(
|
|||
.set_width((width - 2 * padding).max(0) * PANGO_SCALE);
|
||||
data.layout.set_ellipsize(PANGO_ELLIPSIZE_END);
|
||||
}
|
||||
data.layout.set_text(text);
|
||||
if markup {
|
||||
data.layout.set_markup(text);
|
||||
} else {
|
||||
data.layout.set_text(text);
|
||||
}
|
||||
let font_height = data.layout.pixel_size().1;
|
||||
data.cctx.set_operator(CAIRO_OPERATOR_SOURCE);
|
||||
data.cctx
|
||||
|
|
@ -127,8 +136,9 @@ pub fn render_fitting(
|
|||
font: &str,
|
||||
text: &str,
|
||||
color: Color,
|
||||
markup: bool,
|
||||
) -> Result<Rc<Texture>, TextError> {
|
||||
let rect = measure(font, text)?;
|
||||
let rect = measure(font, text, markup)?;
|
||||
render2(
|
||||
ctx,
|
||||
rect.x1().neg(),
|
||||
|
|
@ -139,5 +149,6 @@ pub fn render_fitting(
|
|||
text,
|
||||
color,
|
||||
false,
|
||||
markup,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ use {
|
|||
backend::KeyState,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT},
|
||||
ifs::wl_seat::{
|
||||
wl_pointer::{PendingScroll, VERTICAL_SCROLL},
|
||||
NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT, PX_PER_SCROLL,
|
||||
},
|
||||
rect::Rect,
|
||||
render::{Renderer, Texture},
|
||||
state::State,
|
||||
|
|
@ -32,8 +35,6 @@ use {
|
|||
rc::Rc,
|
||||
},
|
||||
};
|
||||
use crate::ifs::wl_seat::PX_PER_SCROLL;
|
||||
use crate::ifs::wl_seat::wl_pointer::{PendingScroll, VERTICAL_SCROLL};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
|
|
@ -290,6 +291,7 @@ impl ContainerNode {
|
|||
};
|
||||
new.clone().set_workspace(&self.workspace.get());
|
||||
new.clone().set_parent(self.clone());
|
||||
new.set_visible(self.visible.get());
|
||||
let num_children = self.num_children.fetch_add(1) + 1;
|
||||
self.update_content_size();
|
||||
let new_child_factor = 1.0 / num_children as f64;
|
||||
|
|
@ -1134,24 +1136,36 @@ impl Node for ContainerNode {
|
|||
Some(c) => c,
|
||||
None => return,
|
||||
};
|
||||
let (have_mc, was_mc) = match self.mono_child.get() {
|
||||
None => (false, false),
|
||||
Some(mc) => (true, mc.node.id() == old.id()),
|
||||
};
|
||||
node.focus_history.set(None);
|
||||
let link = node.append(ContainerChild {
|
||||
node: new.clone(),
|
||||
active: Cell::new(false),
|
||||
body: Cell::new(node.body.get()),
|
||||
content: Cell::new(node.content.get()),
|
||||
content: Default::default(),
|
||||
factor: Cell::new(node.factor.get()),
|
||||
title: Default::default(),
|
||||
title_rect: Cell::new(node.title_rect.get()),
|
||||
focus_history: Cell::new(None),
|
||||
});
|
||||
let body = link.body.get();
|
||||
drop(node);
|
||||
let mut body = None;
|
||||
if was_mc {
|
||||
self.mono_child.set(Some(link.to_ref()));
|
||||
body = Some(self.mono_body.get());
|
||||
} else if !have_mc {
|
||||
body = Some(link.body.get());
|
||||
};
|
||||
self.child_nodes.borrow_mut().insert(new.id(), link);
|
||||
new.clone().set_parent(self.clone());
|
||||
new.clone().set_workspace(&self.workspace.get());
|
||||
let body = body.move_(self.abs_x1.get(), self.abs_y1.get());
|
||||
new.clone().change_extents(&body);
|
||||
if let Some(body) = body {
|
||||
let body = body.move_(self.abs_x1.get(), self.abs_y1.get());
|
||||
new.clone().change_extents(&body);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_child(self: Rc<Self>, child: &dyn Node) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use {
|
|||
crate::{
|
||||
backend::Mode,
|
||||
cursor::KnownCursor,
|
||||
fixed::Fixed,
|
||||
ifs::{
|
||||
wl_output::WlOutputGlobal,
|
||||
wl_seat::{NodeSeatState, WlSeatGlobal},
|
||||
|
|
@ -36,6 +35,7 @@ pub struct OutputNode {
|
|||
pub render_data: RefCell<OutputRenderData>,
|
||||
pub state: Rc<State>,
|
||||
pub is_dummy: bool,
|
||||
pub status: CloneCell<Rc<String>>,
|
||||
}
|
||||
|
||||
impl OutputNode {
|
||||
|
|
@ -43,6 +43,7 @@ impl OutputNode {
|
|||
let mut rd = self.render_data.borrow_mut();
|
||||
rd.titles.clear();
|
||||
rd.inactive_workspaces.clear();
|
||||
rd.status = None;
|
||||
let mut pos = 0;
|
||||
let font = self.state.theme.font.borrow_mut();
|
||||
let th = self.state.theme.title_height.get();
|
||||
|
|
@ -54,13 +55,14 @@ impl OutputNode {
|
|||
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 title =
|
||||
match text::render_fitting(&ctx, th, &font, &ws.name, Color::GREY, false) {
|
||||
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;
|
||||
|
|
@ -82,6 +84,29 @@ impl OutputNode {
|
|||
}
|
||||
pos += title_width;
|
||||
}
|
||||
'set_status: {
|
||||
let ctx = match self.state.render_ctx.get() {
|
||||
Some(ctx) => ctx,
|
||||
_ => break 'set_status,
|
||||
};
|
||||
let status = self.status.get();
|
||||
if status.is_empty() {
|
||||
break 'set_status;
|
||||
}
|
||||
let title = match text::render_fitting(&ctx, th, &font, &status, Color::GREY, true) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
log::error!("Could not render status {}: {}", status, ErrorFmt(e));
|
||||
break 'set_status;
|
||||
}
|
||||
};
|
||||
let pos = self.global.pos.get().width() - title.width() - 1;
|
||||
rd.status = Some(OutputTitle {
|
||||
x: pos,
|
||||
y: 0,
|
||||
tex: title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_workspace(self: &Rc<Self>) -> Rc<WorkspaceNode> {
|
||||
|
|
@ -106,7 +131,7 @@ impl OutputNode {
|
|||
seat_state: Default::default(),
|
||||
name: name.clone(),
|
||||
output_link: Default::default(),
|
||||
visible: Cell::new(false),
|
||||
visible: Cell::new(true),
|
||||
});
|
||||
self.state.workspaces.set(name, workspace.clone());
|
||||
workspace
|
||||
|
|
@ -122,9 +147,9 @@ impl OutputNode {
|
|||
if old.id == ws.id {
|
||||
return;
|
||||
}
|
||||
old.visible.set(false);
|
||||
old.set_visible(false);
|
||||
}
|
||||
ws.visible.set(true);
|
||||
ws.set_visible(true);
|
||||
ws.clone().change_extents(&self.workspace_rect());
|
||||
}
|
||||
|
||||
|
|
@ -161,6 +186,7 @@ impl OutputNode {
|
|||
|
||||
fn change_extents_(&self, rect: &Rect) {
|
||||
self.global.pos.set(*rect);
|
||||
self.update_render_data();
|
||||
if let Some(c) = self.workspace.get() {
|
||||
c.change_extents(&self.workspace_rect());
|
||||
}
|
||||
|
|
@ -194,6 +220,11 @@ impl OutputNode {
|
|||
}
|
||||
FindTreeResult::Other
|
||||
}
|
||||
|
||||
pub fn set_status(&self, status: &Rc<String>) {
|
||||
self.status.set(status.clone());
|
||||
self.update_render_data();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputTitle {
|
||||
|
|
@ -207,6 +238,7 @@ pub struct OutputRenderData {
|
|||
pub active_workspace: Rect,
|
||||
pub inactive_workspaces: Vec<Rect>,
|
||||
pub titles: Vec<OutputTitle>,
|
||||
pub status: Option<OutputTitle>,
|
||||
}
|
||||
|
||||
impl Debug for OutputNode {
|
||||
|
|
@ -283,10 +315,6 @@ impl Node for OutputNode {
|
|||
unimplemented!();
|
||||
}
|
||||
|
||||
fn pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
|
||||
seat.enter_output(&self)
|
||||
}
|
||||
|
||||
fn pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
seat.set_known_cursor(KnownCursor::Default);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ impl WorkspaceNode {
|
|||
let pos = self.position.get();
|
||||
container.clone().change_extents(&pos);
|
||||
container.clone().set_workspace(self);
|
||||
container.set_visible(self.visible.get());
|
||||
self.container.set(Some(container.clone()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue