On Wayland, send Focused(false) once seats left
Given that we merge all the seats, we should consider that window is not focused once all seats wl_keyboards are no longer present. We use seats instead of keyboards to track focus to protect against wl_keyboard::leave not being delivered when removing the seat (usually it's not the case though). Fixes: #3376
This commit is contained in:
parent
df8805c0d2
commit
dd13ccda4c
4 changed files with 72 additions and 32 deletions
|
|
@ -45,6 +45,7 @@ Unreleased` header.
|
||||||
- Add `Window::builder`, which is intended to replace the (now deprecated) `WindowBuilder::new`.
|
- Add `Window::builder`, which is intended to replace the (now deprecated) `WindowBuilder::new`.
|
||||||
- On X11, reload dpi on `_XSETTINGS_SETTINGS` update.
|
- On X11, reload dpi on `_XSETTINGS_SETTINGS` update.
|
||||||
- On X11, fix deadlock when adjusting DPI and resizing at the same time.
|
- On X11, fix deadlock when adjusting DPI and resizing at the same time.
|
||||||
|
- On Wayland, fix `Focused(false)` being send when other seats still have window focused.
|
||||||
|
|
||||||
# 0.29.10
|
# 0.29.10
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,13 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||||
let window_id = wayland::make_wid(&surface);
|
let window_id = wayland::make_wid(&surface);
|
||||||
|
|
||||||
// Mark the window as focused.
|
// Mark the window as focused.
|
||||||
match state.windows.get_mut().get(&window_id) {
|
let was_unfocused = match state.windows.get_mut().get(&window_id) {
|
||||||
Some(window) => window.lock().unwrap().set_has_focus(true),
|
Some(window) => {
|
||||||
|
let mut window = window.lock().unwrap();
|
||||||
|
let was_unfocused = !window.has_focus();
|
||||||
|
window.add_seat_focus(data.seat.id());
|
||||||
|
was_unfocused
|
||||||
|
}
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,13 +78,15 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||||
keyboard_state.loop_handle.remove(token);
|
keyboard_state.loop_handle.remove(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The keyboard focus is considered as general focus.
|
|
||||||
state
|
|
||||||
.events_sink
|
|
||||||
.push_window_event(WindowEvent::Focused(true), window_id);
|
|
||||||
|
|
||||||
*data.window_id.lock().unwrap() = Some(window_id);
|
*data.window_id.lock().unwrap() = Some(window_id);
|
||||||
|
|
||||||
|
// The keyboard focus is considered as general focus.
|
||||||
|
if was_unfocused {
|
||||||
|
state
|
||||||
|
.events_sink
|
||||||
|
.push_window_event(WindowEvent::Focused(true), window_id);
|
||||||
|
}
|
||||||
|
|
||||||
// HACK: this is just for GNOME not fixing their ordering issue of modifiers.
|
// HACK: this is just for GNOME not fixing their ordering issue of modifiers.
|
||||||
if std::mem::take(&mut seat_state.modifiers_pending) {
|
if std::mem::take(&mut seat_state.modifiers_pending) {
|
||||||
state.events_sink.push_window_event(
|
state.events_sink.push_window_event(
|
||||||
|
|
@ -101,24 +108,30 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||||
|
|
||||||
// NOTE: The check whether the window exists is essential as we might get a
|
// NOTE: The check whether the window exists is essential as we might get a
|
||||||
// nil surface, regardless of what protocol says.
|
// nil surface, regardless of what protocol says.
|
||||||
match state.windows.get_mut().get(&window_id) {
|
let focused = match state.windows.get_mut().get(&window_id) {
|
||||||
Some(window) => window.lock().unwrap().set_has_focus(false),
|
Some(window) => {
|
||||||
|
let mut window = window.lock().unwrap();
|
||||||
|
window.remove_seat_focus(&data.seat.id());
|
||||||
|
window.has_focus()
|
||||||
|
}
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Notify that no modifiers are being pressed.
|
|
||||||
state.events_sink.push_window_event(
|
|
||||||
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
|
|
||||||
window_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
// We don't need to update it above, because the next `Enter` will overwrite
|
// We don't need to update it above, because the next `Enter` will overwrite
|
||||||
// anyway.
|
// anyway.
|
||||||
*data.window_id.lock().unwrap() = None;
|
*data.window_id.lock().unwrap() = None;
|
||||||
|
|
||||||
state
|
if !focused {
|
||||||
.events_sink
|
// Notify that no modifiers are being pressed.
|
||||||
.push_window_event(WindowEvent::Focused(false), window_id);
|
state.events_sink.push_window_event(
|
||||||
|
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
state
|
||||||
|
.events_sink
|
||||||
|
.push_window_event(WindowEvent::Focused(false), window_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WlKeyboardEvent::Key {
|
WlKeyboardEvent::Key {
|
||||||
key,
|
key,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
|
|
||||||
|
use sctk::reexports::client::backend::ObjectId;
|
||||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||||
|
|
@ -13,6 +14,7 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
||||||
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
||||||
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
||||||
|
|
||||||
|
use crate::event::WindowEvent;
|
||||||
use crate::keyboard::ModifiersState;
|
use crate::keyboard::ModifiersState;
|
||||||
use crate::platform_impl::wayland::state::WinitState;
|
use crate::platform_impl::wayland::state::WinitState;
|
||||||
|
|
||||||
|
|
@ -143,6 +145,10 @@ impl SeatHandler for WinitState {
|
||||||
) {
|
) {
|
||||||
let seat_state = self.seats.get_mut(&seat.id()).unwrap();
|
let seat_state = self.seats.get_mut(&seat.id()).unwrap();
|
||||||
|
|
||||||
|
if let Some(text_input) = seat_state.text_input.take() {
|
||||||
|
text_input.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
match capability {
|
match capability {
|
||||||
SeatCapability::Touch => {
|
SeatCapability::Touch => {
|
||||||
if let Some(touch) = seat_state.touch.take() {
|
if let Some(touch) = seat_state.touch.take() {
|
||||||
|
|
@ -174,13 +180,10 @@ impl SeatHandler for WinitState {
|
||||||
}
|
}
|
||||||
SeatCapability::Keyboard => {
|
SeatCapability::Keyboard => {
|
||||||
seat_state.keyboard_state = None;
|
seat_state.keyboard_state = None;
|
||||||
|
self.on_keyboard_destroy(&seat.id());
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(text_input) = seat_state.text_input.take() {
|
|
||||||
text_input.destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_seat(
|
fn new_seat(
|
||||||
|
|
@ -199,6 +202,21 @@ impl SeatHandler for WinitState {
|
||||||
seat: WlSeat,
|
seat: WlSeat,
|
||||||
) {
|
) {
|
||||||
let _ = self.seats.remove(&seat.id());
|
let _ = self.seats.remove(&seat.id());
|
||||||
|
self.on_keyboard_destroy(&seat.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WinitState {
|
||||||
|
fn on_keyboard_destroy(&mut self, seat: &ObjectId) {
|
||||||
|
for (window_id, window) in self.windows.get_mut() {
|
||||||
|
let mut window = window.lock().unwrap();
|
||||||
|
let had_focus = window.has_focus();
|
||||||
|
window.remove_seat_focus(seat);
|
||||||
|
if had_focus != window.has_focus() {
|
||||||
|
self.events_sink
|
||||||
|
.push_window_event(WindowEvent::Focused(false), *window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ use std::num::NonZeroU32;
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
use std::sync::{Arc, Mutex, Weak};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use ahash::HashSet;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
|
use sctk::reexports::client::backend::ObjectId;
|
||||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||||
use sctk::reexports::client::protocol::wl_shm::WlShm;
|
use sctk::reexports::client::protocol::wl_shm::WlShm;
|
||||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
|
|
@ -90,8 +92,10 @@ pub struct WindowState {
|
||||||
/// Whether the frame is resizable.
|
/// Whether the frame is resizable.
|
||||||
resizable: bool,
|
resizable: bool,
|
||||||
|
|
||||||
/// Whether the window has focus.
|
// NOTE: we can't use simple counter, since it's racy when seat getting destroyed and new
|
||||||
has_focus: bool,
|
// is created, since add/removed stuff could be delivered a bit out of order.
|
||||||
|
/// Seats that has keyboard focus on that window.
|
||||||
|
seat_focus: HashSet<ObjectId>,
|
||||||
|
|
||||||
/// The scale factor of the window.
|
/// The scale factor of the window.
|
||||||
scale_factor: f64,
|
scale_factor: f64,
|
||||||
|
|
@ -187,7 +191,7 @@ impl WindowState {
|
||||||
fractional_scale,
|
fractional_scale,
|
||||||
frame: None,
|
frame: None,
|
||||||
frame_callback_state: FrameCallbackState::None,
|
frame_callback_state: FrameCallbackState::None,
|
||||||
has_focus: false,
|
seat_focus: Default::default(),
|
||||||
has_pending_move: None,
|
has_pending_move: None,
|
||||||
ime_allowed: false,
|
ime_allowed: false,
|
||||||
ime_purpose: ImePurpose::Normal,
|
ime_purpose: ImePurpose::Normal,
|
||||||
|
|
@ -521,10 +525,10 @@ impl WindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the window is focused.
|
/// Whether the window is focused by any seat.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn has_focus(&self) -> bool {
|
pub fn has_focus(&self) -> bool {
|
||||||
self.has_focus
|
!self.seat_focus.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the IME is allowed.
|
/// Whether the IME is allowed.
|
||||||
|
|
@ -951,12 +955,16 @@ impl WindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark that the window has focus.
|
/// Add seat focus for the window.
|
||||||
///
|
|
||||||
/// Should be used from routine that sends focused event.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_has_focus(&mut self, has_focus: bool) {
|
pub fn add_seat_focus(&mut self, seat: ObjectId) {
|
||||||
self.has_focus = has_focus;
|
self.seat_focus.insert(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove seat focus from the window.
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_seat_focus(&mut self, seat: &ObjectId) {
|
||||||
|
self.seat_focus.remove(seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the requested state was applied.
|
/// Returns `true` if the requested state was applied.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue