Merge pull request #85 from mahkoh/jorth/activation
wayland: implement xdg-activation
This commit is contained in:
commit
ccacdda03e
28 changed files with 676 additions and 58 deletions
|
|
@ -253,6 +253,10 @@ pub mod colors {
|
||||||
///
|
///
|
||||||
/// Default: `#772831`.
|
/// Default: `#772831`.
|
||||||
const 13 => CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR,
|
const 13 => CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR,
|
||||||
|
/// The title background color of a window that has requested attention.
|
||||||
|
///
|
||||||
|
/// Default: `#23092c`.
|
||||||
|
const 14 => ATTENTION_REQUESTED_BACKGROUND_COLOR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use {
|
||||||
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{
|
utils::{
|
||||||
|
activation_token::ActivationToken,
|
||||||
asyncevent::AsyncEvent,
|
asyncevent::AsyncEvent,
|
||||||
buffd::{MsgFormatter, MsgParser, MsgParserError, OutBufferSwapchain},
|
buffd::{MsgFormatter, MsgParser, MsgParserError, OutBufferSwapchain},
|
||||||
copyhashmap::{CopyHashMap, Locked},
|
copyhashmap::{CopyHashMap, Locked},
|
||||||
|
|
@ -147,6 +148,7 @@ impl Clients {
|
||||||
symmetric_delete: Cell::new(false),
|
symmetric_delete: Cell::new(false),
|
||||||
last_xwayland_serial: Cell::new(0),
|
last_xwayland_serial: Cell::new(0),
|
||||||
surfaces_by_xwayland_serial: Default::default(),
|
surfaces_by_xwayland_serial: Default::default(),
|
||||||
|
activation_tokens: Default::default(),
|
||||||
});
|
});
|
||||||
track!(data, data);
|
track!(data, data);
|
||||||
let display = Rc::new(WlDisplay::new(&data));
|
let display = Rc::new(WlDisplay::new(&data));
|
||||||
|
|
@ -217,6 +219,7 @@ impl Drop for ClientHolder {
|
||||||
self.data.flush_request.clear();
|
self.data.flush_request.clear();
|
||||||
self.data.shutdown.clear();
|
self.data.shutdown.clear();
|
||||||
self.data.surfaces_by_xwayland_serial.clear();
|
self.data.surfaces_by_xwayland_serial.clear();
|
||||||
|
self.data.remove_activation_tokens();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,6 +259,7 @@ pub struct Client {
|
||||||
pub symmetric_delete: Cell<bool>,
|
pub symmetric_delete: Cell<bool>,
|
||||||
pub last_xwayland_serial: Cell<u64>,
|
pub last_xwayland_serial: Cell<u64>,
|
||||||
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
||||||
|
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
||||||
|
|
@ -444,6 +448,12 @@ impl Client {
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_activation_tokens(&self) {
|
||||||
|
for token in &*self.activation_tokens.borrow() {
|
||||||
|
self.state.activation_tokens.remove(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait WaylandObject: Object {
|
pub trait WaylandObject: Object {
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,7 @@ fn start_compositor2(
|
||||||
workspace_watchers: Default::default(),
|
workspace_watchers: Default::default(),
|
||||||
default_workspace_capture: Cell::new(true),
|
default_workspace_capture: Cell::new(true),
|
||||||
default_gfx_api: Cell::new(GfxApi::OpenGl),
|
default_gfx_api: Cell::new(GfxApi::OpenGl),
|
||||||
|
activation_tokens: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -413,6 +414,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
jay_workspaces: Default::default(),
|
jay_workspaces: Default::default(),
|
||||||
capture: Cell::new(false),
|
capture: Cell::new(false),
|
||||||
title_texture: Cell::new(None),
|
title_texture: Cell::new(None),
|
||||||
|
attention_requests: Default::default(),
|
||||||
});
|
});
|
||||||
dummy_workspace.output_link.set(Some(
|
dummy_workspace.output_link.set(Some(
|
||||||
dummy_output.workspaces.add_last(dummy_workspace.clone()),
|
dummy_output.workspaces.add_last(dummy_workspace.clone()),
|
||||||
|
|
|
||||||
|
|
@ -1066,6 +1066,7 @@ impl ConfigProxyHandler {
|
||||||
FOCUSED_TITLE_TEXT_COLOR => &colors.focused_title_text,
|
FOCUSED_TITLE_TEXT_COLOR => &colors.focused_title_text,
|
||||||
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => &colors.focused_inactive_title_text,
|
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => &colors.focused_inactive_title_text,
|
||||||
BAR_STATUS_TEXT_COLOR => &colors.bar_text,
|
BAR_STATUS_TEXT_COLOR => &colors.bar_text,
|
||||||
|
ATTENTION_REQUESTED_BACKGROUND_COLOR => &colors.attention_requested_background,
|
||||||
_ => return Err(CphError::UnknownColor(colorable.0)),
|
_ => return Err(CphError::UnknownColor(colorable.0)),
|
||||||
};
|
};
|
||||||
Ok(colorable)
|
Ok(colorable)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ use {
|
||||||
wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1Global,
|
wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1Global,
|
||||||
wp_tearing_control_manager_v1::WpTearingControlManagerV1Global,
|
wp_tearing_control_manager_v1::WpTearingControlManagerV1Global,
|
||||||
wp_viewporter::WpViewporterGlobal,
|
wp_viewporter::WpViewporterGlobal,
|
||||||
|
xdg_activation_v1::XdgActivationV1Global,
|
||||||
xdg_wm_base::XdgWmBaseGlobal,
|
xdg_wm_base::XdgWmBaseGlobal,
|
||||||
zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
|
zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
|
||||||
zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global,
|
zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global,
|
||||||
|
|
@ -162,6 +163,7 @@ impl Globals {
|
||||||
add_singleton!(WpSinglePixelBufferManagerV1Global);
|
add_singleton!(WpSinglePixelBufferManagerV1Global);
|
||||||
add_singleton!(WpCursorShapeManagerV1Global);
|
add_singleton!(WpCursorShapeManagerV1Global);
|
||||||
add_singleton!(WpContentTypeManagerV1Global);
|
add_singleton!(WpContentTypeManagerV1Global);
|
||||||
|
add_singleton!(XdgActivationV1Global);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ pub mod wp_presentation_feedback;
|
||||||
pub mod wp_single_pixel_buffer_manager_v1;
|
pub mod wp_single_pixel_buffer_manager_v1;
|
||||||
pub mod wp_tearing_control_manager_v1;
|
pub mod wp_tearing_control_manager_v1;
|
||||||
pub mod wp_viewporter;
|
pub mod wp_viewporter;
|
||||||
|
pub mod xdg_activation_token_v1;
|
||||||
|
pub mod xdg_activation_v1;
|
||||||
pub mod xdg_positioner;
|
pub mod xdg_positioner;
|
||||||
pub mod xdg_wm_base;
|
pub mod xdg_wm_base;
|
||||||
pub mod zwlr_layer_shell_v1;
|
pub mod zwlr_layer_shell_v1;
|
||||||
|
|
|
||||||
|
|
@ -1059,6 +1059,12 @@ impl WlSurface {
|
||||||
pub fn set_content_type(&self, content_type: Option<ContentType>) {
|
pub fn set_content_type(&self, content_type: Option<ContentType>) {
|
||||||
self.pending.content_type.set(Some(content_type));
|
self.pending.content_type.set(Some(content_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn request_activation(&self) {
|
||||||
|
if let Some(tl) = self.toplevel.get() {
|
||||||
|
tl.tl_data().request_attention(tl.tl_as_node());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,6 @@ pub struct XwindowData {
|
||||||
tree_id!(XwindowId);
|
tree_id!(XwindowId);
|
||||||
pub struct Xwindow {
|
pub struct Xwindow {
|
||||||
pub id: XwindowId,
|
pub id: XwindowId,
|
||||||
pub seat_state: NodeSeatState,
|
|
||||||
pub data: Rc<XwindowData>,
|
pub data: Rc<XwindowData>,
|
||||||
pub x: Rc<XSurface>,
|
pub x: Rc<XSurface>,
|
||||||
pub display_link: RefCell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
|
pub display_link: RefCell<Option<LinkedNode<Rc<dyn StackedNode>>>>,
|
||||||
|
|
@ -214,7 +213,6 @@ impl Xwindow {
|
||||||
tld.pos.set(surface.extents.get());
|
tld.pos.set(surface.extents.get());
|
||||||
let slf = Rc::new(Self {
|
let slf = Rc::new(Self {
|
||||||
id: data.state.node_ids.next(),
|
id: data.state.node_ids.next(),
|
||||||
seat_state: Default::default(),
|
|
||||||
data: data.clone(),
|
data: data.clone(),
|
||||||
display_link: Default::default(),
|
display_link: Default::default(),
|
||||||
toplevel_data: tld,
|
toplevel_data: tld,
|
||||||
|
|
@ -298,7 +296,7 @@ impl Node for Xwindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_seat_state(&self) -> &NodeSeatState {
|
fn node_seat_state(&self) -> &NodeSeatState {
|
||||||
&self.seat_state
|
&self.toplevel_data.seat_state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_visit(self: Rc<Self>, visitor: &mut dyn NodeVisitor) {
|
fn node_visit(self: Rc<Self>, visitor: &mut dyn NodeVisitor) {
|
||||||
|
|
@ -422,7 +420,7 @@ impl ToplevelNode for Xwindow {
|
||||||
|
|
||||||
fn tl_set_visible(&self, visible: bool) {
|
fn tl_set_visible(&self, visible: bool) {
|
||||||
self.x.surface.set_visible(visible);
|
self.x.surface.set_visible(visible);
|
||||||
self.seat_state.set_visible(self, visible);
|
self.toplevel_data.set_visible(self, visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_destroy(&self) {
|
fn tl_destroy(&self) {
|
||||||
|
|
|
||||||
108
src/ifs/xdg_activation_token_v1.rs
Normal file
108
src/ifs/xdg_activation_token_v1.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::Object,
|
||||||
|
utils::{
|
||||||
|
activation_token::{activation_token, ActivationToken},
|
||||||
|
buffd::{MsgParser, MsgParserError},
|
||||||
|
},
|
||||||
|
wire::{xdg_activation_token_v1::*, XdgActivationTokenV1Id},
|
||||||
|
},
|
||||||
|
std::{cell::Cell, rc::Rc},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_TOKENS_PER_CLIENT: usize = 8;
|
||||||
|
|
||||||
|
pub struct XdgActivationTokenV1 {
|
||||||
|
pub id: XdgActivationTokenV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
already_used: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XdgActivationTokenV1 {
|
||||||
|
pub fn new(id: XdgActivationTokenV1Id, client: &Rc<Client>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
already_used: Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_serial(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationTokenV1Error> {
|
||||||
|
let _req: SetSerial = self.client.parse(self, parser)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_app_id(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationTokenV1Error> {
|
||||||
|
let _req: SetAppId = self.client.parse(self, parser)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_surface(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationTokenV1Error> {
|
||||||
|
let req: SetSurface = self.client.parse(self, parser)?;
|
||||||
|
self.client.lookup(req.surface)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationTokenV1Error> {
|
||||||
|
let _req: Commit = self.client.parse(self, parser)?;
|
||||||
|
if self.already_used.replace(true) {
|
||||||
|
return Err(XdgActivationTokenV1Error::AlreadyUsed);
|
||||||
|
}
|
||||||
|
let token = activation_token();
|
||||||
|
self.client.state.activation_tokens.set(token, ());
|
||||||
|
let mut tokens = self.client.activation_tokens.borrow_mut();
|
||||||
|
if tokens.len() >= MAX_TOKENS_PER_CLIENT {
|
||||||
|
if let Some(oldest) = tokens.pop_front() {
|
||||||
|
self.client.state.activation_tokens.remove(&oldest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens.push_back(token);
|
||||||
|
self.send_done(token);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationTokenV1Error> {
|
||||||
|
let _req: Destroy = self.client.parse(self, parser)?;
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_done(&self, token: ActivationToken) {
|
||||||
|
let token = token.to_string();
|
||||||
|
self.client.event(Done {
|
||||||
|
self_id: self.id,
|
||||||
|
token: &token,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = XdgActivationTokenV1;
|
||||||
|
|
||||||
|
SET_SERIAL => set_serial,
|
||||||
|
SET_APP_ID => set_app_id,
|
||||||
|
SET_SURFACE => set_surface,
|
||||||
|
COMMIT => commit,
|
||||||
|
DESTROY => destroy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for XdgActivationTokenV1 {}
|
||||||
|
|
||||||
|
simple_add_obj!(XdgActivationTokenV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum XdgActivationTokenV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("Parsing failed")]
|
||||||
|
MsgParserError(#[source] Box<MsgParserError>),
|
||||||
|
#[error("The activation token has already been used")]
|
||||||
|
AlreadyUsed,
|
||||||
|
}
|
||||||
|
efrom!(XdgActivationTokenV1Error, ClientError);
|
||||||
|
efrom!(XdgActivationTokenV1Error, MsgParserError);
|
||||||
120
src/ifs/xdg_activation_v1.rs
Normal file
120
src/ifs/xdg_activation_v1.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
globals::{Global, GlobalName},
|
||||||
|
ifs::xdg_activation_token_v1::XdgActivationTokenV1,
|
||||||
|
leaks::Tracker,
|
||||||
|
object::Object,
|
||||||
|
utils::{
|
||||||
|
activation_token::ActivationToken,
|
||||||
|
buffd::{MsgParser, MsgParserError},
|
||||||
|
opaque::OpaqueError,
|
||||||
|
},
|
||||||
|
wire::{xdg_activation_v1::*, XdgActivationV1Id},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct XdgActivationV1Global {
|
||||||
|
pub name: GlobalName,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XdgActivationV1Global {
|
||||||
|
pub fn new(name: GlobalName) -> Self {
|
||||||
|
Self { name }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_(
|
||||||
|
self: Rc<Self>,
|
||||||
|
id: XdgActivationV1Id,
|
||||||
|
client: &Rc<Client>,
|
||||||
|
version: u32,
|
||||||
|
) -> Result<(), XdgActivationV1Error> {
|
||||||
|
let mgr = Rc::new(XdgActivationV1 {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
track!(client, mgr);
|
||||||
|
client.add_client_obj(&mgr)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_base!(XdgActivationV1Global, XdgActivationV1, XdgActivationV1Error);
|
||||||
|
|
||||||
|
simple_add_global!(XdgActivationV1Global);
|
||||||
|
|
||||||
|
impl Global for XdgActivationV1Global {
|
||||||
|
fn singleton(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct XdgActivationV1 {
|
||||||
|
pub id: XdgActivationV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XdgActivationV1 {
|
||||||
|
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationV1Error> {
|
||||||
|
let _req: Destroy = self.client.parse(self, parser)?;
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_activation_token(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationV1Error> {
|
||||||
|
let req: GetActivationToken = self.client.parse(self, parser)?;
|
||||||
|
let token = Rc::new(XdgActivationTokenV1::new(req.id, &self.client));
|
||||||
|
track!(self.client, token);
|
||||||
|
self.client.add_client_obj(&token)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgActivationV1Error> {
|
||||||
|
let req: Activate = self.client.parse(self, parser)?;
|
||||||
|
let token: ActivationToken = req.token.parse()?;
|
||||||
|
let surface = self.client.lookup(req.surface)?;
|
||||||
|
if self.client.state.activation_tokens.remove(&token).is_none() {
|
||||||
|
log::warn!(
|
||||||
|
"Client requested activation with unknown token {}",
|
||||||
|
req.token
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
surface.request_activation();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = XdgActivationV1;
|
||||||
|
|
||||||
|
DESTROY => destroy,
|
||||||
|
GET_ACTIVATION_TOKEN => get_activation_token,
|
||||||
|
ACTIVATE => activate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for XdgActivationV1 {}
|
||||||
|
|
||||||
|
simple_add_obj!(XdgActivationV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum XdgActivationV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("Parsing failed")]
|
||||||
|
MsgParserError(#[source] Box<MsgParserError>),
|
||||||
|
#[error("Could not parse the activation token")]
|
||||||
|
ParseActivationToken(#[from] OpaqueError),
|
||||||
|
}
|
||||||
|
efrom!(XdgActivationV1Error, ClientError);
|
||||||
|
efrom!(XdgActivationV1Error, MsgParserError);
|
||||||
|
|
@ -136,6 +136,9 @@ impl Renderer<'_> {
|
||||||
};
|
};
|
||||||
self.base
|
self.base
|
||||||
.fill_boxes2(&rd.captured_inactive_workspaces, &c, x, y);
|
.fill_boxes2(&rd.captured_inactive_workspaces, &c, x, y);
|
||||||
|
let c = theme.colors.attention_requested_background.get();
|
||||||
|
self.base
|
||||||
|
.fill_boxes2(&rd.attention_requested_workspaces, &c, x, y);
|
||||||
let scale = output.preferred_scale.get();
|
let scale = output.preferred_scale.get();
|
||||||
for title in &rd.titles {
|
for title in &rd.titles {
|
||||||
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
let (x, y) = self.base.scale_point(x + title.tex_x, y + title.tex_y);
|
||||||
|
|
@ -209,6 +212,8 @@ impl Renderer<'_> {
|
||||||
self.base.fill_boxes2(&rd.title_rects, &c, x, y);
|
self.base.fill_boxes2(&rd.title_rects, &c, x, y);
|
||||||
let c = self.state.theme.colors.focused_title_background.get();
|
let c = self.state.theme.colors.focused_title_background.get();
|
||||||
self.base.fill_boxes2(&rd.active_title_rects, &c, x, y);
|
self.base.fill_boxes2(&rd.active_title_rects, &c, x, y);
|
||||||
|
let c = self.state.theme.colors.attention_requested_background.get();
|
||||||
|
self.base.fill_boxes2(&rd.attention_title_rects, &c, x, y);
|
||||||
let c = self.state.theme.colors.separator.get();
|
let c = self.state.theme.colors.separator.get();
|
||||||
self.base.fill_boxes2(&rd.underline_rects, &c, x, y);
|
self.base.fill_boxes2(&rd.underline_rects, &c, x, y);
|
||||||
let c = self.state.theme.colors.border.get();
|
let c = self.state.theme.colors.border.get();
|
||||||
|
|
@ -408,9 +413,12 @@ impl Renderer<'_> {
|
||||||
let th = theme.sizes.title_height.get();
|
let th = theme.sizes.title_height.get();
|
||||||
let bw = theme.sizes.border_width.get();
|
let bw = theme.sizes.border_width.get();
|
||||||
let bc = theme.colors.border.get();
|
let bc = theme.colors.border.get();
|
||||||
let tc = match floating.active.get() {
|
let tc = if floating.active.get() {
|
||||||
true => theme.colors.focused_title_background.get(),
|
theme.colors.focused_title_background.get()
|
||||||
false => theme.colors.unfocused_title_background.get(),
|
} else if floating.attention_requested.get() {
|
||||||
|
theme.colors.attention_requested_background.get()
|
||||||
|
} else {
|
||||||
|
theme.colors.unfocused_title_background.get()
|
||||||
};
|
};
|
||||||
let uc = theme.colors.separator.get();
|
let uc = theme.colors.separator.get();
|
||||||
let borders = [
|
let borders = [
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,10 @@ use {
|
||||||
NodeVisitorBase, OutputNode, PlaceholderNode, ToplevelNode, WorkspaceNode,
|
NodeVisitorBase, OutputNode, PlaceholderNode, ToplevelNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
activation_token::ActivationToken, asyncevent::AsyncEvent, clonecell::CloneCell,
|
||||||
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell,
|
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, fdcloser::FdCloser,
|
||||||
queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
||||||
|
run_toplevel::RunToplevel,
|
||||||
},
|
},
|
||||||
video::drm::Drm,
|
video::drm::Drm,
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
|
|
@ -134,6 +135,7 @@ pub struct State {
|
||||||
pub workspace_watchers: CopyHashMap<(ClientId, JayWorkspaceWatcherId), Rc<JayWorkspaceWatcher>>,
|
pub workspace_watchers: CopyHashMap<(ClientId, JayWorkspaceWatcherId), Rc<JayWorkspaceWatcher>>,
|
||||||
pub default_workspace_capture: Cell<bool>,
|
pub default_workspace_capture: Cell<bool>,
|
||||||
pub default_gfx_api: Cell<GfxApi>,
|
pub default_gfx_api: Cell<GfxApi>,
|
||||||
|
pub activation_tokens: CopyHashMap<ActivationToken, ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ impl ConnectorHandler {
|
||||||
active_workspace: None,
|
active_workspace: None,
|
||||||
underline: Default::default(),
|
underline: Default::default(),
|
||||||
inactive_workspaces: Default::default(),
|
inactive_workspaces: Default::default(),
|
||||||
|
attention_requested_workspaces: Default::default(),
|
||||||
captured_inactive_workspaces: Default::default(),
|
captured_inactive_workspaces: Default::default(),
|
||||||
titles: Default::default(),
|
titles: Default::default(),
|
||||||
status: None,
|
status: None,
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ colors! {
|
||||||
border = (0x3f, 0x47, 0x4a),
|
border = (0x3f, 0x47, 0x4a),
|
||||||
bar_background = (0x00, 0x00, 0x00),
|
bar_background = (0x00, 0x00, 0x00),
|
||||||
bar_text = (0xff, 0xff, 0xff),
|
bar_text = (0xff, 0xff, 0xff),
|
||||||
|
attention_requested_background = (0x23, 0x09, 0x2c),
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! sizes {
|
macro_rules! sizes {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ use {
|
||||||
rc_eq::rc_eq,
|
rc_eq::rc_eq,
|
||||||
scroller::Scroller,
|
scroller::Scroller,
|
||||||
smallmap::{SmallMap, SmallMapMut},
|
smallmap::{SmallMap, SmallMapMut},
|
||||||
|
threshold_counter::ThresholdCounter,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -83,6 +84,7 @@ pub struct ContainerTitle {
|
||||||
pub struct ContainerRenderData {
|
pub struct ContainerRenderData {
|
||||||
pub title_rects: Vec<Rect>,
|
pub title_rects: Vec<Rect>,
|
||||||
pub active_title_rects: Vec<Rect>,
|
pub active_title_rects: Vec<Rect>,
|
||||||
|
pub attention_title_rects: Vec<Rect>,
|
||||||
pub last_active_rect: Option<Rect>,
|
pub last_active_rect: Option<Rect>,
|
||||||
pub border_rects: Vec<Rect>,
|
pub border_rects: Vec<Rect>,
|
||||||
pub underline_rects: Vec<Rect>,
|
pub underline_rects: Vec<Rect>,
|
||||||
|
|
@ -115,6 +117,7 @@ pub struct ContainerNode {
|
||||||
pub render_data: RefCell<ContainerRenderData>,
|
pub render_data: RefCell<ContainerRenderData>,
|
||||||
scroller: Scroller,
|
scroller: Scroller,
|
||||||
toplevel_data: ToplevelData,
|
toplevel_data: ToplevelData,
|
||||||
|
attention_requests: ThresholdCounter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for ContainerNode {
|
impl Debug for ContainerNode {
|
||||||
|
|
@ -126,6 +129,7 @@ impl Debug for ContainerNode {
|
||||||
pub struct ContainerChild {
|
pub struct ContainerChild {
|
||||||
pub node: Rc<dyn ToplevelNode>,
|
pub node: Rc<dyn ToplevelNode>,
|
||||||
pub active: Cell<bool>,
|
pub active: Cell<bool>,
|
||||||
|
pub attention_requested: Cell<bool>,
|
||||||
title: RefCell<String>,
|
title: RefCell<String>,
|
||||||
pub title_tex: SmallMap<Scale, TextTexture, 2>,
|
pub title_tex: SmallMap<Scale, TextTexture, 2>,
|
||||||
pub title_rect: Cell<Rect>,
|
pub title_rect: Cell<Rect>,
|
||||||
|
|
@ -172,21 +176,21 @@ impl ContainerNode {
|
||||||
) -> Rc<Self> {
|
) -> Rc<Self> {
|
||||||
child.clone().tl_set_workspace(workspace);
|
child.clone().tl_set_workspace(workspace);
|
||||||
let children = LinkedList::new();
|
let children = LinkedList::new();
|
||||||
|
let child_node = children.add_last(ContainerChild {
|
||||||
|
node: child.clone(),
|
||||||
|
active: Default::default(),
|
||||||
|
body: Default::default(),
|
||||||
|
content: Default::default(),
|
||||||
|
factor: Cell::new(1.0),
|
||||||
|
title: Default::default(),
|
||||||
|
title_tex: Default::default(),
|
||||||
|
title_rect: Default::default(),
|
||||||
|
focus_history: Default::default(),
|
||||||
|
attention_requested: Cell::new(false),
|
||||||
|
});
|
||||||
|
let child_node_ref = child_node.clone();
|
||||||
let mut child_nodes = AHashMap::new();
|
let mut child_nodes = AHashMap::new();
|
||||||
child_nodes.insert(
|
child_nodes.insert(child.node_id(), child_node);
|
||||||
child.node_id(),
|
|
||||||
children.add_last(ContainerChild {
|
|
||||||
node: child.clone(),
|
|
||||||
active: Default::default(),
|
|
||||||
body: Default::default(),
|
|
||||||
content: Default::default(),
|
|
||||||
factor: Cell::new(1.0),
|
|
||||||
title: Default::default(),
|
|
||||||
title_tex: Default::default(),
|
|
||||||
title_rect: Default::default(),
|
|
||||||
focus_history: Default::default(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let slf = Rc::new(Self {
|
let slf = Rc::new(Self {
|
||||||
id: state.node_ids.next(),
|
id: state.node_ids.next(),
|
||||||
parent: CloneCell::new(parent.clone()),
|
parent: CloneCell::new(parent.clone()),
|
||||||
|
|
@ -213,9 +217,11 @@ impl ContainerNode {
|
||||||
render_data: Default::default(),
|
render_data: Default::default(),
|
||||||
scroller: Default::default(),
|
scroller: Default::default(),
|
||||||
toplevel_data: ToplevelData::new(state, Default::default(), None),
|
toplevel_data: ToplevelData::new(state, Default::default(), None),
|
||||||
|
attention_requests: Default::default(),
|
||||||
});
|
});
|
||||||
slf.tl_set_parent(parent);
|
slf.tl_set_parent(parent);
|
||||||
child.tl_set_parent(slf.clone());
|
child.tl_set_parent(slf.clone());
|
||||||
|
slf.apply_child_flags(&child_node_ref);
|
||||||
slf
|
slf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,11 +299,13 @@ impl ContainerNode {
|
||||||
title_tex: Default::default(),
|
title_tex: Default::default(),
|
||||||
title_rect: Default::default(),
|
title_rect: Default::default(),
|
||||||
focus_history: Default::default(),
|
focus_history: Default::default(),
|
||||||
|
attention_requested: Default::default(),
|
||||||
});
|
});
|
||||||
let r = link.to_ref();
|
let r = link.to_ref();
|
||||||
links.insert(new.node_id(), link);
|
links.insert(new.node_id(), link);
|
||||||
r
|
r
|
||||||
};
|
};
|
||||||
|
self.apply_child_flags(&new_ref);
|
||||||
new.clone().tl_set_workspace(&self.workspace.get());
|
new.clone().tl_set_workspace(&self.workspace.get());
|
||||||
new.tl_set_parent(self.clone());
|
new.tl_set_parent(self.clone());
|
||||||
new.tl_set_visible(self.toplevel_data.visible.get());
|
new.tl_set_visible(self.toplevel_data.visible.get());
|
||||||
|
|
@ -644,6 +652,7 @@ impl ContainerNode {
|
||||||
}
|
}
|
||||||
rd.title_rects.clear();
|
rd.title_rects.clear();
|
||||||
rd.active_title_rects.clear();
|
rd.active_title_rects.clear();
|
||||||
|
rd.attention_title_rects.clear();
|
||||||
rd.border_rects.clear();
|
rd.border_rects.clear();
|
||||||
rd.underline_rects.clear();
|
rd.underline_rects.clear();
|
||||||
rd.last_active_rect.take();
|
rd.last_active_rect.take();
|
||||||
|
|
@ -667,6 +676,9 @@ impl ContainerNode {
|
||||||
let color = if child.active.get() {
|
let color = if child.active.get() {
|
||||||
rd.active_title_rects.push(rect);
|
rd.active_title_rects.push(rect);
|
||||||
theme.colors.focused_title_text.get()
|
theme.colors.focused_title_text.get()
|
||||||
|
} else if child.attention_requested.get() {
|
||||||
|
rd.attention_title_rects.push(rect);
|
||||||
|
theme.colors.unfocused_title_text.get()
|
||||||
} else if !have_active && last_active == Some(child.node.node_id()) {
|
} else if !have_active && last_active == Some(child.node.node_id()) {
|
||||||
rd.last_active_rect = Some(rect);
|
rd.last_active_rect = Some(rect);
|
||||||
theme.colors.focused_inactive_title_text.get()
|
theme.colors.focused_inactive_title_text.get()
|
||||||
|
|
@ -760,7 +772,7 @@ impl ContainerNode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let child = {
|
let child = {
|
||||||
let children = self.child_nodes.borrow_mut();
|
let children = self.child_nodes.borrow();
|
||||||
match child {
|
match child {
|
||||||
Some(c) => match children.get(&c.node_id()) {
|
Some(c) => match children.get(&c.node_id()) {
|
||||||
Some(c) => Some(c.to_ref()),
|
Some(c) => Some(c.to_ref()),
|
||||||
|
|
@ -815,7 +827,7 @@ impl ContainerNode {
|
||||||
child: &dyn ToplevelNode,
|
child: &dyn ToplevelNode,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
) {
|
) {
|
||||||
let child = match self.child_nodes.borrow_mut().get(&child.node_id()) {
|
let child = match self.child_nodes.borrow().get(&child.node_id()) {
|
||||||
Some(c) => c.to_ref(),
|
Some(c) => c.to_ref(),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
@ -880,7 +892,7 @@ impl ContainerNode {
|
||||||
if split == self.split.get()
|
if split == self.split.get()
|
||||||
|| (split == ContainerSplit::Horizontal && self.mono_child.get().is_some())
|
|| (split == ContainerSplit::Horizontal && self.mono_child.get().is_some())
|
||||||
{
|
{
|
||||||
let cc = match self.child_nodes.borrow_mut().get(&child.node_id()) {
|
let cc = match self.child_nodes.borrow().get(&child.node_id()) {
|
||||||
Some(l) => l.to_ref(),
|
Some(l) => l.to_ref(),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
@ -934,6 +946,33 @@ impl ContainerNode {
|
||||||
self.prepend_child(node);
|
self.prepend_child(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_child_flags(&self, child: &ContainerChild) {
|
||||||
|
let data = child.node.tl_data();
|
||||||
|
let attention_requested = data.wants_attention.get();
|
||||||
|
child.attention_requested.set(attention_requested);
|
||||||
|
if attention_requested {
|
||||||
|
self.mod_attention_requests(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard_child_flags(&self, child: &ContainerChild) {
|
||||||
|
if child.attention_requested.get() {
|
||||||
|
self.mod_attention_requests(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mod_attention_requests(&self, set: bool) {
|
||||||
|
let propagate = self.attention_requests.adj(set);
|
||||||
|
if set || propagate {
|
||||||
|
self.toplevel_data.wants_attention.set(set);
|
||||||
|
}
|
||||||
|
if propagate {
|
||||||
|
self.parent
|
||||||
|
.get()
|
||||||
|
.cnode_child_attention_request_changed(self, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SeatOp {
|
struct SeatOp {
|
||||||
|
|
@ -999,7 +1038,7 @@ impl Node for ContainerNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_child_title_changed(self: Rc<Self>, child: &dyn Node, title: &str) {
|
fn node_child_title_changed(self: Rc<Self>, child: &dyn Node, title: &str) {
|
||||||
let child = match self.child_nodes.borrow_mut().get(&child.node_id()) {
|
let child = match self.child_nodes.borrow().get(&child.node_id()) {
|
||||||
Some(cn) => cn.to_ref(),
|
Some(cn) => cn.to_ref(),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
@ -1083,7 +1122,7 @@ impl Node for ContainerNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_child_active_changed(self: Rc<Self>, child: &dyn Node, active: bool, depth: u32) {
|
fn node_child_active_changed(self: Rc<Self>, child: &dyn Node, active: bool, depth: u32) {
|
||||||
let node = match self.child_nodes.borrow_mut().get(&child.node_id()) {
|
let node = match self.child_nodes.borrow().get(&child.node_id()) {
|
||||||
Some(l) => l.to_ref(),
|
Some(l) => l.to_ref(),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
@ -1276,6 +1315,7 @@ impl ContainingNode for ContainerNode {
|
||||||
None => (false, false),
|
None => (false, false),
|
||||||
Some(mc) => (true, mc.node.node_id() == old.node_id()),
|
Some(mc) => (true, mc.node.node_id() == old.node_id()),
|
||||||
};
|
};
|
||||||
|
self.discard_child_flags(&node);
|
||||||
let link = node.append(ContainerChild {
|
let link = node.append(ContainerChild {
|
||||||
node: new.clone(),
|
node: new.clone(),
|
||||||
active: Cell::new(false),
|
active: Cell::new(false),
|
||||||
|
|
@ -1286,7 +1326,9 @@ impl ContainingNode for ContainerNode {
|
||||||
title_tex: Default::default(),
|
title_tex: Default::default(),
|
||||||
title_rect: Cell::new(node.title_rect.get()),
|
title_rect: Cell::new(node.title_rect.get()),
|
||||||
focus_history: Cell::new(None),
|
focus_history: Cell::new(None),
|
||||||
|
attention_requested: Cell::new(false),
|
||||||
});
|
});
|
||||||
|
self.apply_child_flags(&link);
|
||||||
if let Some(fh) = node.focus_history.take() {
|
if let Some(fh) = node.focus_history.take() {
|
||||||
link.focus_history.set(Some(fh.append(link.to_ref())));
|
link.focus_history.set(Some(fh.append(link.to_ref())));
|
||||||
}
|
}
|
||||||
|
|
@ -1314,6 +1356,7 @@ impl ContainingNode for ContainerNode {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
node.focus_history.set(None);
|
node.focus_history.set(None);
|
||||||
|
self.discard_child_flags(&node);
|
||||||
if let Some(mono) = self.mono_child.get() {
|
if let Some(mono) = self.mono_child.get() {
|
||||||
if mono.node.node_id() == child.node_id() {
|
if mono.node.node_id() == child.node_id() {
|
||||||
let mut new = self.focus_history.last().map(|n| n.deref().clone());
|
let mut new = self.focus_history.last().map(|n| n.deref().clone());
|
||||||
|
|
@ -1364,6 +1407,19 @@ impl ContainingNode for ContainerNode {
|
||||||
fn cnode_accepts_child(&self, _node: &dyn Node) -> bool {
|
fn cnode_accepts_child(&self, _node: &dyn Node) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cnode_child_attention_request_changed(self: Rc<Self>, child: &dyn Node, set: bool) {
|
||||||
|
let children = self.child_nodes.borrow();
|
||||||
|
let child = match children.get(&child.node_id()) {
|
||||||
|
Some(c) => c,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if child.attention_requested.replace(set) == set {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.mod_attention_requests(set);
|
||||||
|
self.schedule_compute_render_data();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToplevelNode for ContainerNode {
|
impl ToplevelNode for ContainerNode {
|
||||||
|
|
@ -1429,9 +1485,12 @@ impl ToplevelNode for ContainerNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_set_visible(&self, visible: bool) {
|
fn tl_set_visible(&self, visible: bool) {
|
||||||
self.toplevel_data.visible.set(visible);
|
if let Some(mc) = self.mono_child.get() {
|
||||||
for child in self.children.iter() {
|
mc.node.tl_set_visible(visible);
|
||||||
child.node.tl_set_visible(visible);
|
} else {
|
||||||
|
for child in self.children.iter() {
|
||||||
|
child.node.tl_set_visible(visible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.toplevel_data.set_visible(self, visible);
|
self.toplevel_data.set_visible(self, visible);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,5 @@ pub trait ContainingNode: Node {
|
||||||
}
|
}
|
||||||
fn cnode_remove_child2(self: Rc<Self>, child: &dyn Node, preserve_focus: bool);
|
fn cnode_remove_child2(self: Rc<Self>, child: &dyn Node, preserve_focus: bool);
|
||||||
fn cnode_accepts_child(&self, node: &dyn Node) -> bool;
|
fn cnode_accepts_child(&self, node: &dyn Node) -> bool;
|
||||||
|
fn cnode_child_attention_request_changed(self: Rc<Self>, child: &dyn Node, set: bool);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ pub struct FloatNode {
|
||||||
pub title: RefCell<String>,
|
pub title: RefCell<String>,
|
||||||
pub title_textures: CopyHashMap<Scale, TextTexture>,
|
pub title_textures: CopyHashMap<Scale, TextTexture>,
|
||||||
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
||||||
|
pub attention_requested: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SeatState {
|
struct SeatState {
|
||||||
|
|
@ -112,7 +113,9 @@ impl FloatNode {
|
||||||
title: Default::default(),
|
title: Default::default(),
|
||||||
title_textures: Default::default(),
|
title_textures: Default::default(),
|
||||||
seats: Default::default(),
|
seats: Default::default(),
|
||||||
|
attention_requested: Cell::new(false),
|
||||||
});
|
});
|
||||||
|
floater.apply_child_flags();
|
||||||
floater
|
floater
|
||||||
.display_link
|
.display_link
|
||||||
.set(Some(state.root.stacked.add_last(floater.clone())));
|
.set(Some(state.root.stacked.add_last(floater.clone())));
|
||||||
|
|
@ -345,6 +348,29 @@ impl FloatNode {
|
||||||
self.workspace.set(ws.clone());
|
self.workspace.set(ws.clone());
|
||||||
self.stacked_set_visible(ws.stacked_visible());
|
self.stacked_set_visible(ws.stacked_visible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_child_flags(&self) {
|
||||||
|
let child = match self.child.get() {
|
||||||
|
None => return,
|
||||||
|
Some(c) => c,
|
||||||
|
};
|
||||||
|
let data = child.tl_data();
|
||||||
|
let activation_requested = data.wants_attention.get();
|
||||||
|
self.attention_requested.set(activation_requested);
|
||||||
|
if activation_requested {
|
||||||
|
self.workspace
|
||||||
|
.get()
|
||||||
|
.cnode_child_attention_request_changed(self, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard_child_flags(&self) {
|
||||||
|
if self.attention_requested.get() {
|
||||||
|
self.workspace
|
||||||
|
.get()
|
||||||
|
.cnode_child_attention_request_changed(self, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for FloatNode {
|
impl Debug for FloatNode {
|
||||||
|
|
@ -527,13 +553,16 @@ impl ContainingNode for FloatNode {
|
||||||
containing_node_impl!();
|
containing_node_impl!();
|
||||||
|
|
||||||
fn cnode_replace_child(self: Rc<Self>, _old: &dyn Node, new: Rc<dyn ToplevelNode>) {
|
fn cnode_replace_child(self: Rc<Self>, _old: &dyn Node, new: Rc<dyn ToplevelNode>) {
|
||||||
|
self.discard_child_flags();
|
||||||
self.child.set(Some(new.clone()));
|
self.child.set(Some(new.clone()));
|
||||||
|
self.apply_child_flags();
|
||||||
new.tl_set_parent(self.clone());
|
new.tl_set_parent(self.clone());
|
||||||
new.clone().tl_set_workspace(&self.workspace.get());
|
new.clone().tl_set_workspace(&self.workspace.get());
|
||||||
self.schedule_layout();
|
self.schedule_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cnode_remove_child2(self: Rc<Self>, _child: &dyn Node, _preserve_focus: bool) {
|
fn cnode_remove_child2(self: Rc<Self>, _child: &dyn Node, _preserve_focus: bool) {
|
||||||
|
self.discard_child_flags();
|
||||||
self.child.set(None);
|
self.child.set(None);
|
||||||
self.display_link.set(None);
|
self.display_link.set(None);
|
||||||
self.workspace_link.set(None);
|
self.workspace_link.set(None);
|
||||||
|
|
@ -542,6 +571,14 @@ impl ContainingNode for FloatNode {
|
||||||
fn cnode_accepts_child(&self, _node: &dyn Node) -> bool {
|
fn cnode_accepts_child(&self, _node: &dyn Node) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cnode_child_attention_request_changed(self: Rc<Self>, _node: &dyn Node, set: bool) {
|
||||||
|
if self.attention_requested.replace(set) != set {
|
||||||
|
self.workspace
|
||||||
|
.get()
|
||||||
|
.cnode_child_attention_request_changed(&*self, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackedNode for FloatNode {
|
impl StackedNode for FloatNode {
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,7 @@ impl OutputNode {
|
||||||
let mut rd = self.render_data.borrow_mut();
|
let mut rd = self.render_data.borrow_mut();
|
||||||
rd.titles.clear();
|
rd.titles.clear();
|
||||||
rd.inactive_workspaces.clear();
|
rd.inactive_workspaces.clear();
|
||||||
|
rd.attention_requested_workspaces.clear();
|
||||||
rd.captured_inactive_workspaces.clear();
|
rd.captured_inactive_workspaces.clear();
|
||||||
rd.active_workspace = None;
|
rd.active_workspace = None;
|
||||||
rd.status = None;
|
rd.status = None;
|
||||||
|
|
@ -219,10 +220,15 @@ impl OutputNode {
|
||||||
rect,
|
rect,
|
||||||
captured: ws.capture.get(),
|
captured: ws.capture.get(),
|
||||||
});
|
});
|
||||||
} else if ws.capture.get() {
|
|
||||||
rd.captured_inactive_workspaces.push(rect);
|
|
||||||
} else {
|
} else {
|
||||||
rd.inactive_workspaces.push(rect);
|
if ws.attention_requests.active() {
|
||||||
|
rd.attention_requested_workspaces.push(rect);
|
||||||
|
}
|
||||||
|
if ws.capture.get() {
|
||||||
|
rd.captured_inactive_workspaces.push(rect);
|
||||||
|
} else {
|
||||||
|
rd.inactive_workspaces.push(rect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pos += title_width;
|
pos += title_width;
|
||||||
}
|
}
|
||||||
|
|
@ -330,6 +336,7 @@ impl OutputNode {
|
||||||
jay_workspaces: Default::default(),
|
jay_workspaces: Default::default(),
|
||||||
capture: self.state.default_workspace_capture.clone(),
|
capture: self.state.default_workspace_capture.clone(),
|
||||||
title_texture: Default::default(),
|
title_texture: Default::default(),
|
||||||
|
attention_requests: Default::default(),
|
||||||
});
|
});
|
||||||
ws.output_link
|
ws.output_link
|
||||||
.set(Some(self.workspaces.add_last(ws.clone())));
|
.set(Some(self.workspaces.add_last(ws.clone())));
|
||||||
|
|
@ -493,6 +500,7 @@ pub struct OutputRenderData {
|
||||||
pub active_workspace: Option<OutputWorkspaceRenderData>,
|
pub active_workspace: Option<OutputWorkspaceRenderData>,
|
||||||
pub underline: Rect,
|
pub underline: Rect,
|
||||||
pub inactive_workspaces: Vec<Rect>,
|
pub inactive_workspaces: Vec<Rect>,
|
||||||
|
pub attention_requested_workspaces: Vec<Rect>,
|
||||||
pub captured_inactive_workspaces: Vec<Rect>,
|
pub captured_inactive_workspaces: Vec<Rect>,
|
||||||
pub titles: Vec<OutputTitle>,
|
pub titles: Vec<OutputTitle>,
|
||||||
pub status: Option<OutputStatus>,
|
pub status: Option<OutputStatus>,
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ impl ToplevelNode for PlaceholderNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_set_visible(&self, visible: bool) {
|
fn tl_set_visible(&self, visible: bool) {
|
||||||
self.toplevel.visible.set(visible);
|
self.toplevel.set_visible(self, visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tl_destroy(&self) {
|
fn tl_destroy(&self) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use {
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
state::State,
|
state::State,
|
||||||
tree::{ContainingNode, Direction, Node, OutputNode, PlaceholderNode, WorkspaceNode},
|
tree::{ContainingNode, Direction, Node, OutputNode, PlaceholderNode, WorkspaceNode},
|
||||||
utils::{clonecell::CloneCell, numcell::NumCell, smallmap::SmallMap},
|
utils::{clonecell::CloneCell, smallmap::SmallMap, threshold_counter::ThresholdCounter},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
|
@ -42,14 +42,14 @@ pub trait ToplevelNode: Node {
|
||||||
fn tl_surface_active_changed(&self, active: bool) {
|
fn tl_surface_active_changed(&self, active: bool) {
|
||||||
let data = self.tl_data();
|
let data = self.tl_data();
|
||||||
if active {
|
if active {
|
||||||
if data.active_surfaces.fetch_add(1) == 0 {
|
if data.active_surfaces.inc() {
|
||||||
self.tl_set_active(true);
|
self.tl_set_active(true);
|
||||||
if let Some(parent) = data.parent.get() {
|
if let Some(parent) = data.parent.get() {
|
||||||
parent.node_child_active_changed(self.tl_as_node(), true, 1);
|
parent.node_child_active_changed(self.tl_as_node(), true, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if data.active_surfaces.fetch_sub(1) == 1 {
|
if data.active_surfaces.dec() {
|
||||||
self.tl_set_active(false);
|
self.tl_set_active(false);
|
||||||
if let Some(parent) = data.parent.get() {
|
if let Some(parent) = data.parent.get() {
|
||||||
parent.node_child_active_changed(self.tl_as_node(), false, 1);
|
parent.node_child_active_changed(self.tl_as_node(), false, 1);
|
||||||
|
|
@ -109,7 +109,7 @@ pub trait ToplevelNode: Node {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let node = self.tl_as_node();
|
let node = self.tl_as_node();
|
||||||
if data.active.get() || data.active_surfaces.get() > 0 {
|
if data.active.get() || data.active_surfaces.active() {
|
||||||
parent.clone().node_child_active_changed(node, true, 1);
|
parent.clone().node_child_active_changed(node, true, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +161,7 @@ pub struct ToplevelData {
|
||||||
pub active: Cell<bool>,
|
pub active: Cell<bool>,
|
||||||
pub client: Option<Rc<Client>>,
|
pub client: Option<Rc<Client>>,
|
||||||
pub state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
pub active_surfaces: NumCell<u32>,
|
pub active_surfaces: ThresholdCounter,
|
||||||
pub focus_node: SmallMap<SeatId, Rc<dyn Node>, 1>,
|
pub focus_node: SmallMap<SeatId, Rc<dyn Node>, 1>,
|
||||||
pub visible: Cell<bool>,
|
pub visible: Cell<bool>,
|
||||||
pub is_floating: Cell<bool>,
|
pub is_floating: Cell<bool>,
|
||||||
|
|
@ -174,6 +174,8 @@ pub struct ToplevelData {
|
||||||
pub parent: CloneCell<Option<Rc<dyn ContainingNode>>>,
|
pub parent: CloneCell<Option<Rc<dyn ContainingNode>>>,
|
||||||
pub pos: Cell<Rect>,
|
pub pos: Cell<Rect>,
|
||||||
pub seat_state: NodeSeatState,
|
pub seat_state: NodeSeatState,
|
||||||
|
pub wants_attention: Cell<bool>,
|
||||||
|
pub requested_attention: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToplevelData {
|
impl ToplevelData {
|
||||||
|
|
@ -195,6 +197,8 @@ impl ToplevelData {
|
||||||
parent: Default::default(),
|
parent: Default::default(),
|
||||||
pos: Default::default(),
|
pos: Default::default(),
|
||||||
seat_state: Default::default(),
|
seat_state: Default::default(),
|
||||||
|
wants_attention: Cell::new(false),
|
||||||
|
requested_attention: Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,12 +267,10 @@ impl ToplevelData {
|
||||||
let mut kb_foci = Default::default();
|
let mut kb_foci = Default::default();
|
||||||
if ws.visible.get() {
|
if ws.visible.get() {
|
||||||
if let Some(container) = ws.container.get() {
|
if let Some(container) = ws.container.get() {
|
||||||
kb_foci = collect_kb_foci(container.clone());
|
kb_foci = collect_kb_foci(container);
|
||||||
container.tl_set_visible(false);
|
|
||||||
}
|
}
|
||||||
for stacked in ws.stacked.iter() {
|
for stacked in ws.stacked.iter() {
|
||||||
collect_kb_foci2(stacked.deref().clone().stacked_into_node(), &mut kb_foci);
|
collect_kb_foci2(stacked.deref().clone().stacked_into_node(), &mut kb_foci);
|
||||||
stacked.stacked_set_visible(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*data = Some(FullscreenedData {
|
*data = Some(FullscreenedData {
|
||||||
|
|
@ -277,7 +279,7 @@ impl ToplevelData {
|
||||||
});
|
});
|
||||||
drop(data);
|
drop(data);
|
||||||
self.is_fullscreen.set(true);
|
self.is_fullscreen.set(true);
|
||||||
ws.fullscreen.set(Some(node.clone()));
|
ws.set_fullscreen_node(&node);
|
||||||
node.tl_set_parent(ws.clone());
|
node.tl_set_parent(ws.clone());
|
||||||
node.clone().tl_set_workspace(ws);
|
node.clone().tl_set_workspace(ws);
|
||||||
node.clone()
|
node.clone()
|
||||||
|
|
@ -313,11 +315,7 @@ impl ToplevelData {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
fd.workspace.fullscreen.take();
|
fd.workspace.remove_fullscreen_node();
|
||||||
if node.node_visible() {
|
|
||||||
fd.workspace.set_visible(true);
|
|
||||||
fd.workspace.flush_jay_workspaces();
|
|
||||||
}
|
|
||||||
if fd.placeholder.is_destroyed() {
|
if fd.placeholder.is_destroyed() {
|
||||||
state.map_tiled(node);
|
state.map_tiled(node);
|
||||||
return;
|
return;
|
||||||
|
|
@ -339,6 +337,31 @@ impl ToplevelData {
|
||||||
|
|
||||||
pub fn set_visible(&self, node: &dyn Node, visible: bool) {
|
pub fn set_visible(&self, node: &dyn Node, visible: bool) {
|
||||||
self.visible.set(visible);
|
self.visible.set(visible);
|
||||||
self.seat_state.set_visible(node, visible)
|
self.seat_state.set_visible(node, visible);
|
||||||
|
if !visible {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if !self.requested_attention.replace(false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.wants_attention.set(false);
|
||||||
|
if let Some(parent) = self.parent.get() {
|
||||||
|
parent.cnode_child_attention_request_changed(node, false);
|
||||||
|
}
|
||||||
|
self.state.damage();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_attention(&self, node: &dyn Node) {
|
||||||
|
if self.visible.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.requested_attention.replace(true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.wants_attention.set(true);
|
||||||
|
if let Some(parent) = self.parent.get() {
|
||||||
|
parent.cnode_child_attention_request_changed(node, true);
|
||||||
|
}
|
||||||
|
self.state.damage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use {
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
linkedlist::{LinkedList, LinkedNode},
|
linkedlist::{LinkedList, LinkedNode},
|
||||||
|
threshold_counter::ThresholdCounter,
|
||||||
},
|
},
|
||||||
wire::JayWorkspaceId,
|
wire::JayWorkspaceId,
|
||||||
},
|
},
|
||||||
|
|
@ -45,6 +46,7 @@ pub struct WorkspaceNode {
|
||||||
pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc<JayWorkspace>>,
|
pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc<JayWorkspace>>,
|
||||||
pub capture: Cell<bool>,
|
pub capture: Cell<bool>,
|
||||||
pub title_texture: Cell<Option<TextTexture>>,
|
pub title_texture: Cell<Option<TextTexture>>,
|
||||||
|
pub attention_requests: ThresholdCounter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkspaceNode {
|
impl WorkspaceNode {
|
||||||
|
|
@ -74,6 +76,10 @@ impl WorkspaceNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_container(self: &Rc<Self>, container: &Rc<ContainerNode>) {
|
pub fn set_container(self: &Rc<Self>, container: &Rc<ContainerNode>) {
|
||||||
|
if let Some(prev) = self.container.get() {
|
||||||
|
self.discard_child_flags(&*prev);
|
||||||
|
}
|
||||||
|
self.apply_child_flags(&**container);
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
container.clone().tl_change_extents(&pos);
|
container.clone().tl_change_extents(&pos);
|
||||||
container.clone().tl_set_workspace(self);
|
container.clone().tl_set_workspace(self);
|
||||||
|
|
@ -103,6 +109,15 @@ impl WorkspaceNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn plane_set_visible(&self, visible: bool) {
|
||||||
|
if let Some(container) = self.container.get() {
|
||||||
|
container.tl_set_visible(visible);
|
||||||
|
}
|
||||||
|
for stacked in self.stacked.iter() {
|
||||||
|
stacked.stacked_set_visible(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_visible(&self, visible: bool) {
|
pub fn set_visible(&self, visible: bool) {
|
||||||
for jw in self.jay_workspaces.lock().values() {
|
for jw in self.jay_workspaces.lock().values() {
|
||||||
jw.send_visible(visible);
|
jw.send_visible(visible);
|
||||||
|
|
@ -111,15 +126,52 @@ impl WorkspaceNode {
|
||||||
if let Some(fs) = self.fullscreen.get() {
|
if let Some(fs) = self.fullscreen.get() {
|
||||||
fs.tl_set_visible(visible);
|
fs.tl_set_visible(visible);
|
||||||
} else {
|
} else {
|
||||||
if let Some(container) = self.container.get() {
|
self.plane_set_visible(visible);
|
||||||
container.tl_set_visible(visible);
|
|
||||||
}
|
|
||||||
for stacked in self.stacked.iter() {
|
|
||||||
stacked.stacked_set_visible(visible);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.seat_state.set_visible(self, visible);
|
self.seat_state.set_visible(self, visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_fullscreen_node(&self, node: &Rc<dyn ToplevelNode>) {
|
||||||
|
let visible = self.visible.get();
|
||||||
|
let mut plane_was_visible = visible;
|
||||||
|
if let Some(prev) = self.fullscreen.set(Some(node.clone())) {
|
||||||
|
plane_was_visible = false;
|
||||||
|
self.discard_child_flags(&*prev);
|
||||||
|
}
|
||||||
|
self.apply_child_flags(&**node);
|
||||||
|
node.tl_set_visible(visible);
|
||||||
|
if plane_was_visible {
|
||||||
|
self.plane_set_visible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_fullscreen_node(&self) {
|
||||||
|
if let Some(node) = self.fullscreen.take() {
|
||||||
|
self.discard_child_flags(&*node);
|
||||||
|
if self.visible.get() {
|
||||||
|
self.plane_set_visible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_child_flags(&self, child: &dyn ToplevelNode) {
|
||||||
|
if child.tl_data().wants_attention.get() {
|
||||||
|
self.mod_attention_requested(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discard_child_flags(&self, child: &dyn ToplevelNode) {
|
||||||
|
if child.tl_data().wants_attention.get() {
|
||||||
|
self.mod_attention_requested(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mod_attention_requested(&self, set: bool) {
|
||||||
|
let crossed_threshold = self.attention_requests.adj(set);
|
||||||
|
if crossed_threshold {
|
||||||
|
self.output.get().schedule_update_render_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for WorkspaceNode {
|
impl Node for WorkspaceNode {
|
||||||
|
|
@ -224,12 +276,14 @@ impl ContainingNode for WorkspaceNode {
|
||||||
fn cnode_remove_child2(self: Rc<Self>, child: &dyn Node, _preserve_focus: bool) {
|
fn cnode_remove_child2(self: Rc<Self>, child: &dyn Node, _preserve_focus: bool) {
|
||||||
if let Some(container) = self.container.get() {
|
if let Some(container) = self.container.get() {
|
||||||
if container.node_id() == child.node_id() {
|
if container.node_id() == child.node_id() {
|
||||||
|
self.discard_child_flags(&*container);
|
||||||
self.container.set(None);
|
self.container.set(None);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(fs) = self.fullscreen.get() {
|
if let Some(fs) = self.fullscreen.get() {
|
||||||
if fs.tl_as_node().node_id() == child.node_id() {
|
if fs.tl_as_node().node_id() == child.node_id() {
|
||||||
|
self.discard_child_flags(&*fs);
|
||||||
self.fullscreen.set(None);
|
self.fullscreen.set(None);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -240,4 +294,8 @@ impl ContainingNode for WorkspaceNode {
|
||||||
fn cnode_accepts_child(&self, node: &dyn Node) -> bool {
|
fn cnode_accepts_child(&self, node: &dyn Node) -> bool {
|
||||||
node.node_is_container()
|
node.node_is_container()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cnode_child_attention_request_changed(self: Rc<Self>, _node: &dyn Node, set: bool) {
|
||||||
|
self.mod_attention_requested(set);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod activation_token;
|
||||||
pub mod array;
|
pub mod array;
|
||||||
pub mod asyncevent;
|
pub mod asyncevent;
|
||||||
pub mod bitfield;
|
pub mod bitfield;
|
||||||
|
|
@ -18,6 +19,7 @@ pub mod nonblock;
|
||||||
pub mod num_cpus;
|
pub mod num_cpus;
|
||||||
pub mod numcell;
|
pub mod numcell;
|
||||||
pub mod once;
|
pub mod once;
|
||||||
|
pub mod opaque;
|
||||||
pub mod option_ext;
|
pub mod option_ext;
|
||||||
pub mod oserror;
|
pub mod oserror;
|
||||||
pub mod page_size;
|
pub mod page_size;
|
||||||
|
|
@ -30,6 +32,7 @@ pub mod scroller;
|
||||||
pub mod smallmap;
|
pub mod smallmap;
|
||||||
pub mod stack;
|
pub mod stack;
|
||||||
pub mod syncqueue;
|
pub mod syncqueue;
|
||||||
|
pub mod threshold_counter;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod tri;
|
pub mod tri;
|
||||||
pub mod trim;
|
pub mod trim;
|
||||||
|
|
|
||||||
28
src/utils/activation_token.rs
Normal file
28
src/utils/activation_token.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
use {
|
||||||
|
crate::utils::opaque::{opaque, Opaque, OpaqueError},
|
||||||
|
std::{
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
str::FromStr,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||||
|
pub struct ActivationToken(Opaque);
|
||||||
|
|
||||||
|
pub fn activation_token() -> ActivationToken {
|
||||||
|
ActivationToken(opaque())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ActivationToken {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ActivationToken {
|
||||||
|
type Err = OpaqueError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(s.parse()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/utils/opaque.rs
Normal file
66
src/utils/opaque.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
use {
|
||||||
|
rand::{thread_rng, Rng},
|
||||||
|
std::{
|
||||||
|
fmt::{Debug, Display, Formatter},
|
||||||
|
num::ParseIntError,
|
||||||
|
str::FromStr,
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct Opaque {
|
||||||
|
lo: u64,
|
||||||
|
hi: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opaque() -> Opaque {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
Opaque {
|
||||||
|
lo: rng.gen(),
|
||||||
|
hi: rng.gen(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Opaque {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:016x}", self.hi)?;
|
||||||
|
write!(f, "{:016x}", self.lo)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Opaque {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Opaque {
|
||||||
|
type Err = OpaqueError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if s.len() != LEN {
|
||||||
|
return Err(OpaqueError::InvalidLength);
|
||||||
|
}
|
||||||
|
if !s.is_char_boundary(LEN / 2) {
|
||||||
|
return Err(OpaqueError::NotAscii);
|
||||||
|
}
|
||||||
|
let (hi, lo) = s.split_at(LEN / 2);
|
||||||
|
let hi = u64::from_str_radix(hi, 16).map_err(OpaqueError::Parse)?;
|
||||||
|
let lo = u64::from_str_radix(lo, 16).map_err(OpaqueError::Parse)?;
|
||||||
|
Ok(Self { lo, hi })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LEN: usize = 32;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum OpaqueError {
|
||||||
|
#[error("The string is not exactly 32 bytes long")]
|
||||||
|
InvalidLength,
|
||||||
|
#[error("The string is not ascii")]
|
||||||
|
NotAscii,
|
||||||
|
#[error("Could not parse the string as a hex number")]
|
||||||
|
Parse(ParseIntError),
|
||||||
|
}
|
||||||
28
src/utils/threshold_counter.rs
Normal file
28
src/utils/threshold_counter.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
use crate::utils::numcell::NumCell;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ThresholdCounter {
|
||||||
|
counter: NumCell<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThresholdCounter {
|
||||||
|
pub fn inc(&self) -> bool {
|
||||||
|
self.counter.fetch_add(1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dec(&self) -> bool {
|
||||||
|
self.counter.fetch_sub(1) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adj(&self, inc: bool) -> bool {
|
||||||
|
if inc {
|
||||||
|
self.inc()
|
||||||
|
} else {
|
||||||
|
self.dec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active(&self) -> bool {
|
||||||
|
self.counter.get() > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2234,7 +2234,7 @@ impl Wm {
|
||||||
|
|
||||||
async fn handle_minimize_requested(&self, data: &Rc<XwindowData>) -> bool {
|
async fn handle_minimize_requested(&self, data: &Rc<XwindowData>) -> bool {
|
||||||
if let Some(w) = data.window.get() {
|
if let Some(w) = data.window.get() {
|
||||||
if w.toplevel_data.active_surfaces.get() > 0 {
|
if w.toplevel_data.active_surfaces.active() {
|
||||||
self.set_wm_state(data, ICCCM_WM_STATE_NORMAL).await;
|
self.set_wm_state(data, ICCCM_WM_STATE_NORMAL).await;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
wire/xdg_activation_token_v1.txt
Normal file
28
wire/xdg_activation_token_v1.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# requests
|
||||||
|
|
||||||
|
msg set_serial = 0 {
|
||||||
|
serial: u32,
|
||||||
|
seat: id(wl_seat),
|
||||||
|
}
|
||||||
|
|
||||||
|
msg set_app_id = 1 {
|
||||||
|
app_id: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
msg set_surface = 2 {
|
||||||
|
surface: id(wl_surface),
|
||||||
|
}
|
||||||
|
|
||||||
|
msg commit = 3 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
msg destroy = 4 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# events
|
||||||
|
|
||||||
|
msg done = 0 {
|
||||||
|
token: str,
|
||||||
|
}
|
||||||
14
wire/xdg_activation_v1.txt
Normal file
14
wire/xdg_activation_v1.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# requests
|
||||||
|
|
||||||
|
msg destroy = 0 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
msg get_activation_token = 1 {
|
||||||
|
id: id(xdg_activation_token_v1),
|
||||||
|
}
|
||||||
|
|
||||||
|
msg activate = 2 {
|
||||||
|
token: str,
|
||||||
|
surface: id(wl_surface),
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue