Merge pull request #180 from mahkoh/jorth/client-caps
wayland: implement wp-security-manager-v1
This commit is contained in:
commit
4a236b7833
29 changed files with 533 additions and 103 deletions
|
|
@ -32,7 +32,7 @@ keymap = """
|
|||
"""
|
||||
|
||||
# An action that will be executed when the GPU has been initialized.
|
||||
on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } }
|
||||
on-graphics-initialized = { type = "exec", exec = "mako" }
|
||||
|
||||
# Shortcuts that are processed by the compositor.
|
||||
# The left hand side should be a key, possibly prefixed with modifiers.
|
||||
|
|
@ -79,7 +79,7 @@ alt-shift-f = "toggle-floating"
|
|||
# For example, the exec action spawns an application and has the exec field
|
||||
# that describes how to spawn the application.
|
||||
Super_L = { type = "exec", exec = "alacritty" }
|
||||
alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } }
|
||||
alt-p = { type = "exec", exec = "bemenu-run" }
|
||||
|
||||
# The quit action terminates the compositor.
|
||||
alt-q = "quit"
|
||||
|
|
@ -266,7 +266,7 @@ If you want to run an action at startup, you can use the top-level `on-graphics-
|
|||
field:
|
||||
|
||||
```toml
|
||||
on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } }
|
||||
on-graphics-initialized = { type = "exec", exec = "mako" }
|
||||
```
|
||||
|
||||
### Setting Environment Variables
|
||||
|
|
|
|||
|
|
@ -122,48 +122,50 @@ Jay's shortcut system allows you to execute an action when a key is pressed and
|
|||
|
||||
Jay supports the following wayland protocols:
|
||||
|
||||
| Global | Version | Privileged |
|
||||
|-----------------------------------------|:-----------------|------------|
|
||||
| ext_foreign_toplevel_list_v1 | 1 | Yes |
|
||||
| ext_idle_notifier_v1 | 1 | Yes |
|
||||
| ext_session_lock_manager_v1 | 1 | Yes |
|
||||
| org_kde_kwin_server_decoration_manager | 1 | |
|
||||
| wl_compositor | 6[^no_touch] | |
|
||||
| wl_data_device_manager | 3 | |
|
||||
| wl_drm | 2 | |
|
||||
| wl_output | 4 | |
|
||||
| wl_seat | 9 | |
|
||||
| wl_shm | 2 | |
|
||||
| wl_subcompositor | 1 | |
|
||||
| wp_alpha_modifier_v1 | 1 | |
|
||||
| wp_content_type_manager_v1 | 1 | |
|
||||
| wp_cursor_shape_manager_v1 | 1 | |
|
||||
| wp_fractional_scale_manager_v1 | 1 | |
|
||||
| wp_linux_drm_syncobj_manager_v1 | 1 | |
|
||||
| wp_presentation | 1 | |
|
||||
| wp_single_pixel_buffer_manager_v1 | 1 | |
|
||||
| wp_tearing_control_manager_v1 | 1[^no_tearing] | |
|
||||
| wp_viewporter | 1 | |
|
||||
| xdg_activation_v1 | 1 | |
|
||||
| xdg_toplevel_drag_manager_v1 | 1 | |
|
||||
| xdg_wm_base | 6 | |
|
||||
| zwlr_data_control_manager_v1 | 2 | Yes |
|
||||
| zwlr_layer_shell_v1 | 4[^no_exclusive] | Yes |
|
||||
| zwlr_screencopy_manager_v1 | 3 | Yes |
|
||||
| zwp_idle_inhibit_manager_v1 | 1 | |
|
||||
| zwp_input_method_manager_v2 | 1 | Yes |
|
||||
| zwp_linux_dmabuf_v1 | 5 | |
|
||||
| zwp_pointer_constraints_v1 | 1 | |
|
||||
| zwp_primary_selection_device_manager_v1 | 1 | |
|
||||
| zwp_relative_pointer_manager_v1 | 1 | |
|
||||
| zwp_text_input_manager_v3 | 1 | |
|
||||
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
|
||||
| zxdg_decoration_manager_v1 | 1 | |
|
||||
| zxdg_output_manager_v1 | 3 | |
|
||||
| Global | Version | Privileged |
|
||||
|-----------------------------------------|:-----------------|---------------|
|
||||
| ext_foreign_toplevel_list_v1 | 1 | Yes |
|
||||
| ext_idle_notifier_v1 | 1 | Yes |
|
||||
| ext_session_lock_manager_v1 | 1 | Yes |
|
||||
| org_kde_kwin_server_decoration_manager | 1 | |
|
||||
| wl_compositor | 6[^no_touch] | |
|
||||
| wl_data_device_manager | 3 | |
|
||||
| wl_drm | 2 | |
|
||||
| wl_output | 4 | |
|
||||
| wl_seat | 9 | |
|
||||
| wl_shm | 2 | |
|
||||
| wl_subcompositor | 1 | |
|
||||
| wp_alpha_modifier_v1 | 1 | |
|
||||
| wp_content_type_manager_v1 | 1 | |
|
||||
| wp_cursor_shape_manager_v1 | 1 | |
|
||||
| wp_fractional_scale_manager_v1 | 1 | |
|
||||
| wp_linux_drm_syncobj_manager_v1 | 1 | |
|
||||
| wp_presentation | 1 | |
|
||||
| wp_security_context_manager_v1 | 1 | |
|
||||
| wp_single_pixel_buffer_manager_v1 | 1 | |
|
||||
| wp_tearing_control_manager_v1 | 1[^no_tearing] | |
|
||||
| wp_viewporter | 1 | |
|
||||
| xdg_activation_v1 | 1 | |
|
||||
| xdg_toplevel_drag_manager_v1 | 1 | |
|
||||
| xdg_wm_base | 6 | |
|
||||
| zwlr_data_control_manager_v1 | 2 | Yes |
|
||||
| zwlr_layer_shell_v1 | 4[^no_exclusive] | No[^lsaccess] |
|
||||
| zwlr_screencopy_manager_v1 | 3 | Yes |
|
||||
| zwp_idle_inhibit_manager_v1 | 1 | |
|
||||
| zwp_input_method_manager_v2 | 1 | Yes |
|
||||
| zwp_linux_dmabuf_v1 | 5 | |
|
||||
| zwp_pointer_constraints_v1 | 1 | |
|
||||
| zwp_primary_selection_device_manager_v1 | 1 | |
|
||||
| zwp_relative_pointer_manager_v1 | 1 | |
|
||||
| zwp_text_input_manager_v3 | 1 | |
|
||||
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
|
||||
| zxdg_decoration_manager_v1 | 1 | |
|
||||
| zxdg_output_manager_v1 | 3 | |
|
||||
|
||||
[^no_touch]: Touch input is not supported.
|
||||
[^no_tearing]: Tearing screen updates are not supported.
|
||||
[^no_exclusive]: Exclusive zones are not supported.
|
||||
[^lsaccess]: Sandboxes can restrict access to this protocol.
|
||||
|
||||
## Missing Features
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Unreleased
|
||||
|
||||
- Add support for wp-security-manager-v1.
|
||||
|
||||
# 1.1.0 (2024-04-22)
|
||||
|
||||
- Screencasts now support window capture.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::{ClientCaps, CAP_LAYER_SHELL},
|
||||
state::State,
|
||||
utils::{errorfmt::ErrorFmt, oserror::OsError, xrd::xrd},
|
||||
},
|
||||
|
|
@ -145,12 +146,16 @@ impl Acceptor {
|
|||
}
|
||||
let acc = Rc::new(Acceptor { socket });
|
||||
let futures = vec![
|
||||
state
|
||||
.eng
|
||||
.spawn(accept(acc.socket.secure.clone(), state.clone(), true)),
|
||||
state
|
||||
.eng
|
||||
.spawn(accept(acc.socket.insecure.clone(), state.clone(), false)),
|
||||
state.eng.spawn(accept(
|
||||
acc.socket.secure.clone(),
|
||||
state.clone(),
|
||||
ClientCaps::all(),
|
||||
)),
|
||||
state.eng.spawn(accept(
|
||||
acc.socket.insecure.clone(),
|
||||
state.clone(),
|
||||
CAP_LAYER_SHELL,
|
||||
)),
|
||||
];
|
||||
state.acceptor.set(Some(acc.clone()));
|
||||
Ok((acc, futures))
|
||||
|
|
@ -166,7 +171,7 @@ impl Acceptor {
|
|||
}
|
||||
}
|
||||
|
||||
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
|
||||
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, effective_caps: ClientCaps) {
|
||||
loop {
|
||||
let fd = match state.ring.accept(&fd, c::SOCK_CLOEXEC).await {
|
||||
Ok(fd) => fd,
|
||||
|
|
@ -176,7 +181,10 @@ async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
|
|||
}
|
||||
};
|
||||
let id = state.clients.id();
|
||||
if let Err(e) = state.clients.spawn(id, &state, fd, secure) {
|
||||
if let Err(e) = state
|
||||
.clients
|
||||
.spawn(id, &state, fd, effective_caps, ClientCaps::all())
|
||||
{
|
||||
log::error!("Could not spawn a client: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,18 @@ mod error;
|
|||
mod objects;
|
||||
mod tasks;
|
||||
|
||||
bitflags! {
|
||||
ClientCaps: u32;
|
||||
CAP_DATA_CONTROL_MANAGER = 1 << 0,
|
||||
CAP_VIRTUAL_KEYBOARD_MANAGER = 1 << 1,
|
||||
CAP_FOREIGN_TOPLEVEL_LIST = 1 << 2,
|
||||
CAP_IDLE_NOTIFIER = 1 << 3,
|
||||
CAP_SESSION_LOCK_MANAGER = 1 << 4,
|
||||
CAP_JAY_COMPOSITOR = 1 << 5,
|
||||
CAP_LAYER_SHELL = 1 << 6,
|
||||
CAP_SCREENCOPY_MANAGER = 1 << 7,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct ClientId(u64);
|
||||
|
||||
|
|
@ -101,7 +113,8 @@ impl Clients {
|
|||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: Rc<OwnedFd>,
|
||||
secure: bool,
|
||||
effective_caps: ClientCaps,
|
||||
bounding_caps: ClientCaps,
|
||||
) -> Result<(), ClientError> {
|
||||
let (uid, pid) = {
|
||||
let mut cred = c::ucred {
|
||||
|
|
@ -120,7 +133,16 @@ impl Clients {
|
|||
}
|
||||
}
|
||||
};
|
||||
self.spawn2(id, global, socket, uid, pid, secure, false)?;
|
||||
self.spawn2(
|
||||
id,
|
||||
global,
|
||||
socket,
|
||||
uid,
|
||||
pid,
|
||||
effective_caps,
|
||||
bounding_caps,
|
||||
false,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +153,8 @@ impl Clients {
|
|||
socket: Rc<OwnedFd>,
|
||||
uid: c::uid_t,
|
||||
pid: c::pid_t,
|
||||
secure: bool,
|
||||
effective_caps: ClientCaps,
|
||||
bounding_caps: ClientCaps,
|
||||
is_xwayland: bool,
|
||||
) -> Result<Rc<Client>, ClientError> {
|
||||
let data = Rc::new(Client {
|
||||
|
|
@ -145,7 +168,8 @@ impl Clients {
|
|||
shutdown: Default::default(),
|
||||
tracker: Default::default(),
|
||||
is_xwayland,
|
||||
secure,
|
||||
effective_caps,
|
||||
bounding_caps,
|
||||
last_enter_serial: Cell::new(0),
|
||||
pid_info: get_pid_info(uid, pid),
|
||||
serials: Default::default(),
|
||||
|
|
@ -165,13 +189,13 @@ impl Clients {
|
|||
data: data.clone(),
|
||||
};
|
||||
log::info!(
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}, secure: {}, comm: {:?}",
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}, caps: {:?}",
|
||||
id,
|
||||
pid,
|
||||
uid,
|
||||
client.data.socket.raw(),
|
||||
secure,
|
||||
data.pid_info.comm,
|
||||
effective_caps,
|
||||
);
|
||||
self.clients.borrow_mut().insert(client.data.id, client);
|
||||
Ok(data)
|
||||
|
|
@ -193,13 +217,15 @@ impl Clients {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn broadcast<B>(&self, secure: bool, xwayland_only: bool, mut f: B)
|
||||
pub fn broadcast<B>(&self, required_caps: ClientCaps, xwayland_only: bool, mut f: B)
|
||||
where
|
||||
B: FnMut(&Rc<Client>),
|
||||
{
|
||||
let clients = self.clients.borrow();
|
||||
for client in clients.values() {
|
||||
if (!secure || client.data.secure) && (!xwayland_only || client.data.is_xwayland) {
|
||||
if client.data.effective_caps.contains(required_caps)
|
||||
&& (!xwayland_only || client.data.is_xwayland)
|
||||
{
|
||||
f(&client.data);
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +284,8 @@ pub struct Client {
|
|||
shutdown: AsyncEvent,
|
||||
pub tracker: Tracker<Client>,
|
||||
pub is_xwayland: bool,
|
||||
pub secure: bool,
|
||||
pub effective_caps: ClientCaps,
|
||||
pub bounding_caps: ClientCaps,
|
||||
pub last_enter_serial: Cell<u32>,
|
||||
pub pid_info: PidInfo,
|
||||
pub serials: RefCell<VecDeque<SerialRange>>,
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ fn start_compositor2(
|
|||
wait_for_sync_obj: Rc::new(WaitForSyncObj::new(&ring, &engine)),
|
||||
explicit_sync_enabled: Cell::new(true),
|
||||
keyboard_state_ids: Default::default(),
|
||||
security_context_acceptors: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::Backend,
|
||||
client::Client,
|
||||
client::{Client, ClientCaps},
|
||||
ifs::{
|
||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1Global,
|
||||
ext_idle_notifier_v1::ExtIdleNotifierV1Global,
|
||||
|
|
@ -34,6 +34,7 @@ use {
|
|||
wp_cursor_shape_manager_v1::WpCursorShapeManagerV1Global,
|
||||
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1Global,
|
||||
wp_presentation::WpPresentationGlobal,
|
||||
wp_security_context_manager_v1::WpSecurityContextManagerV1Global,
|
||||
wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1Global,
|
||||
wp_tearing_control_manager_v1::WpTearingControlManagerV1Global,
|
||||
wp_viewporter::WpViewporterGlobal,
|
||||
|
|
@ -113,8 +114,8 @@ pub trait Global: GlobalBase {
|
|||
fn singleton(&self) -> bool;
|
||||
fn version(&self) -> u32;
|
||||
fn break_loops(&self) {}
|
||||
fn secure(&self) -> bool {
|
||||
false
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
ClientCaps::none()
|
||||
}
|
||||
fn xwayland_only(&self) -> bool {
|
||||
false
|
||||
|
|
@ -184,6 +185,7 @@ impl Globals {
|
|||
add_singleton!(ZwpVirtualKeyboardManagerV1Global);
|
||||
add_singleton!(ZwpInputMethodManagerV2Global);
|
||||
add_singleton!(ZwpTextInputManagerV3Global);
|
||||
add_singleton!(WpSecurityContextManagerV1Global);
|
||||
}
|
||||
|
||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||
|
|
@ -215,7 +217,7 @@ impl Globals {
|
|||
|
||||
fn insert(&self, state: &State, global: Rc<dyn Global>) {
|
||||
self.insert_no_broadcast_(&global);
|
||||
self.broadcast(state, global.secure(), global.xwayland_only(), |r| {
|
||||
self.broadcast(state, global.required_caps(), global.xwayland_only(), |r| {
|
||||
r.send_global(&global)
|
||||
});
|
||||
}
|
||||
|
|
@ -223,11 +225,13 @@ impl Globals {
|
|||
pub fn get(
|
||||
&self,
|
||||
name: GlobalName,
|
||||
allow_secure: bool,
|
||||
client_caps: ClientCaps,
|
||||
allow_xwayland_only: bool,
|
||||
) -> Result<Rc<dyn Global>, GlobalsError> {
|
||||
let global = self.take(name, false)?;
|
||||
if (global.secure() && !allow_secure) || (global.xwayland_only() && !allow_xwayland_only) {
|
||||
if client_caps.not_contains(global.required_caps())
|
||||
|| (global.xwayland_only() && !allow_xwayland_only)
|
||||
{
|
||||
return Err(GlobalsError::GlobalDoesNotExist(name));
|
||||
}
|
||||
Ok(global)
|
||||
|
|
@ -236,7 +240,7 @@ impl Globals {
|
|||
pub fn remove<T: WaylandGlobal>(&self, state: &State, global: &T) -> Result<(), GlobalsError> {
|
||||
let _global = self.take(global.name(), true)?;
|
||||
global.remove(self);
|
||||
self.broadcast(state, global.secure(), global.xwayland_only(), |r| {
|
||||
self.broadcast(state, global.required_caps(), global.xwayland_only(), |r| {
|
||||
r.send_global_remove(global.name())
|
||||
});
|
||||
Ok(())
|
||||
|
|
@ -247,14 +251,16 @@ impl Globals {
|
|||
}
|
||||
|
||||
pub fn notify_all(&self, registry: &Rc<WlRegistry>) {
|
||||
let secure = registry.client.secure;
|
||||
let caps = registry.client.effective_caps;
|
||||
let xwayland = registry.client.is_xwayland;
|
||||
let globals = self.registry.lock();
|
||||
macro_rules! emit {
|
||||
($singleton:expr) => {
|
||||
for global in globals.values() {
|
||||
if global.singleton() == $singleton {
|
||||
if (secure || !global.secure()) && (xwayland || !global.xwayland_only()) {
|
||||
if caps.contains(global.required_caps())
|
||||
&& (xwayland || !global.xwayland_only())
|
||||
{
|
||||
registry.send_global(global);
|
||||
}
|
||||
}
|
||||
|
|
@ -268,11 +274,11 @@ impl Globals {
|
|||
fn broadcast<F: Fn(&Rc<WlRegistry>)>(
|
||||
&self,
|
||||
state: &State,
|
||||
secure: bool,
|
||||
required_caps: ClientCaps,
|
||||
xwayland_only: bool,
|
||||
f: F,
|
||||
) {
|
||||
state.clients.broadcast(secure, xwayland_only, |c| {
|
||||
state.clients.broadcast(required_caps, xwayland_only, |c| {
|
||||
let registries = c.lock_registries();
|
||||
for registry in registries.values() {
|
||||
f(registry);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ pub mod wp_linux_drm_syncobj_manager_v1;
|
|||
pub mod wp_linux_drm_syncobj_timeline_v1;
|
||||
pub mod wp_presentation;
|
||||
pub mod wp_presentation_feedback;
|
||||
pub mod wp_security_context_manager_v1;
|
||||
pub mod wp_security_context_v1;
|
||||
pub mod wp_single_pixel_buffer_manager_v1;
|
||||
pub mod wp_tearing_control_manager_v1;
|
||||
pub mod wp_viewporter;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_FOREIGN_TOPLEVEL_LIST},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||
|
|
@ -145,8 +145,8 @@ impl Global for ExtForeignToplevelListV1Global {
|
|||
1
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_FOREIGN_TOPLEVEL_LIST
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_IDLE_NOTIFIER},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::ext_idle_notification_v1::ExtIdleNotificationV1,
|
||||
leaks::Tracker,
|
||||
|
|
@ -117,8 +117,8 @@ impl Global for ExtIdleNotifierV1Global {
|
|||
1
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_IDLE_NOTIFIER
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_SESSION_LOCK_MANAGER},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::ext_session_lock_v1::ExtSessionLockV1,
|
||||
leaks::Tracker,
|
||||
|
|
@ -98,8 +98,8 @@ impl Global for ExtSessionLockManagerV1Global {
|
|||
1
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_SESSION_LOCK_MANAGER
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_DATA_CONTROL_MANAGER},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::ipc::{
|
||||
zwlr_data_control_device_v1::{ZwlrDataControlDeviceV1, PRIMARY_SELECTION_SINCE},
|
||||
|
|
@ -111,8 +111,8 @@ impl Global for ZwlrDataControlManagerV1Global {
|
|||
2
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_DATA_CONTROL_MANAGER
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
cli::CliLogLevel,
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_JAY_COMPOSITOR},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
jay_idle::JayIdle,
|
||||
|
|
@ -68,8 +68,8 @@ impl Global for JayCompositorGlobal {
|
|||
1
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_JAY_COMPOSITOR
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ impl WlRegistryRequestHandler for WlRegistry {
|
|||
fn bind(&self, bind: Bind, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let name = GlobalName::from_raw(bind.name);
|
||||
let globals = &self.client.state.globals;
|
||||
let global = globals.get(name, self.client.secure, self.client.is_xwayland)?;
|
||||
let global = globals.get(name, self.client.effective_caps, self.client.is_xwayland)?;
|
||||
if global.interface().name() != bind.interface {
|
||||
return Err(WlRegistryError::InvalidInterface(InterfaceError {
|
||||
name: global.name(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_VIRTUAL_KEYBOARD_MANAGER},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::wl_seat::zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
|
||||
leaks::Tracker,
|
||||
|
|
@ -61,8 +61,8 @@ impl Global for ZwpVirtualKeyboardManagerV1Global {
|
|||
1
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_VIRTUAL_KEYBOARD_MANAGER
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
107
src/ifs/wp_security_context_manager_v1.rs
Normal file
107
src/ifs/wp_security_context_manager_v1.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::wp_security_context_v1::WpSecurityContextV1,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{wp_security_context_manager_v1::*, WpSecurityContextManagerV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct WpSecurityContextManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
impl WpSecurityContextManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: WpSecurityContextManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), WpSecurityContextManagerV1Error> {
|
||||
let obj = Rc::new(WpSecurityContextManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
WpSecurityContextManagerV1Global,
|
||||
WpSecurityContextManagerV1,
|
||||
WpSecurityContextManagerV1Error
|
||||
);
|
||||
|
||||
impl Global for WpSecurityContextManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(WpSecurityContextManagerV1Global);
|
||||
|
||||
pub struct WpSecurityContextManagerV1 {
|
||||
pub id: WpSecurityContextManagerV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl WpSecurityContextManagerV1RequestHandler for WpSecurityContextManagerV1 {
|
||||
type Error = WpSecurityContextManagerV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_listener(&self, req: CreateListener, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let obj = Rc::new(WpSecurityContextV1 {
|
||||
id: req.id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
listen_fd: req.listen_fd,
|
||||
close_fd: req.close_fd,
|
||||
sandbox_engine: Default::default(),
|
||||
app_id: Default::default(),
|
||||
instance_id: Default::default(),
|
||||
committed: Default::default(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpSecurityContextManagerV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for WpSecurityContextManagerV1 {}
|
||||
|
||||
simple_add_obj!(WpSecurityContextManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpSecurityContextManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(WpSecurityContextManagerV1Error, ClientError);
|
||||
119
src/ifs/wp_security_context_v1.rs
Normal file
119
src/ifs/wp_security_context_v1.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientCaps, ClientError},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{wp_security_context_v1::*, WpSecurityContextV1Id},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
rc::Rc,
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct WpSecurityContextV1 {
|
||||
pub id: WpSecurityContextV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub listen_fd: Rc<OwnedFd>,
|
||||
pub close_fd: Rc<OwnedFd>,
|
||||
pub sandbox_engine: RefCell<Option<String>>,
|
||||
pub app_id: RefCell<Option<String>>,
|
||||
pub instance_id: RefCell<Option<String>>,
|
||||
pub committed: Cell<bool>,
|
||||
}
|
||||
|
||||
impl WpSecurityContextV1 {
|
||||
fn check_committed(&self) -> Result<(), WpSecurityContextV1Error> {
|
||||
if self.committed.get() {
|
||||
return Err(WpSecurityContextV1Error::Committed);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WpSecurityContextV1RequestHandler for WpSecurityContextV1 {
|
||||
type Error = WpSecurityContextV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_sandbox_engine(
|
||||
&self,
|
||||
req: SetSandboxEngine<'_>,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.check_committed()?;
|
||||
let val = &mut *self.sandbox_engine.borrow_mut();
|
||||
if val.is_some() {
|
||||
return Err(WpSecurityContextV1Error::EnginSet);
|
||||
}
|
||||
*val = Some(req.name.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_app_id(&self, req: SetAppId<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.check_committed()?;
|
||||
let val = &mut *self.app_id.borrow_mut();
|
||||
if val.is_some() {
|
||||
return Err(WpSecurityContextV1Error::AppSet);
|
||||
}
|
||||
*val = Some(req.app_id.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_instance_id(&self, req: SetInstanceId<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.check_committed()?;
|
||||
let val = &mut *self.instance_id.borrow_mut();
|
||||
if val.is_some() {
|
||||
return Err(WpSecurityContextV1Error::InstanceSet);
|
||||
}
|
||||
*val = Some(req.instance_id.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit(&self, _req: Commit, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.check_committed()?;
|
||||
self.committed.set(true);
|
||||
let caps = ClientCaps::none() & self.client.bounding_caps;
|
||||
self.client.state.security_context_acceptors.spawn(
|
||||
&self.client.state,
|
||||
self.sandbox_engine.take(),
|
||||
self.app_id.take(),
|
||||
self.instance_id.take(),
|
||||
&self.listen_fd,
|
||||
&self.close_fd,
|
||||
caps,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpSecurityContextV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for WpSecurityContextV1 {}
|
||||
|
||||
simple_add_obj!(WpSecurityContextV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpSecurityContextV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("The sandbox engine has already been set")]
|
||||
EnginSet,
|
||||
#[error("The app id has already been set")]
|
||||
AppSet,
|
||||
#[error("The instance id has already been set")]
|
||||
InstanceSet,
|
||||
#[error("The context has already been committed")]
|
||||
Committed,
|
||||
}
|
||||
efrom!(WpSecurityContextV1Error, ClientError);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_LAYER_SHELL},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::wl_surface::zwlr_layer_surface_v1::{ZwlrLayerSurfaceV1, ZwlrLayerSurfaceV1Error},
|
||||
leaks::Tracker,
|
||||
|
|
@ -110,8 +110,8 @@ impl Global for ZwlrLayerShellV1Global {
|
|||
4
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_LAYER_SHELL
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
client::{Client, ClientCaps, ClientError, CAP_SCREENCOPY_MANAGER},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||
leaks::Tracker,
|
||||
|
|
@ -59,8 +59,8 @@ impl Global for ZwlrScreencopyManagerV1Global {
|
|||
3
|
||||
}
|
||||
|
||||
fn secure(&self) -> bool {
|
||||
true
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_SCREENCOPY_MANAGER
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ mod rect;
|
|||
mod renderer;
|
||||
mod scale;
|
||||
mod screenshoter;
|
||||
mod security_context_acceptor;
|
||||
mod sighand;
|
||||
mod state;
|
||||
mod tasks;
|
||||
|
|
|
|||
123
src/security_context_acceptor.rs
Normal file
123
src/security_context_acceptor.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::ClientCaps,
|
||||
state::State,
|
||||
utils::{copyhashmap::CopyHashMap, errorfmt::ErrorFmt},
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
fmt::{Display, Formatter},
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SecurityContextAcceptors {
|
||||
ids: AcceptorIds,
|
||||
acceptors: CopyHashMap<AcceptorId, Rc<Acceptor>>,
|
||||
}
|
||||
|
||||
linear_ids!(AcceptorIds, AcceptorId, u64);
|
||||
|
||||
struct Acceptor {
|
||||
id: AcceptorId,
|
||||
state: Rc<State>,
|
||||
sandbox_engine: Option<String>,
|
||||
app_id: Option<String>,
|
||||
instance_id: Option<String>,
|
||||
listen_fd: Rc<OwnedFd>,
|
||||
close_fd: Rc<OwnedFd>,
|
||||
caps: ClientCaps,
|
||||
listen_future: Cell<Option<SpawnedFuture<()>>>,
|
||||
close_future: Cell<Option<SpawnedFuture<()>>>,
|
||||
}
|
||||
|
||||
impl SecurityContextAcceptors {
|
||||
pub fn clear(&self) {
|
||||
for (_, acceptor) in self.acceptors.lock().drain() {
|
||||
acceptor.kill();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&self,
|
||||
state: &Rc<State>,
|
||||
sandbox_engine: Option<String>,
|
||||
app_id: Option<String>,
|
||||
instance_id: Option<String>,
|
||||
listen_fd: &Rc<OwnedFd>,
|
||||
close_fd: &Rc<OwnedFd>,
|
||||
caps: ClientCaps,
|
||||
) {
|
||||
let acceptor = Rc::new(Acceptor {
|
||||
id: self.ids.next(),
|
||||
state: state.clone(),
|
||||
sandbox_engine,
|
||||
app_id,
|
||||
instance_id,
|
||||
listen_fd: listen_fd.clone(),
|
||||
close_fd: close_fd.clone(),
|
||||
caps,
|
||||
listen_future: Cell::new(None),
|
||||
close_future: Cell::new(None),
|
||||
});
|
||||
log::info!("Creating security acceptor {acceptor}");
|
||||
acceptor
|
||||
.listen_future
|
||||
.set(Some(state.eng.spawn(acceptor.clone().accept())));
|
||||
acceptor
|
||||
.close_future
|
||||
.set(Some(state.eng.spawn(acceptor.clone().close())));
|
||||
self.acceptors.set(acceptor.id, acceptor);
|
||||
}
|
||||
}
|
||||
|
||||
impl Acceptor {
|
||||
fn kill(&self) {
|
||||
log::info!("Destroying security acceptor {self}");
|
||||
self.listen_future.take();
|
||||
self.close_future.take();
|
||||
self.state
|
||||
.security_context_acceptors
|
||||
.acceptors
|
||||
.remove(&self.id);
|
||||
}
|
||||
|
||||
async fn accept(self: Rc<Self>) {
|
||||
let s = &self.state;
|
||||
loop {
|
||||
let fd = match s.ring.accept(&self.listen_fd, c::SOCK_CLOEXEC).await {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
log::error!("Could not accept a client: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
};
|
||||
let id = s.clients.id();
|
||||
if let Err(e) = s.clients.spawn(id, s, fd, self.caps, self.caps) {
|
||||
log::error!("Could not spawn a client: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.kill();
|
||||
}
|
||||
|
||||
async fn close(self: Rc<Self>) {
|
||||
let _ = self.state.ring.poll(&self.close_fd, 0).await;
|
||||
self.kill();
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Acceptor {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}/{}/{}",
|
||||
self.sandbox_engine.as_deref().unwrap_or(""),
|
||||
self.app_id.as_deref().unwrap_or(""),
|
||||
self.instance_id.as_deref().unwrap_or(""),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -51,6 +51,7 @@ use {
|
|||
rect::Rect,
|
||||
renderer::{RenderResult, Renderer},
|
||||
scale::Scale,
|
||||
security_context_acceptor::SecurityContextAcceptors,
|
||||
theme::{Color, Theme},
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, Direction, DisplayNode, FloatNode, Node, NodeIds,
|
||||
|
|
@ -181,6 +182,7 @@ pub struct State {
|
|||
pub wait_for_sync_obj: Rc<WaitForSyncObj>,
|
||||
pub explicit_sync_enabled: Cell<bool>,
|
||||
pub keyboard_state_ids: KeyboardStateIds,
|
||||
pub security_context_acceptors: SecurityContextAcceptors,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -744,6 +746,7 @@ impl State {
|
|||
self.render_ctx_watchers.clear();
|
||||
self.workspace_watchers.clear();
|
||||
self.toplevel_lists.clear();
|
||||
self.security_context_acceptors.clear();
|
||||
self.slow_clients.clear();
|
||||
for (_, h) in self.input_device_handlers.borrow_mut().drain() {
|
||||
h.async_event.clear();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ mod xwm;
|
|||
|
||||
use {
|
||||
crate::{
|
||||
client::ClientError,
|
||||
client::{ClientCaps, ClientError},
|
||||
compositor::DISPLAY,
|
||||
forker::{ForkerError, ForkerProxy},
|
||||
ifs::{
|
||||
|
|
@ -171,7 +171,8 @@ async fn run(
|
|||
Rc::new(client1),
|
||||
uapi::getuid(),
|
||||
pid,
|
||||
true,
|
||||
ClientCaps::all(),
|
||||
ClientCaps::all(),
|
||||
true,
|
||||
);
|
||||
let client = match client {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ keymap = """
|
|||
};
|
||||
"""
|
||||
|
||||
on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } }
|
||||
on-graphics-initialized = { type = "exec", exec = "mako" }
|
||||
|
||||
[shortcuts]
|
||||
alt-h = "focus-left"
|
||||
|
|
@ -31,7 +31,7 @@ alt-f = "focus-parent"
|
|||
alt-shift-c = "close"
|
||||
alt-shift-f = "toggle-floating"
|
||||
Super_L = { type = "exec", exec = "alacritty" }
|
||||
alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } }
|
||||
alt-p = { type = "exec", exec = "bemenu-run" }
|
||||
alt-q = "quit"
|
||||
alt-shift-r = "reload-config-toml"
|
||||
|
||||
|
|
|
|||
|
|
@ -447,7 +447,7 @@
|
|||
"required": []
|
||||
},
|
||||
"Config": {
|
||||
"description": "This is the top-level table.\n\n- Example:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n\n on-graphics-initialized = { type = \"exec\", exec = { prog = \"mako\", privileged = true } }\n\n [shortcuts]\n alt-h = \"focus-left\"\n alt-j = \"focus-down\"\n alt-k = \"focus-up\"\n alt-l = \"focus-right\"\n\n alt-shift-h = \"move-left\"\n alt-shift-j = \"move-down\"\n alt-shift-k = \"move-up\"\n alt-shift-l = \"move-right\"\n\n alt-d = \"split-horizontal\"\n alt-v = \"split-vertical\"\n\n alt-t = \"toggle-split\"\n alt-m = \"toggle-mono\"\n alt-u = \"toggle-fullscreen\"\n\n alt-f = \"focus-parent\"\n alt-shift-c = \"close\"\n alt-shift-f = \"toggle-floating\"\n Super_L = { type = \"exec\", exec = \"alacritty\" }\n alt-p = { type = \"exec\", exec = { prog = \"bemenu-run\", privileged = true } }\n alt-q = \"quit\"\n alt-shift-r = \"reload-config-toml\"\n\n ctrl-alt-F1 = { type = \"switch-to-vt\", num = 1 }\n ctrl-alt-F2 = { type = \"switch-to-vt\", num = 2 }\n # ...\n\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n alt-F2 = { type = \"show-workspace\", name = \"2\" }\n # ...\n\n alt-shift-F1 = { type = \"move-to-workspace\", name = \"1\" }\n alt-shift-F2 = { type = \"move-to-workspace\", name = \"2\" }\n # ...\n ```\n",
|
||||
"description": "This is the top-level table.\n\n- Example:\n\n ```toml\n keymap = \"\"\"\n xkb_keymap {\n xkb_keycodes { include \"evdev+aliases(qwerty)\" };\n xkb_types { include \"complete\" };\n xkb_compat { include \"complete\" };\n xkb_symbols { include \"pc+us+inet(evdev)\" };\n };\n \"\"\"\n\n on-graphics-initialized = { type = \"exec\", exec = \"mako\" }\n\n [shortcuts]\n alt-h = \"focus-left\"\n alt-j = \"focus-down\"\n alt-k = \"focus-up\"\n alt-l = \"focus-right\"\n\n alt-shift-h = \"move-left\"\n alt-shift-j = \"move-down\"\n alt-shift-k = \"move-up\"\n alt-shift-l = \"move-right\"\n\n alt-d = \"split-horizontal\"\n alt-v = \"split-vertical\"\n\n alt-t = \"toggle-split\"\n alt-m = \"toggle-mono\"\n alt-u = \"toggle-fullscreen\"\n\n alt-f = \"focus-parent\"\n alt-shift-c = \"close\"\n alt-shift-f = \"toggle-floating\"\n Super_L = { type = \"exec\", exec = \"alacritty\" }\n alt-p = { type = \"exec\", exec = \"bemenu-run\" }\n alt-q = \"quit\"\n alt-shift-r = \"reload-config-toml\"\n\n ctrl-alt-F1 = { type = \"switch-to-vt\", num = 1 }\n ctrl-alt-F2 = { type = \"switch-to-vt\", num = 2 }\n # ...\n\n alt-F1 = { type = \"show-workspace\", name = \"1\" }\n alt-F2 = { type = \"show-workspace\", name = \"2\" }\n # ...\n\n alt-shift-F1 = { type = \"move-to-workspace\", name = \"1\" }\n alt-shift-F2 = { type = \"move-to-workspace\", name = \"2\" }\n # ...\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"keymap": {
|
||||
|
|
|
|||
|
|
@ -679,7 +679,7 @@ This is the top-level table.
|
|||
};
|
||||
"""
|
||||
|
||||
on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } }
|
||||
on-graphics-initialized = { type = "exec", exec = "mako" }
|
||||
|
||||
[shortcuts]
|
||||
alt-h = "focus-left"
|
||||
|
|
@ -703,7 +703,7 @@ This is the top-level table.
|
|||
alt-shift-c = "close"
|
||||
alt-shift-f = "toggle-floating"
|
||||
Super_L = { type = "exec", exec = "alacritty" }
|
||||
alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } }
|
||||
alt-p = { type = "exec", exec = "bemenu-run" }
|
||||
alt-q = "quit"
|
||||
alt-shift-r = "reload-config-toml"
|
||||
|
||||
|
|
|
|||
|
|
@ -1642,7 +1642,7 @@ Config:
|
|||
};
|
||||
"""
|
||||
|
||||
on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } }
|
||||
on-graphics-initialized = { type = "exec", exec = "mako" }
|
||||
|
||||
[shortcuts]
|
||||
alt-h = "focus-left"
|
||||
|
|
@ -1666,7 +1666,7 @@ Config:
|
|||
alt-shift-c = "close"
|
||||
alt-shift-f = "toggle-floating"
|
||||
Super_L = { type = "exec", exec = "alacritty" }
|
||||
alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } }
|
||||
alt-p = { type = "exec", exec = "bemenu-run" }
|
||||
alt-q = "quit"
|
||||
alt-shift-r = "reload-config-toml"
|
||||
|
||||
|
|
|
|||
9
wire/wp_security_context_manager_v1.txt
Normal file
9
wire/wp_security_context_manager_v1.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
request destroy {
|
||||
|
||||
}
|
||||
|
||||
request create_listener {
|
||||
id: id(wp_security_context_v1),
|
||||
listen_fd: fd,
|
||||
close_fd: fd,
|
||||
}
|
||||
19
wire/wp_security_context_v1.txt
Normal file
19
wire/wp_security_context_v1.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
request destroy {
|
||||
|
||||
}
|
||||
|
||||
request set_sandbox_engine {
|
||||
name: str,
|
||||
}
|
||||
|
||||
request set_app_id {
|
||||
app_id: str,
|
||||
}
|
||||
|
||||
request set_instance_id {
|
||||
instance_id: str,
|
||||
}
|
||||
|
||||
request commit {
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue