git.lucas.co / cce-compositor
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 }