Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wayland: implement jay-tray-v1 #307

Merged
merged 3 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ keymap = """
"""

# An action that will be executed when the GPU has been initialized.
on-graphics-initialized = { type = "exec", exec = "mako" }
on-graphics-initialized = [
{ type = "exec", exec = "mako" },
{ type = "exec", exec = "wl-tray-bridge" },
]

# Shortcuts that are processed by the compositor.
# The left hand side should be a key, possibly prefixed with modifiers.
Expand Down Expand Up @@ -266,7 +269,10 @@ 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 = "mako" }
on-graphics-initialized = [
{ type = "exec", exec = "mako" },
{ type = "exec", exec = "wl-tray-bridge" },
]
```

### Setting Environment Variables
Expand Down Expand Up @@ -490,7 +496,7 @@ output.name = "left"

See the specification for more details.

# Theming
### Theming

You can configure the colors, sizes, and fonts used by the compositor with the top-level `theme` table.

Expand All @@ -500,3 +506,10 @@ bg-color = "#ff000"
```

See the specification for more details.

### Tray Icons and Menus

The default configuration will try to start [wl-tray-bridge] to give you access to tray
icons and menus.

[wl-tray-bridge]: https://github.com/mahkoh/wl-tray-bridge
1 change: 1 addition & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Jay supports the following wayland protocols:
| ext_output_image_capture_source_manager_v1 | 1 | |
| ext_session_lock_manager_v1 | 1 | Yes |
| ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes |
| jay_tray_v1 | 1 | |
| org_kde_kwin_server_decoration_manager | 1 | |
| wl_compositor | 6 | |
| wl_data_device_manager | 3 | |
Expand Down
2 changes: 2 additions & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
- Fix screen sharing in zoom.
- Implement wp-fifo-v1.
- Implement wp-commit-timing-v1.
- Implement jay-tray-v1. You can get tray icons and menus by using
https://github.com/mahkoh/wl-tray-bridge.

# 1.6.0 (2024-09-25)

Expand Down
2 changes: 2 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ impl Clients {
slf,
)),
wire_scale: Default::default(),
focus_stealing_serial: Default::default(),
});
track!(data, data);
let display = Rc::new(WlDisplay::new(&data));
Expand Down Expand Up @@ -286,6 +287,7 @@ pub struct Client {
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
pub commit_timelines: Rc<CommitTimelines>,
pub wire_scale: Cell<Option<i32>>,
pub focus_stealing_serial: Cell<Option<u64>>,
}

pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
Expand Down
3 changes: 3 additions & 0 deletions src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ fn start_compositor2(
ui_drag_threshold_squared: Cell::new(10),
toplevels: Default::default(),
const_40hz_latch: Default::default(),
tray_item_ids: Default::default(),
});
state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state);
Expand Down Expand Up @@ -586,6 +587,8 @@ fn create_dummy_output(state: &Rc<State>) {
flip_margin_ns: Default::default(),
ext_copy_sessions: Default::default(),
before_latch_event: Default::default(),
tray_start_rel: Default::default(),
tray_items: Default::default(),
});
let dummy_workspace = Rc::new(WorkspaceNode {
id: state.node_ids.next(),
Expand Down
6 changes: 3 additions & 3 deletions src/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,11 @@ impl Globals {
pub fn remove<T: RemovableWaylandGlobal>(
&self,
state: &State,
global: &T,
global: &Rc<T>,
) -> Result<(), GlobalsError> {
let _global = self.take(global.name(), true)?;
global.remove(self);
let replacement = global.create_replacement();
let replacement = global.clone().create_replacement();
assert_eq!(global.name(), replacement.name());
assert_eq!(global.interface().0, replacement.interface().0);
self.removed.set(global.name(), replacement);
Expand Down Expand Up @@ -360,5 +360,5 @@ pub trait WaylandGlobal: Global + 'static {
}

pub trait RemovableWaylandGlobal: WaylandGlobal {
fn create_replacement(&self) -> Rc<dyn Global>;
fn create_replacement(self: Rc<Self>) -> Rc<dyn Global>;
}
1 change: 1 addition & 0 deletions src/ifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod jay_seat_events;
pub mod jay_select_toplevel;
pub mod jay_select_workspace;
pub mod jay_toplevel;
pub mod jay_tray_v1;
pub mod jay_workspace;
pub mod jay_workspace_watcher;
pub mod jay_xwayland;
Expand Down
109 changes: 109 additions & 0 deletions src/ifs/jay_tray_v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName, RemovableWaylandGlobal},
ifs::{
wl_output::OutputGlobalOpt,
wl_surface::tray::jay_tray_item_v1::{JayTrayItemV1, JayTrayItemV1Error},
},
leaks::Tracker,
object::{Object, Version},
wire::{jay_tray_v1::*, JayTrayV1Id},
},
std::rc::Rc,
thiserror::Error,
};

pub struct JayTrayV1Global {
pub name: GlobalName,
pub output: Rc<OutputGlobalOpt>,
}

pub struct JayTrayV1 {
pub id: JayTrayV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
pub output: Rc<OutputGlobalOpt>,
}

impl JayTrayV1Global {
fn bind_(
self: Rc<Self>,
id: JayTrayV1Id,
client: &Rc<Client>,
version: Version,
) -> Result<(), JayTrayManagerV1Error> {
let obj = Rc::new(JayTrayV1 {
id,
client: client.clone(),
tracker: Default::default(),
version,
output: self.output.clone(),
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}

global_base!(JayTrayV1Global, JayTrayV1, JayTrayManagerV1Error);

impl Global for JayTrayV1Global {
fn singleton(&self) -> bool {
false
}

fn version(&self) -> u32 {
1
}
}

simple_add_global!(JayTrayV1Global);

impl RemovableWaylandGlobal for JayTrayV1Global {
fn create_replacement(self: Rc<Self>) -> Rc<dyn Global> {
self
}
}

impl JayTrayV1RequestHandler for JayTrayV1 {
type Error = JayTrayManagerV1Error;

fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}

fn get_tray_item(&self, req: GetTrayItem, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let surface = self.client.lookup(req.surface)?;
let fs = Rc::new(JayTrayItemV1::new(
req.id,
self.version,
&surface,
&self.output,
));
track!(self.client, fs);
fs.install()?;
self.client.add_client_obj(&fs)?;
Ok(())
}
}

object_base! {
self = JayTrayV1;
version = self.version;
}

impl Object for JayTrayV1 {}

simple_add_obj!(JayTrayV1);

#[derive(Debug, Error)]
pub enum JayTrayManagerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error(transparent)]
ExtTrayItemV1Error(#[from] JayTrayItemV1Error),
}
efrom!(JayTrayManagerV1Error, ClientError);
2 changes: 1 addition & 1 deletion src/ifs/wl_output/removed_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl Global for RemovedOutputGlobal {
simple_add_global!(RemovedOutputGlobal);

impl RemovableWaylandGlobal for WlOutputGlobal {
fn create_replacement(&self) -> Rc<dyn Global> {
fn create_replacement(self: Rc<Self>) -> Rc<dyn Global> {
Rc::new(RemovedOutputGlobal { name: self.name })
}
}
Expand Down
58 changes: 55 additions & 3 deletions src/ifs/wl_seat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod zwp_virtual_keyboard_v1;
use {
crate::{
async_engine::SpawnedFuture,
backend::KeyState,
client::{Client, ClientError, ClientId},
cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner},
ei::ei_ifs::ei_seat::EiSeat,
Expand Down Expand Up @@ -64,7 +65,12 @@ use {
zwp_pointer_gesture_swipe_v1::ZwpPointerGestureSwipeV1,
zwp_relative_pointer_v1::ZwpRelativePointerV1,
},
wl_surface::{dnd_icon::DndIcon, WlSurface},
wl_surface::{
dnd_icon::DndIcon,
tray::{DynTrayItem, TrayItemId},
xdg_surface::xdg_popup::XdgPopup,
WlSurface,
},
xdg_toplevel_drag_v1::XdgToplevelDragV1,
},
leaks::Tracker,
Expand All @@ -82,8 +88,8 @@ use {
},
wire::{
wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId,
WlSeatId, WlTouchId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id,
ZwpRelativePointerV1Id, ZwpTextInputV3Id,
WlSeatId, WlTouchId, XdgPopupId, ZwlrDataControlDeviceV1Id,
ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, ZwpTextInputV3Id,
},
wire_ei::EiSeatId,
xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState},
Expand Down Expand Up @@ -200,6 +206,8 @@ pub struct WlSeatGlobal {
tablet: TabletSeatData,
ei_seats: CopyHashMap<(ClientId, EiSeatId), Rc<EiSeat>>,
ui_drag_highlight: Cell<Option<Rect>>,
keyboard_node_serial: Cell<u64>,
tray_popups: CopyHashMap<(TrayItemId, XdgPopupId), Rc<dyn DynTrayItem>>,
}

const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
Expand Down Expand Up @@ -229,6 +237,7 @@ impl WlSeatGlobal {
pointer_stack_modified: Cell::new(false),
found_tree: RefCell::new(vec![]),
keyboard_node: CloneCell::new(state.root.clone()),
keyboard_node_serial: Default::default(),
bindings: Default::default(),
x_data_devices: Default::default(),
data_devices: RefCell::new(Default::default()),
Expand Down Expand Up @@ -271,6 +280,7 @@ impl WlSeatGlobal {
tablet: Default::default(),
ei_seats: Default::default(),
ui_drag_highlight: Default::default(),
tray_popups: Default::default(),
});
slf.pointer_cursor.set_owner(slf.clone());
let seat = slf.clone();
Expand Down Expand Up @@ -1039,6 +1049,48 @@ impl WlSeatGlobal {
ei_seat.regions_changed();
});
}

pub fn add_tray_item_popup<T: DynTrayItem>(&self, item: &Rc<T>, popup: &Rc<XdgPopup>) {
self.tray_popups
.set((item.data().tray_item_id, popup.id), item.clone());
}

pub fn remove_tray_item_popup<T: DynTrayItem>(&self, item: &T, popup: &Rc<XdgPopup>) {
self.tray_popups
.remove(&(item.data().tray_item_id, popup.id));
}

fn handle_node_button(
self: &Rc<Self>,
node: Rc<dyn Node>,
time_usec: u64,
button: u32,
state: KeyState,
serial: u64,
) {
if self.tray_popups.is_not_empty() && state == KeyState::Pressed {
let id = node.node_tray_item();
self.tray_popups.lock().retain(|&(tray_item_id, _), item| {
let retain = Some(tray_item_id) == id;
if !retain {
item.destroy_popups();
}
retain
})
}
node.node_on_button(self, time_usec, button, state, serial);
}

pub fn handle_focus_request(self: &Rc<Self>, client: &Client, node: Rc<dyn Node>, serial: u64) {
let Some(max_serial) = client.focus_stealing_serial.get() else {
return;
};
let serial = serial.min(max_serial);
if serial <= self.keyboard_node_serial.get() {
return;
}
self.focus_node_with_serial(node, serial);
}
}

impl CursorUserOwner for WlSeatGlobal {
Expand Down
Loading
Loading