Skip to content

Commit

Permalink
Merge pull request #307 from mahkoh/jorth/jay-tray
Browse files Browse the repository at this point in the history
wayland: implement jay-tray-v1
  • Loading branch information
mahkoh authored Oct 24, 2024
2 parents eff490a + 8c3cd97 commit d2fa4be
Show file tree
Hide file tree
Showing 33 changed files with 1,025 additions and 56 deletions.
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

0 comments on commit d2fa4be

Please sign in to comment.