Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
src/server/xdg_popup.rs (5.6K)
1 // SPDX-FileCopyrightText: © 2023 The River Developers
2 // SPDX-License-Identifier: GPL-3.0-only
3
4 use crate::ffi;
5 use crate::server::{WlListener, wl_listener_remove, wl_signal_add};
6
7 pub struct XdgPopup {
8 pub wlr_popup: *mut ffi::wlr_xdg_popup,
9 pub tree: *mut ffi::wlr_scene_tree,
10 pub capture_tree: *mut ffi::wlr_scene_tree,
11
12 pub destroy: ffi::wl_listener,
13 pub commit: ffi::wl_listener,
14 pub new_popup: ffi::wl_listener,
15 pub reposition: ffi::wl_listener,
16 }
17
18 impl XdgPopup {
19 pub unsafe fn create(
20 wlr_popup: *mut ffi::wlr_xdg_popup,
21 parent: *mut ffi::wlr_scene_tree,
22 capture_parent: *mut ffi::wlr_scene_tree,
23 ) -> Result<*mut Self, &'static str> {
24 let base_surface = ffi::river_wlr_xdg_popup_get_base(wlr_popup);
25 let tree = ffi::wlr_scene_xdg_surface_create(parent, base_surface);
26 if tree.is_null() {
27 return Err("wlr_scene_xdg_surface_create failed");
28 }
29
30 let mut capture_tree = std::ptr::null_mut();
31 if !capture_parent.is_null() {
32 capture_tree = ffi::wlr_scene_xdg_surface_create(capture_parent, base_surface);
33 if capture_tree.is_null() {
34 ffi::wlr_scene_node_destroy(tree as *mut ffi::wlr_scene_node);
35 return Err("wlr_scene_xdg_surface_create for capture parent failed");
36 }
37 }
38
39 let popup = Box::into_raw(Box::new(XdgPopup {
40 wlr_popup,
41 tree,
42 capture_tree,
43 destroy: std::mem::zeroed(),
44 commit: std::mem::zeroed(),
45 new_popup: std::mem::zeroed(),
46 reposition: std::mem::zeroed(),
47 }));
48
49 let destroy_ptr = &mut (*popup).destroy as *mut ffi::wl_listener as *mut WlListener;
50 (*destroy_ptr).notify = Some(handle_destroy);
51 wl_signal_add(ffi::river_wlr_xdg_popup_get_destroy_signal(wlr_popup), &mut (*popup).destroy);
52
53 let commit_ptr = &mut (*popup).commit as *mut ffi::wl_listener as *mut WlListener;
54 (*commit_ptr).notify = Some(handle_commit);
55 let wlr_surface = ffi::river_wlr_xdg_surface_get_surface(base_surface);
56 wl_signal_add(ffi::river_wlr_surface_get_commit_signal(wlr_surface), &mut (*popup).commit);
57
58 let new_popup_ptr = &mut (*popup).new_popup as *mut ffi::wl_listener as *mut WlListener;
59 (*new_popup_ptr).notify = Some(handle_new_popup);
60 wl_signal_add(ffi::river_wlr_xdg_surface_get_new_popup_signal(base_surface), &mut (*popup).new_popup);
61
62 let reposition_ptr = &mut (*popup).reposition as *mut ffi::wl_listener as *mut WlListener;
63 (*reposition_ptr).notify = Some(handle_reposition);
64 wl_signal_add(ffi::river_wlr_xdg_popup_get_reposition_signal(wlr_popup), &mut (*popup).reposition);
65
66 Ok(popup)
67 }
68 }
69
70 unsafe extern "C" fn handle_destroy(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
71 let popup = crate::container_of!(listener, XdgPopup, destroy);
72
73 wl_listener_remove(&mut (*popup).destroy);
74 wl_listener_remove(&mut (*popup).commit);
75 wl_listener_remove(&mut (*popup).new_popup);
76 wl_listener_remove(&mut (*popup).reposition);
77
78 let _ = Box::from_raw(popup);
79 }
80
81 unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
82 let popup = crate::container_of!(listener, XdgPopup, commit);
83 let base_surface = ffi::river_wlr_xdg_popup_get_base((*popup).wlr_popup);
84 if ffi::river_wlr_xdg_surface_get_initial_commit(base_surface) {
85 handle_reposition(&mut (*popup).reposition, std::ptr::null_mut());
86 }
87 }
88
89 unsafe extern "C" fn handle_new_popup(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void) {
90 let popup = crate::container_of!(listener, XdgPopup, new_popup);
91 let wlr_xdg_popup = data as *mut ffi::wlr_xdg_popup;
92
93 if let Err(e) = XdgPopup::create(wlr_xdg_popup, (*popup).tree, (*popup).capture_tree) {
94 log::error!("Failed to create nested popup: {}", e);
95 ffi::wl_resource_post_no_memory((*wlr_xdg_popup).resource);
96 }
97 }
98
99 unsafe extern "C" fn handle_reposition(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
100 let popup = crate::container_of!(listener, XdgPopup, reposition);
101
102 let mut parent_lx: i32 = 0;
103 let mut parent_ly: i32 = 0;
104 let parent_tree = ffi::river_wlr_scene_tree_get_parent((*popup).tree);
105 if parent_tree.is_null() {
106 return;
107 }
108
109 ffi::wlr_scene_node_coords(parent_tree as *mut ffi::wlr_scene_node, &mut parent_lx, &mut parent_ly);
110
111 let mut anchor = std::mem::zeroed();
112 ffi::river_wlr_xdg_popup_get_anchor_rect((*popup).wlr_popup, &mut anchor);
113 anchor.x += parent_lx;
114 anchor.y += parent_ly;
115
116 let server = if let Some(node_data) = crate::scene_node_data::SceneNodeData::from_node(parent_tree as *mut ffi::wlr_scene_node) {
117 match node_data.data {
118 crate::scene_node_data::SceneNodeDataVal::Window(w) => (*w).server,
119 crate::scene_node_data::SceneNodeDataVal::ShellSurface(s) => (*s).server,
120 _ => std::ptr::null_mut(),
121 }
122 } else {
123 std::ptr::null_mut()
124 };
125
126 if server.is_null() {
127 return;
128 }
129
130 let wlr_output = (*server).om.max_overlap_output(&anchor);
131 if wlr_output.is_null() {
132 return;
133 }
134
135 let mut constraint = std::mem::zeroed();
136 ffi::wlr_output_layout_get_box((*server).om.output_layout, wlr_output, &mut constraint);
137 constraint.x -= parent_lx;
138 constraint.y -= parent_ly;
139
140 ffi::wlr_xdg_popup_unconstrain_from_box((*popup).wlr_popup, &mut constraint);
141 ffi::wlr_xdg_surface_schedule_configure(ffi::river_wlr_xdg_popup_get_base((*popup).wlr_popup));
142 }