wayland: implement xdg-activation
This commit is contained in:
parent
0628a9d393
commit
41d7531cd5
26 changed files with 667 additions and 50 deletions
|
|
@ -7,6 +7,7 @@ use {
|
|||
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
||||
state::State,
|
||||
utils::{
|
||||
activation_token::ActivationToken,
|
||||
asyncevent::AsyncEvent,
|
||||
buffd::{MsgFormatter, MsgParser, MsgParserError, OutBufferSwapchain},
|
||||
copyhashmap::{CopyHashMap, Locked},
|
||||
|
|
@ -147,6 +148,7 @@ impl Clients {
|
|||
symmetric_delete: Cell::new(false),
|
||||
last_xwayland_serial: Cell::new(0),
|
||||
surfaces_by_xwayland_serial: Default::default(),
|
||||
activation_tokens: Default::default(),
|
||||
});
|
||||
track!(data, data);
|
||||
let display = Rc::new(WlDisplay::new(&data));
|
||||
|
|
@ -217,6 +219,7 @@ impl Drop for ClientHolder {
|
|||
self.data.flush_request.clear();
|
||||
self.data.shutdown.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 last_xwayland_serial: Cell<u64>,
|
||||
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
||||
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ fn start_compositor2(
|
|||
workspace_watchers: Default::default(),
|
||||
default_workspace_capture: Cell::new(true),
|
||||
default_gfx_api: Cell::new(GfxApi::OpenGl),
|
||||
activation_tokens: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -413,6 +414,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
jay_workspaces: Default::default(),
|
||||
capture: Cell::new(false),
|
||||
title_texture: Cell::new(None),
|
||||
attention_requests: Default::default(),
|
||||
});
|
||||
dummy_workspace.output_link.set(Some(
|
||||
dummy_output.workspaces.add_last(dummy_workspace.clone()),
|
||||
|
|
|
|||
|
|
@ -1066,6 +1066,7 @@ impl ConfigProxyHandler {
|
|||
FOCUSED_TITLE_TEXT_COLOR => &colors.focused_title_text,
|
||||
FOCUSED_INACTIVE_TITLE_TEXT_COLOR => &colors.focused_inactive_title_text,
|
||||
BAR_STATUS_TEXT_COLOR => &colors.bar_text,
|
||||
ATTENTION_REQUESTED_BACKGROUND_COLOR => &colors.attention_requested_background,
|
||||
_ => return Err(CphError::UnknownColor(colorable.0)),
|
||||
};
|
||||
Ok(colorable)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use {
|
|||
wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1Global,
|
||||
wp_tearing_control_manager_v1::WpTearingControlManagerV1Global,
|
||||
wp_viewporter::WpViewporterGlobal,
|
||||
xdg_activation_v1::XdgActivationV1Global,
|
||||
xdg_wm_base::XdgWmBaseGlobal,
|
||||
zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
|
||||
zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global,
|
||||
|
|
@ -162,6 +163,7 @@ impl Globals {
|
|||
add_singleton!(WpSinglePixelBufferManagerV1Global);
|
||||
add_singleton!(WpCursorShapeManagerV1Global);
|
||||
add_singleton!(WpContentTypeManagerV1Global);
|
||||
add_singleton!(XdgActivationV1Global);
|
||||
}
|
||||
|
||||
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_tearing_control_manager_v1;
|
||||
pub mod wp_viewporter;
|
||||
pub mod xdg_activation_token_v1;
|
||||
pub mod xdg_activation_v1;
|
||||
pub mod xdg_positioner;
|
||||
pub mod xdg_wm_base;
|
||||
pub mod zwlr_layer_shell_v1;
|
||||
|
|
|
|||
|
|
@ -1059,6 +1059,12 @@ impl WlSurface {
|
|||
pub fn set_content_type(&self, content_type: Option<ContentType>) {
|
||||
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! {
|
||||
|
|
|
|||
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
|
||||
.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();
|
||||
for title in &rd.titles {
|
||||
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);
|
||||
let c = self.state.theme.colors.focused_title_background.get();
|
||||
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();
|
||||
self.base.fill_boxes2(&rd.underline_rects, &c, x, y);
|
||||
let c = self.state.theme.colors.border.get();
|
||||
|
|
@ -408,9 +413,12 @@ impl Renderer<'_> {
|
|||
let th = theme.sizes.title_height.get();
|
||||
let bw = theme.sizes.border_width.get();
|
||||
let bc = theme.colors.border.get();
|
||||
let tc = match floating.active.get() {
|
||||
true => theme.colors.focused_title_background.get(),
|
||||
false => theme.colors.unfocused_title_background.get(),
|
||||
let tc = if floating.active.get() {
|
||||
theme.colors.focused_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 borders = [
|
||||
|
|
|
|||
|
|
@ -42,9 +42,10 @@ use {
|
|||
NodeVisitorBase, OutputNode, PlaceholderNode, ToplevelNode, WorkspaceNode,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell,
|
||||
queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||
activation_token::ActivationToken, asyncevent::AsyncEvent, clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap, errorfmt::ErrorFmt, fdcloser::FdCloser,
|
||||
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
||||
run_toplevel::RunToplevel,
|
||||
},
|
||||
video::drm::Drm,
|
||||
wheel::Wheel,
|
||||
|
|
@ -134,6 +135,7 @@ pub struct State {
|
|||
pub workspace_watchers: CopyHashMap<(ClientId, JayWorkspaceWatcherId), Rc<JayWorkspaceWatcher>>,
|
||||
pub default_workspace_capture: Cell<bool>,
|
||||
pub default_gfx_api: Cell<GfxApi>,
|
||||
pub activation_tokens: CopyHashMap<ActivationToken, ()>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ impl ConnectorHandler {
|
|||
active_workspace: None,
|
||||
underline: Default::default(),
|
||||
inactive_workspaces: Default::default(),
|
||||
attention_requested_workspaces: Default::default(),
|
||||
captured_inactive_workspaces: Default::default(),
|
||||
titles: Default::default(),
|
||||
status: None,
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ colors! {
|
|||
border = (0x3f, 0x47, 0x4a),
|
||||
bar_background = (0x00, 0x00, 0x00),
|
||||
bar_text = (0xff, 0xff, 0xff),
|
||||
attention_requested_background = (0x23, 0x09, 0x2c),
|
||||
}
|
||||
|
||||
macro_rules! sizes {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use {
|
|||
rc_eq::rc_eq,
|
||||
scroller::Scroller,
|
||||
smallmap::{SmallMap, SmallMapMut},
|
||||
threshold_counter::ThresholdCounter,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
|
|
@ -83,6 +84,7 @@ pub struct ContainerTitle {
|
|||
pub struct ContainerRenderData {
|
||||
pub title_rects: Vec<Rect>,
|
||||
pub active_title_rects: Vec<Rect>,
|
||||
pub attention_title_rects: Vec<Rect>,
|
||||
pub last_active_rect: Option<Rect>,
|
||||
pub border_rects: Vec<Rect>,
|
||||
pub underline_rects: Vec<Rect>,
|
||||
|
|
@ -115,6 +117,7 @@ pub struct ContainerNode {
|
|||
pub render_data: RefCell<ContainerRenderData>,
|
||||
scroller: Scroller,
|
||||
toplevel_data: ToplevelData,
|
||||
attention_requests: ThresholdCounter,
|
||||
}
|
||||
|
||||
impl Debug for ContainerNode {
|
||||
|
|
@ -126,6 +129,7 @@ impl Debug for ContainerNode {
|
|||
pub struct ContainerChild {
|
||||
pub node: Rc<dyn ToplevelNode>,
|
||||
pub active: Cell<bool>,
|
||||
pub attention_requested: Cell<bool>,
|
||||
title: RefCell<String>,
|
||||
pub title_tex: SmallMap<Scale, TextTexture, 2>,
|
||||
pub title_rect: Cell<Rect>,
|
||||
|
|
@ -172,21 +176,21 @@ impl ContainerNode {
|
|||
) -> Rc<Self> {
|
||||
child.clone().tl_set_workspace(workspace);
|
||||
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();
|
||||
child_nodes.insert(
|
||||
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(),
|
||||
}),
|
||||
);
|
||||
child_nodes.insert(child.node_id(), child_node);
|
||||
let slf = Rc::new(Self {
|
||||
id: state.node_ids.next(),
|
||||
parent: CloneCell::new(parent.clone()),
|
||||
|
|
@ -213,9 +217,11 @@ impl ContainerNode {
|
|||
render_data: Default::default(),
|
||||
scroller: Default::default(),
|
||||
toplevel_data: ToplevelData::new(state, Default::default(), None),
|
||||
attention_requests: Default::default(),
|
||||
});
|
||||
slf.tl_set_parent(parent);
|
||||
child.tl_set_parent(slf.clone());
|
||||
slf.apply_child_flags(&child_node_ref);
|
||||
slf
|
||||
}
|
||||
|
||||
|
|
@ -293,11 +299,13 @@ impl ContainerNode {
|
|||
title_tex: Default::default(),
|
||||
title_rect: Default::default(),
|
||||
focus_history: Default::default(),
|
||||
attention_requested: Default::default(),
|
||||
});
|
||||
let r = link.to_ref();
|
||||
links.insert(new.node_id(), link);
|
||||
r
|
||||
};
|
||||
self.apply_child_flags(&new_ref);
|
||||
new.clone().tl_set_workspace(&self.workspace.get());
|
||||
new.tl_set_parent(self.clone());
|
||||
new.tl_set_visible(self.toplevel_data.visible.get());
|
||||
|
|
@ -644,6 +652,7 @@ impl ContainerNode {
|
|||
}
|
||||
rd.title_rects.clear();
|
||||
rd.active_title_rects.clear();
|
||||
rd.attention_title_rects.clear();
|
||||
rd.border_rects.clear();
|
||||
rd.underline_rects.clear();
|
||||
rd.last_active_rect.take();
|
||||
|
|
@ -667,6 +676,9 @@ impl ContainerNode {
|
|||
let color = if child.active.get() {
|
||||
rd.active_title_rects.push(rect);
|
||||
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()) {
|
||||
rd.last_active_rect = Some(rect);
|
||||
theme.colors.focused_inactive_title_text.get()
|
||||
|
|
@ -760,7 +772,7 @@ impl ContainerNode {
|
|||
return;
|
||||
}
|
||||
let child = {
|
||||
let children = self.child_nodes.borrow_mut();
|
||||
let children = self.child_nodes.borrow();
|
||||
match child {
|
||||
Some(c) => match children.get(&c.node_id()) {
|
||||
Some(c) => Some(c.to_ref()),
|
||||
|
|
@ -815,7 +827,7 @@ impl ContainerNode {
|
|||
child: &dyn ToplevelNode,
|
||||
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(),
|
||||
_ => return,
|
||||
};
|
||||
|
|
@ -880,7 +892,7 @@ impl ContainerNode {
|
|||
if split == self.split.get()
|
||||
|| (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(),
|
||||
None => return,
|
||||
};
|
||||
|
|
@ -934,6 +946,33 @@ impl ContainerNode {
|
|||
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 {
|
||||
|
|
@ -999,7 +1038,7 @@ impl Node for ContainerNode {
|
|||
}
|
||||
|
||||
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(),
|
||||
_ => return,
|
||||
};
|
||||
|
|
@ -1083,7 +1122,7 @@ impl Node for ContainerNode {
|
|||
}
|
||||
|
||||
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(),
|
||||
None => return,
|
||||
};
|
||||
|
|
@ -1276,6 +1315,7 @@ impl ContainingNode for ContainerNode {
|
|||
None => (false, false),
|
||||
Some(mc) => (true, mc.node.node_id() == old.node_id()),
|
||||
};
|
||||
self.discard_child_flags(&node);
|
||||
let link = node.append(ContainerChild {
|
||||
node: new.clone(),
|
||||
active: Cell::new(false),
|
||||
|
|
@ -1286,7 +1326,9 @@ impl ContainingNode for ContainerNode {
|
|||
title_tex: Default::default(),
|
||||
title_rect: Cell::new(node.title_rect.get()),
|
||||
focus_history: Cell::new(None),
|
||||
attention_requested: Cell::new(false),
|
||||
});
|
||||
self.apply_child_flags(&link);
|
||||
if let Some(fh) = node.focus_history.take() {
|
||||
link.focus_history.set(Some(fh.append(link.to_ref())));
|
||||
}
|
||||
|
|
@ -1314,6 +1356,7 @@ impl ContainingNode for ContainerNode {
|
|||
None => return,
|
||||
};
|
||||
node.focus_history.set(None);
|
||||
self.discard_child_flags(&node);
|
||||
if let Some(mono) = self.mono_child.get() {
|
||||
if mono.node.node_id() == child.node_id() {
|
||||
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 {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -14,4 +14,5 @@ pub trait ContainingNode: Node {
|
|||
}
|
||||
fn cnode_remove_child2(self: Rc<Self>, child: &dyn Node, preserve_focus: 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_textures: CopyHashMap<Scale, TextTexture>,
|
||||
seats: RefCell<AHashMap<SeatId, SeatState>>,
|
||||
pub attention_requested: Cell<bool>,
|
||||
}
|
||||
|
||||
struct SeatState {
|
||||
|
|
@ -112,7 +113,9 @@ impl FloatNode {
|
|||
title: Default::default(),
|
||||
title_textures: Default::default(),
|
||||
seats: Default::default(),
|
||||
attention_requested: Cell::new(false),
|
||||
});
|
||||
floater.apply_child_flags();
|
||||
floater
|
||||
.display_link
|
||||
.set(Some(state.root.stacked.add_last(floater.clone())));
|
||||
|
|
@ -345,6 +348,29 @@ impl FloatNode {
|
|||
self.workspace.set(ws.clone());
|
||||
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 {
|
||||
|
|
@ -527,13 +553,16 @@ impl ContainingNode for FloatNode {
|
|||
containing_node_impl!();
|
||||
|
||||
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.apply_child_flags();
|
||||
new.tl_set_parent(self.clone());
|
||||
new.clone().tl_set_workspace(&self.workspace.get());
|
||||
self.schedule_layout();
|
||||
}
|
||||
|
||||
fn cnode_remove_child2(self: Rc<Self>, _child: &dyn Node, _preserve_focus: bool) {
|
||||
self.discard_child_flags();
|
||||
self.child.set(None);
|
||||
self.display_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 {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ impl OutputNode {
|
|||
let mut rd = self.render_data.borrow_mut();
|
||||
rd.titles.clear();
|
||||
rd.inactive_workspaces.clear();
|
||||
rd.attention_requested_workspaces.clear();
|
||||
rd.captured_inactive_workspaces.clear();
|
||||
rd.active_workspace = None;
|
||||
rd.status = None;
|
||||
|
|
@ -219,10 +220,15 @@ impl OutputNode {
|
|||
rect,
|
||||
captured: ws.capture.get(),
|
||||
});
|
||||
} else if ws.capture.get() {
|
||||
rd.captured_inactive_workspaces.push(rect);
|
||||
} 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;
|
||||
}
|
||||
|
|
@ -330,6 +336,7 @@ impl OutputNode {
|
|||
jay_workspaces: Default::default(),
|
||||
capture: self.state.default_workspace_capture.clone(),
|
||||
title_texture: Default::default(),
|
||||
attention_requests: Default::default(),
|
||||
});
|
||||
ws.output_link
|
||||
.set(Some(self.workspaces.add_last(ws.clone())));
|
||||
|
|
@ -493,6 +500,7 @@ pub struct OutputRenderData {
|
|||
pub active_workspace: Option<OutputWorkspaceRenderData>,
|
||||
pub underline: Rect,
|
||||
pub inactive_workspaces: Vec<Rect>,
|
||||
pub attention_requested_workspaces: Vec<Rect>,
|
||||
pub captured_inactive_workspaces: Vec<Rect>,
|
||||
pub titles: Vec<OutputTitle>,
|
||||
pub status: Option<OutputStatus>,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use {
|
|||
rect::Rect,
|
||||
state::State,
|
||||
tree::{ContainingNode, Direction, Node, OutputNode, PlaceholderNode, WorkspaceNode},
|
||||
utils::{clonecell::CloneCell, numcell::NumCell, smallmap::SmallMap},
|
||||
utils::{clonecell::CloneCell, smallmap::SmallMap, threshold_counter::ThresholdCounter},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -42,14 +42,14 @@ pub trait ToplevelNode: Node {
|
|||
fn tl_surface_active_changed(&self, active: bool) {
|
||||
let data = self.tl_data();
|
||||
if active {
|
||||
if data.active_surfaces.fetch_add(1) == 0 {
|
||||
if data.active_surfaces.inc() {
|
||||
self.tl_set_active(true);
|
||||
if let Some(parent) = data.parent.get() {
|
||||
parent.node_child_active_changed(self.tl_as_node(), true, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if data.active_surfaces.fetch_sub(1) == 1 {
|
||||
if data.active_surfaces.dec() {
|
||||
self.tl_set_active(false);
|
||||
if let Some(parent) = data.parent.get() {
|
||||
parent.node_child_active_changed(self.tl_as_node(), false, 1);
|
||||
|
|
@ -109,7 +109,7 @@ pub trait ToplevelNode: Node {
|
|||
_ => return,
|
||||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ pub struct ToplevelData {
|
|||
pub active: Cell<bool>,
|
||||
pub client: Option<Rc<Client>>,
|
||||
pub state: Rc<State>,
|
||||
pub active_surfaces: NumCell<u32>,
|
||||
pub active_surfaces: ThresholdCounter,
|
||||
pub focus_node: SmallMap<SeatId, Rc<dyn Node>, 1>,
|
||||
pub visible: Cell<bool>,
|
||||
pub is_floating: Cell<bool>,
|
||||
|
|
@ -174,6 +174,8 @@ pub struct ToplevelData {
|
|||
pub parent: CloneCell<Option<Rc<dyn ContainingNode>>>,
|
||||
pub pos: Cell<Rect>,
|
||||
pub seat_state: NodeSeatState,
|
||||
pub wants_attention: Cell<bool>,
|
||||
pub requested_attention: Cell<bool>,
|
||||
}
|
||||
|
||||
impl ToplevelData {
|
||||
|
|
@ -195,6 +197,8 @@ impl ToplevelData {
|
|||
parent: Default::default(),
|
||||
pos: 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();
|
||||
if ws.visible.get() {
|
||||
if let Some(container) = ws.container.get() {
|
||||
kb_foci = collect_kb_foci(container.clone());
|
||||
container.tl_set_visible(false);
|
||||
kb_foci = collect_kb_foci(container);
|
||||
}
|
||||
for stacked in ws.stacked.iter() {
|
||||
collect_kb_foci2(stacked.deref().clone().stacked_into_node(), &mut kb_foci);
|
||||
stacked.stacked_set_visible(false);
|
||||
}
|
||||
}
|
||||
*data = Some(FullscreenedData {
|
||||
|
|
@ -277,7 +279,7 @@ impl ToplevelData {
|
|||
});
|
||||
drop(data);
|
||||
self.is_fullscreen.set(true);
|
||||
ws.fullscreen.set(Some(node.clone()));
|
||||
ws.set_fullscreen_node(&node);
|
||||
node.tl_set_parent(ws.clone());
|
||||
node.clone().tl_set_workspace(ws);
|
||||
node.clone()
|
||||
|
|
@ -313,11 +315,7 @@ impl ToplevelData {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
fd.workspace.fullscreen.take();
|
||||
if node.node_visible() {
|
||||
fd.workspace.set_visible(true);
|
||||
fd.workspace.flush_jay_workspaces();
|
||||
}
|
||||
fd.workspace.remove_fullscreen_node();
|
||||
if fd.placeholder.is_destroyed() {
|
||||
state.map_tiled(node);
|
||||
return;
|
||||
|
|
@ -339,6 +337,31 @@ impl ToplevelData {
|
|||
|
||||
pub fn set_visible(&self, node: &dyn Node, visible: bool) {
|
||||
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,
|
||||
copyhashmap::CopyHashMap,
|
||||
linkedlist::{LinkedList, LinkedNode},
|
||||
threshold_counter::ThresholdCounter,
|
||||
},
|
||||
wire::JayWorkspaceId,
|
||||
},
|
||||
|
|
@ -45,6 +46,7 @@ pub struct WorkspaceNode {
|
|||
pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc<JayWorkspace>>,
|
||||
pub capture: Cell<bool>,
|
||||
pub title_texture: Cell<Option<TextTexture>>,
|
||||
pub attention_requests: ThresholdCounter,
|
||||
}
|
||||
|
||||
impl WorkspaceNode {
|
||||
|
|
@ -74,6 +76,10 @@ impl WorkspaceNode {
|
|||
}
|
||||
|
||||
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();
|
||||
container.clone().tl_change_extents(&pos);
|
||||
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) {
|
||||
for jw in self.jay_workspaces.lock().values() {
|
||||
jw.send_visible(visible);
|
||||
|
|
@ -111,15 +126,52 @@ impl WorkspaceNode {
|
|||
if let Some(fs) = self.fullscreen.get() {
|
||||
fs.tl_set_visible(visible);
|
||||
} else {
|
||||
if let Some(container) = self.container.get() {
|
||||
container.tl_set_visible(visible);
|
||||
}
|
||||
for stacked in self.stacked.iter() {
|
||||
stacked.stacked_set_visible(visible);
|
||||
}
|
||||
self.plane_set_visible(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 {
|
||||
|
|
@ -224,12 +276,14 @@ impl ContainingNode for WorkspaceNode {
|
|||
fn cnode_remove_child2(self: Rc<Self>, child: &dyn Node, _preserve_focus: bool) {
|
||||
if let Some(container) = self.container.get() {
|
||||
if container.node_id() == child.node_id() {
|
||||
self.discard_child_flags(&*container);
|
||||
self.container.set(None);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(fs) = self.fullscreen.get() {
|
||||
if fs.tl_as_node().node_id() == child.node_id() {
|
||||
self.discard_child_flags(&*fs);
|
||||
self.fullscreen.set(None);
|
||||
return;
|
||||
}
|
||||
|
|
@ -240,4 +294,8 @@ impl ContainingNode for WorkspaceNode {
|
|||
fn cnode_accepts_child(&self, node: &dyn Node) -> bool {
|
||||
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 asyncevent;
|
||||
pub mod bitfield;
|
||||
|
|
@ -18,6 +19,7 @@ pub mod nonblock;
|
|||
pub mod num_cpus;
|
||||
pub mod numcell;
|
||||
pub mod once;
|
||||
pub mod opaque;
|
||||
pub mod option_ext;
|
||||
pub mod oserror;
|
||||
pub mod page_size;
|
||||
|
|
@ -30,6 +32,7 @@ pub mod scroller;
|
|||
pub mod smallmap;
|
||||
pub mod stack;
|
||||
pub mod syncqueue;
|
||||
pub mod threshold_counter;
|
||||
pub mod timer;
|
||||
pub mod tri;
|
||||
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 {
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue