parent
d3a46b7c8a
commit
d8a9c28fa7
5 changed files with 94 additions and 17 deletions
|
|
@ -24,6 +24,7 @@ pub trait XConnection: Sized + 'static {
|
||||||
fn set_fullscreen(&mut self, window: x::Window, fullscreen: bool, data: Self::ExtraData);
|
fn set_fullscreen(&mut self, window: x::Window, fullscreen: bool, data: Self::ExtraData);
|
||||||
fn focus_window(&mut self, window: x::Window, data: Self::ExtraData);
|
fn focus_window(&mut self, window: x::Window, data: Self::ExtraData);
|
||||||
fn close_window(&mut self, window: x::Window, data: Self::ExtraData);
|
fn close_window(&mut self, window: x::Window, data: Self::ExtraData);
|
||||||
|
fn raise_to_top(&mut self, window: x::Window);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromServerState<C: XConnection> {
|
pub trait FromServerState<C: XConnection> {
|
||||||
|
|
|
||||||
|
|
@ -385,17 +385,23 @@ impl HandleEvent for Pointer {
|
||||||
ref surface,
|
ref surface,
|
||||||
surface_x,
|
surface_x,
|
||||||
surface_y,
|
surface_y,
|
||||||
} => {
|
} => 'enter: {
|
||||||
let do_enter = || {
|
|
||||||
debug!("entering surface ({serial})");
|
|
||||||
if let Some(surface) = state.get_server_surface_from_client(surface.clone()) {
|
|
||||||
self.server.enter(serial, surface, surface_x, surface_y)
|
|
||||||
} else {
|
|
||||||
warn!("could not enter surface: stale surface");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let surface_key: ObjectKey = surface.data().copied().unwrap();
|
let surface_key: ObjectKey = surface.data().copied().unwrap();
|
||||||
let surface_data: &SurfaceData = state.objects[surface_key].as_ref();
|
let Some(surface_data): Option<&SurfaceData> =
|
||||||
|
state.objects.get(surface_key).map(|o| o.as_ref())
|
||||||
|
else {
|
||||||
|
warn!("could not enter surface: stale surface");
|
||||||
|
break 'enter;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut do_enter = || {
|
||||||
|
debug!("entering surface ({serial})");
|
||||||
|
self.server
|
||||||
|
.enter(serial, &surface_data.server, surface_x, surface_y);
|
||||||
|
let window = surface_data.window.unwrap();
|
||||||
|
state.connection.as_mut().unwrap().raise_to_top(window);
|
||||||
|
state.last_hovered = Some(window);
|
||||||
|
};
|
||||||
|
|
||||||
if matches!(surface_data.role, Some(SurfaceRole::Popup(_))) {
|
if matches!(surface_data.role, Some(SurfaceRole::Popup(_))) {
|
||||||
match self.pending_enter.0.take() {
|
match self.pending_enter.0.take() {
|
||||||
|
|
|
||||||
|
|
@ -427,6 +427,7 @@ pub struct ServerState<C: XConnection> {
|
||||||
qh: ClientQueueHandle,
|
qh: ClientQueueHandle,
|
||||||
to_focus: Option<x::Window>,
|
to_focus: Option<x::Window>,
|
||||||
last_focused_toplevel: Option<x::Window>,
|
last_focused_toplevel: Option<x::Window>,
|
||||||
|
last_hovered: Option<x::Window>,
|
||||||
connection: Option<C>,
|
connection: Option<C>,
|
||||||
|
|
||||||
xdg_wm_base: XdgWmBase,
|
xdg_wm_base: XdgWmBase,
|
||||||
|
|
@ -468,6 +469,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
dh,
|
dh,
|
||||||
to_focus: None,
|
to_focus: None,
|
||||||
last_focused_toplevel: None,
|
last_focused_toplevel: None,
|
||||||
|
last_hovered: None,
|
||||||
connection: None,
|
connection: None,
|
||||||
objects: Default::default(),
|
objects: Default::default(),
|
||||||
associated_windows: Default::default(),
|
associated_windows: Default::default(),
|
||||||
|
|
@ -629,6 +631,9 @@ impl<C: XConnection> ServerState<C> {
|
||||||
if matches!(self.last_focused_toplevel, Some(x) if x == window) {
|
if matches!(self.last_focused_toplevel, Some(x) if x == window) {
|
||||||
self.last_focused_toplevel.take();
|
self.last_focused_toplevel.take();
|
||||||
}
|
}
|
||||||
|
if self.last_hovered == Some(window) {
|
||||||
|
self.last_hovered.take();
|
||||||
|
}
|
||||||
win.mapped = false;
|
win.mapped = false;
|
||||||
|
|
||||||
if let Some(key) = win.surface_key.take() {
|
if let Some(key) = win.surface_key.take() {
|
||||||
|
|
@ -792,9 +797,10 @@ impl<C: XConnection> ServerState<C> {
|
||||||
let window_data = self.windows.get_mut(&window).unwrap();
|
let window_data = self.windows.get_mut(&window).unwrap();
|
||||||
if window_data.attrs.override_redirect {
|
if window_data.attrs.override_redirect {
|
||||||
// Override redirect is hard to convert to Wayland!
|
// Override redirect is hard to convert to Wayland!
|
||||||
// We will just make them be popups for the last focused toplevel.
|
if let Some(win) = self.last_hovered {
|
||||||
if let Some(win) = self.last_focused_toplevel {
|
window_data.attrs.popup_for = Some(win);
|
||||||
window_data.attrs.popup_for = Some(win)
|
} else if let Some(win) = self.last_focused_toplevel {
|
||||||
|
window_data.attrs.popup_for = Some(win);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let window = self.windows.get(&window).unwrap();
|
let window = self.windows.get(&window).unwrap();
|
||||||
|
|
@ -932,6 +938,9 @@ impl<C: XConnection> ServerState<C> {
|
||||||
if self.last_focused_toplevel == Some(window) {
|
if self.last_focused_toplevel == Some(window) {
|
||||||
self.last_focused_toplevel.take();
|
self.last_focused_toplevel.take();
|
||||||
}
|
}
|
||||||
|
if self.last_hovered == Some(window) {
|
||||||
|
self.last_hovered.take();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use wayland_client::{
|
||||||
wl_compositor::WlCompositor,
|
wl_compositor::WlCompositor,
|
||||||
wl_display::WlDisplay,
|
wl_display::WlDisplay,
|
||||||
wl_keyboard::WlKeyboard,
|
wl_keyboard::WlKeyboard,
|
||||||
|
wl_pointer::WlPointer,
|
||||||
wl_registry::WlRegistry,
|
wl_registry::WlRegistry,
|
||||||
wl_seat::{self, WlSeat},
|
wl_seat::{self, WlSeat},
|
||||||
wl_shm::{Format, WlShm},
|
wl_shm::{Format, WlShm},
|
||||||
|
|
@ -206,6 +207,13 @@ impl super::XConnection for FakeXConnection {
|
||||||
);
|
);
|
||||||
self.focused_window = window.into();
|
self.focused_window = window.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn raise_to_top(&mut self, window: Window) {
|
||||||
|
assert!(
|
||||||
|
self.windows.contains_key(&window),
|
||||||
|
"Unknown window: {window:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeServerState = ServerState<FakeXConnection>;
|
type FakeServerState = ServerState<FakeXConnection>;
|
||||||
|
|
@ -1120,6 +1128,56 @@ fn clipboard_x11_then_wayland() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raise_window_on_pointer_event() {
|
||||||
|
let (mut f, comp) = TestFixture::new_with_compositor();
|
||||||
|
TestObject::<WlPointer>::from_request(&comp.seat.obj, wl_seat::Request::GetPointer {});
|
||||||
|
let win1 = unsafe { Window::new(1) };
|
||||||
|
let (_, id1) = f.create_toplevel(&comp, win1);
|
||||||
|
f.testwl.configure_toplevel(id1, 100, 100, vec![]);
|
||||||
|
|
||||||
|
let win2 = unsafe { Window::new(2) };
|
||||||
|
let (_, id2) = f.create_toplevel(&comp, win2);
|
||||||
|
assert_eq!(f.connection().focused_window, Some(win2));
|
||||||
|
|
||||||
|
f.testwl.move_pointer_to(id2, 0.0, 0.0);
|
||||||
|
f.run();
|
||||||
|
assert_eq!(f.connection().focused_window, Some(win2));
|
||||||
|
assert_eq!(f.exwayland.last_hovered, Some(win2));
|
||||||
|
|
||||||
|
f.testwl.move_pointer_to(id1, 0.0, 0.0);
|
||||||
|
f.run();
|
||||||
|
assert_eq!(f.connection().focused_window, Some(win2));
|
||||||
|
assert_eq!(f.exwayland.last_hovered, Some(win1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn override_redirect_choose_hover_window() {
|
||||||
|
let (mut f, comp) = TestFixture::new_with_compositor();
|
||||||
|
TestObject::<WlPointer>::from_request(&comp.seat.obj, wl_seat::Request::GetPointer {});
|
||||||
|
let win1 = unsafe { Window::new(1) };
|
||||||
|
let (_, id1) = f.create_toplevel(&comp, win1);
|
||||||
|
f.testwl.configure_toplevel(id1, 100, 100, vec![]);
|
||||||
|
|
||||||
|
let win2 = unsafe { Window::new(2) };
|
||||||
|
let _ = f.create_toplevel(&comp, win2);
|
||||||
|
assert_eq!(f.connection().focused_window, Some(win2));
|
||||||
|
|
||||||
|
f.testwl.move_pointer_to(id1, 0.0, 0.0);
|
||||||
|
f.run();
|
||||||
|
assert_eq!(f.exwayland.last_hovered, Some(win1));
|
||||||
|
|
||||||
|
let win3 = unsafe { Window::new(3) };
|
||||||
|
let (buffer, surface) = comp.create_surface();
|
||||||
|
f.new_window(win3, true, WindowData::default(), None);
|
||||||
|
f.map_window(&comp, win3, &surface.obj, &buffer);
|
||||||
|
f.run();
|
||||||
|
let id3 = f.check_new_surface();
|
||||||
|
let popup_data = f.testwl.get_surface_data(id3).unwrap();
|
||||||
|
let win1_xdg = &f.testwl.get_surface_data(id1).unwrap().xdg().surface;
|
||||||
|
assert_eq!(&popup_data.popup().parent, win1_xdg);
|
||||||
|
}
|
||||||
|
|
||||||
/// See Pointer::handle_event for an explanation.
|
/// See Pointer::handle_event for an explanation.
|
||||||
#[test]
|
#[test]
|
||||||
fn popup_pointer_motion_workaround() {}
|
fn popup_pointer_motion_workaround() {}
|
||||||
|
|
|
||||||
|
|
@ -842,10 +842,6 @@ impl super::XConnection for Arc<xcb::Connection> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrap_or_skip_bad_window!(self.send_and_check_request(&x::ConfigureWindow {
|
|
||||||
window,
|
|
||||||
value_list: &[x::ConfigWindow::StackMode(x::StackMode::Above)],
|
|
||||||
}));
|
|
||||||
unwrap_or_skip_bad_window!(self.send_and_check_request(&x::ChangeProperty {
|
unwrap_or_skip_bad_window!(self.send_and_check_request(&x::ChangeProperty {
|
||||||
mode: x::PropMode::Replace,
|
mode: x::PropMode::Replace,
|
||||||
window: self.root_window(),
|
window: self.root_window(),
|
||||||
|
|
@ -870,6 +866,13 @@ impl super::XConnection for Arc<xcb::Connection> {
|
||||||
event,
|
event,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn raise_to_top(&mut self, window: x::Window) {
|
||||||
|
unwrap_or_skip_bad_window!(self.send_and_check_request(&x::ConfigureWindow {
|
||||||
|
window,
|
||||||
|
value_list: &[x::ConfigWindow::StackMode(x::StackMode::Above)],
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::FromServerState<Arc<xcb::Connection>> for Atoms {
|
impl super::FromServerState<Arc<xcb::Connection>> for Atoms {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue