1
0
Fork 0
forked from wry/wry

autocommit 2022-04-10 18:26:13 CEST

This commit is contained in:
Julian Orth 2022-04-10 18:26:13 +02:00
parent af152f7f3e
commit 6b3316e920
26 changed files with 514 additions and 82 deletions

View file

@ -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},

View file

@ -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) => {{

View file

@ -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 {

View file

@ -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(),

View file

@ -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);
}

View file

@ -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 {

View file

@ -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) {

View file

@ -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));

View file

@ -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();
}

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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);
}
}
}

View file

@ -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 {

View file

@ -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,
)
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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()));
}
}