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
35
Cargo.lock
generated
35
Cargo.lock
generated
|
|
@ -123,6 +123,19 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.1.6"
|
||||
|
|
@ -167,6 +180,7 @@ dependencies = [
|
|||
name = "default-config"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"jay-config",
|
||||
"log",
|
||||
"rand",
|
||||
|
|
@ -387,6 +401,16 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
|
|
@ -663,6 +687,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uapi"
|
||||
version = "0.2.7"
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ crate-type = ["lib", "cdylib"]
|
|||
jay-config = { path = "../jay-config" }
|
||||
log = "0.4.14"
|
||||
rand = "0.8.5"
|
||||
chrono = "0.4.19"
|
||||
|
|
|
|||
|
|
@ -1,25 +1,31 @@
|
|||
use jay_config::{
|
||||
config,
|
||||
drm::{get_connector, on_connector_connected, on_new_connector},
|
||||
embedded::grab_input_device,
|
||||
get_workspace,
|
||||
input::{
|
||||
capability::{CAP_KEYBOARD, CAP_POINTER},
|
||||
create_seat, input_devices, on_new_input_device, InputDevice, Seat,
|
||||
},
|
||||
keyboard::{
|
||||
mods::{Modifiers, ALT, CTRL, SHIFT},
|
||||
syms::{
|
||||
SYM_Super_L, SYM_b, SYM_c, 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 {
|
||||
chrono::{format::StrftimeItems, Local, Timelike},
|
||||
jay_config::{
|
||||
config,
|
||||
drm::{get_connector, on_connector_connected, on_new_connector},
|
||||
embedded::grab_input_device,
|
||||
get_timer, get_workspace,
|
||||
input::{
|
||||
capability::{CAP_KEYBOARD, CAP_POINTER},
|
||||
create_seat, input_devices, on_new_input_device, InputDevice, Seat,
|
||||
},
|
||||
keyboard::{
|
||||
mods::{Modifiers, ALT, CTRL, SHIFT},
|
||||
syms::{
|
||||
SYM_Super_L, SYM_b, SYM_c, 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,
|
||||
},
|
||||
},
|
||||
quit,
|
||||
status::set_status,
|
||||
switch_to_vt,
|
||||
Axis::{Horizontal, Vertical},
|
||||
Command,
|
||||
Direction::{Down, Left, Right, Up},
|
||||
},
|
||||
quit, switch_to_vt,
|
||||
Axis::{Horizontal, Vertical},
|
||||
Command,
|
||||
Direction::{Down, Left, Right, Up},
|
||||
std::time::Duration,
|
||||
};
|
||||
|
||||
const MOD: Modifiers = ALT;
|
||||
|
|
@ -117,6 +123,28 @@ pub fn configure() {
|
|||
on_new_connector(move |_| handle_connectors_changed());
|
||||
on_connector_connected(move |_| handle_connectors_changed());
|
||||
handle_connectors_changed();
|
||||
|
||||
{
|
||||
let time_format: Vec<_> = StrftimeItems::new("%Y-%m-%d %H:%M:%S").collect();
|
||||
let update_status = move || {
|
||||
let status = format!(
|
||||
"{}",
|
||||
Local::now().format_with_items(time_format.iter()),
|
||||
);
|
||||
set_status(&status);
|
||||
};
|
||||
update_status();
|
||||
let initial = {
|
||||
let now = Local::now();
|
||||
5000 - (now.second() * 1000 + now.timestamp_subsec_millis()) % 5000
|
||||
};
|
||||
let timer = get_timer("status_timer");
|
||||
timer.program(
|
||||
Duration::from_millis(initial as u64),
|
||||
Some(Duration::from_secs(5)),
|
||||
);
|
||||
timer.on_tick(update_status);
|
||||
}
|
||||
}
|
||||
|
||||
config!(configure);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use {
|
|||
input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat},
|
||||
keyboard::keymap::Keymap,
|
||||
theme::Color,
|
||||
Axis, Command, Direction, LogLevel, ModifiedKeySym, Workspace,
|
||||
Axis, Command, Direction, LogLevel, ModifiedKeySym, Timer, Workspace,
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -23,6 +23,7 @@ use {
|
|||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
time::Duration,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ pub(crate) struct Client {
|
|||
srv_unref: unsafe extern "C" fn(data: *const u8),
|
||||
srv_handler: unsafe extern "C" fn(data: *const u8, msg: *const u8, size: usize),
|
||||
key_handlers: RefCell<HashMap<(Seat, ModifiedKeySym), Rc<dyn Fn()>>>,
|
||||
timer_handlers: RefCell<HashMap<Timer, Rc<dyn Fn()>>>,
|
||||
response: RefCell<Vec<Response>>,
|
||||
on_new_seat: RefCell<Option<Rc<dyn Fn(Seat)>>>,
|
||||
on_new_input_device: RefCell<Option<Rc<dyn Fn(InputDevice)>>>,
|
||||
|
|
@ -113,6 +115,7 @@ pub unsafe extern "C" fn init(
|
|||
srv_unref,
|
||||
srv_handler,
|
||||
key_handlers: Default::default(),
|
||||
timer_handlers: Default::default(),
|
||||
response: Default::default(),
|
||||
on_new_seat: Default::default(),
|
||||
on_new_input_device: Default::default(),
|
||||
|
|
@ -222,6 +225,33 @@ impl Client {
|
|||
mono
|
||||
}
|
||||
|
||||
pub fn get_timer(&self, name: &str) -> Timer {
|
||||
let res = self.with_response(|| self.send(&ClientMessage::GetTimer { name }));
|
||||
get_response!(res, Timer(0), GetTimer, timer);
|
||||
timer
|
||||
}
|
||||
|
||||
pub fn remove_timer(&self, timer: Timer) {
|
||||
self.send(&ClientMessage::RemoveTimer { timer });
|
||||
}
|
||||
|
||||
pub fn program_timer(
|
||||
&self,
|
||||
timer: Timer,
|
||||
initial: Option<Duration>,
|
||||
periodic: Option<Duration>,
|
||||
) {
|
||||
self.send(&ClientMessage::ProgramTimer {
|
||||
timer,
|
||||
initial,
|
||||
periodic,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn on_timer_tick<F: Fn() + 'static>(&self, timer: Timer, f: F) {
|
||||
self.timer_handlers.borrow_mut().insert(timer, Rc::new(f));
|
||||
}
|
||||
|
||||
pub fn get_workspace(&self, name: &str) -> Workspace {
|
||||
let res = self.with_response(|| self.send(&ClientMessage::GetWorkspace { name }));
|
||||
get_response!(res, Workspace(0), GetWorkspace, workspace);
|
||||
|
|
@ -288,6 +318,10 @@ impl Client {
|
|||
self.send(&ClientMessage::SetMono { seat, mono });
|
||||
}
|
||||
|
||||
pub fn set_status(&self, status: &str) {
|
||||
self.send(&ClientMessage::SetStatus { status });
|
||||
}
|
||||
|
||||
pub fn set_split(&self, seat: Seat, axis: Axis) {
|
||||
self.send(&ClientMessage::SetSplit { seat, axis });
|
||||
}
|
||||
|
|
@ -507,6 +541,12 @@ impl Client {
|
|||
}
|
||||
}
|
||||
ServerMessage::DelConnector { .. } => {}
|
||||
ServerMessage::TimerExpired { timer } => {
|
||||
let handler = self.timer_handlers.borrow_mut().get(&timer).cloned();
|
||||
if let Some(handler) = handler {
|
||||
handler();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ use {
|
|||
input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat},
|
||||
keyboard::{keymap::Keymap, mods::Modifiers, syms::KeySym},
|
||||
theme::Color,
|
||||
Axis, Direction, LogLevel, Workspace,
|
||||
Axis, Direction, LogLevel, Timer, Workspace,
|
||||
},
|
||||
bincode::{BorrowDecode, Decode, Encode},
|
||||
std::time::Duration,
|
||||
};
|
||||
|
||||
#[derive(Encode, BorrowDecode, Debug)]
|
||||
|
|
@ -38,6 +39,9 @@ pub enum ServerMessage {
|
|||
mods: Modifiers,
|
||||
sym: KeySym,
|
||||
},
|
||||
TimerExpired {
|
||||
timer: Timer,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Encode, BorrowDecode, Debug)]
|
||||
|
|
@ -77,6 +81,9 @@ pub enum ClientMessage<'a> {
|
|||
GetSplit {
|
||||
seat: Seat,
|
||||
},
|
||||
SetStatus {
|
||||
status: &'a str,
|
||||
},
|
||||
SetSplit {
|
||||
seat: Seat,
|
||||
axis: Axis,
|
||||
|
|
@ -203,6 +210,17 @@ pub enum ClientMessage<'a> {
|
|||
seat: Seat,
|
||||
workspace: Workspace,
|
||||
},
|
||||
GetTimer {
|
||||
name: &'a str,
|
||||
},
|
||||
RemoveTimer {
|
||||
timer: Timer,
|
||||
},
|
||||
ProgramTimer {
|
||||
timer: Timer,
|
||||
initial: Option<Duration>,
|
||||
periodic: Option<Duration>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Debug)]
|
||||
|
|
@ -242,6 +260,9 @@ pub enum Response {
|
|||
GetDeviceName {
|
||||
name: String,
|
||||
},
|
||||
GetTimer {
|
||||
timer: Timer,
|
||||
},
|
||||
GetWorkspace {
|
||||
workspace: Workspace,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::keyboard::{keymap::Keymap, ModifiedKeySym},
|
||||
bincode::{Decode, Encode},
|
||||
std::collections::HashMap,
|
||||
std::{collections::HashMap, time::Duration},
|
||||
};
|
||||
|
||||
#[macro_use]
|
||||
|
|
@ -12,6 +12,7 @@ pub mod drm;
|
|||
pub mod embedded;
|
||||
pub mod input;
|
||||
pub mod keyboard;
|
||||
pub mod status;
|
||||
pub mod theme;
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Debug)]
|
||||
|
|
@ -91,3 +92,28 @@ pub struct Workspace(pub u64);
|
|||
pub fn get_workspace(name: &str) -> Workspace {
|
||||
get!(Workspace(0)).get_workspace(name)
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct Timer(pub u64);
|
||||
|
||||
pub fn get_timer(name: &str) -> Timer {
|
||||
get!(Timer(0)).get_timer(name)
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub fn program(self, initial: Duration, periodic: Option<Duration>) {
|
||||
get!().program_timer(self, Some(initial), periodic);
|
||||
}
|
||||
|
||||
pub fn cancel(self) {
|
||||
get!().program_timer(self, None, None);
|
||||
}
|
||||
|
||||
pub fn remove(self) {
|
||||
get!().remove_timer(self);
|
||||
}
|
||||
|
||||
pub fn on_tick<F: Fn() + 'static>(self, f: F) {
|
||||
get!().on_timer_tick(self, f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
jay-config/src/status.rs
Normal file
3
jay-config/src/status.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub fn set_status(status: &str) {
|
||||
get!().set_status(status);
|
||||
}
|
||||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
todo.md
4
todo.md
|
|
@ -1,11 +1,9 @@
|
|||
# todo
|
||||
|
||||
- Container moving (mouse)
|
||||
- Workspaces
|
||||
- presentation time
|
||||
- viewporter
|
||||
- session lock
|
||||
- xwayland
|
||||
|
||||
# done
|
||||
|
||||
|
|
@ -21,3 +19,5 @@
|
|||
- Config
|
||||
- Highlighting active
|
||||
- Toplevel splitting
|
||||
- Workspaces
|
||||
- xwayland
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue