Skip to content

Commit 6b04843

Browse files
committed
fix: harden input-method-v1 resource lifecycle handling
1 parent 043809a commit 6b04843

File tree

1 file changed

+74
-18
lines changed
  • src/wayland/input_method_v1

1 file changed

+74
-18
lines changed

src/wayland/input_method_v1/mod.rs

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ use crate::input::{
4040
Seat, SeatHandler,
4141
};
4242
use crate::utils::{Logical, Point, Rectangle, SERIAL_COUNTER};
43+
use crate::wayland::compositor;
4344
use crate::wayland::seat::keyboard::for_each_focused_kbds;
4445

4546
const INPUT_METHOD_VERSION: u32 = 1;
4647
const TEXT_INPUT_VERSION: u32 = 1;
4748
const INPUT_PANEL_VERSION: u32 = 1;
49+
const INPUT_PANEL_SURFACE_ROLE: &str = "zwp_input_panel_surface_v1";
4850

4951
/// Parent surface and geometry used to place an IME popup.
5052
#[derive(Debug, Clone)]
@@ -772,12 +774,24 @@ where
772774
fn request(
773775
state: &mut D,
774776
_client: &Client,
775-
_resource: &ZwpInputMethodContextV1,
777+
resource: &ZwpInputMethodContextV1,
776778
request: zwp_input_method_context_v1::Request,
777779
data: &InputMethodContextUserData,
778780
_dhandle: &DisplayHandle,
779781
data_init: &mut DataInit<'_, D>,
780782
) {
783+
let is_active_context = {
784+
let inner = data.handle.inner.lock().unwrap();
785+
inner
786+
.active
787+
.context
788+
.as_ref()
789+
.is_some_and(|ctx| ctx.id() == resource.id())
790+
};
791+
if !is_active_context {
792+
return;
793+
}
794+
781795
match request {
782796
zwp_input_method_context_v1::Request::CommitString { serial, text } => {
783797
if let Some(ti) = data.handle.inner.lock().unwrap().active.text_input.as_ref() {
@@ -937,13 +951,22 @@ where
937951
fn destroyed(
938952
state: &mut D,
939953
_client: ClientId,
940-
_resource: &ZwpInputMethodContextV1,
954+
resource: &ZwpInputMethodContextV1,
941955
data: &InputMethodContextUserData,
942956
) {
943957
let active_seat = {
944958
let mut inner = data.handle.inner.lock().unwrap();
945-
inner.active.context = None;
946-
inner.active.seat.clone()
959+
let is_active_context = inner
960+
.active
961+
.context
962+
.as_ref()
963+
.is_some_and(|ctx| ctx.id() == resource.id());
964+
if is_active_context {
965+
inner.active.context = None;
966+
inner.active.seat.clone()
967+
} else {
968+
None
969+
}
947970
};
948971

949972
if let Some(seat) = active_seat.and_then(|s| Seat::<D>::from_resource(&s)) {
@@ -973,19 +996,22 @@ where
973996
fn destroyed(
974997
state: &mut D,
975998
_client: ClientId,
976-
_resource: &WlKeyboard,
999+
resource: &WlKeyboard,
9771000
data: &InputMethodKeyboardUserData<D>,
9781001
) {
979-
data.handle
980-
.inner
981-
.lock()
982-
.unwrap()
983-
.keyboard_grab
984-
.inner
985-
.lock()
986-
.unwrap()
987-
.grab = None;
988-
data.keyboard_handle.unset_grab(state);
1002+
let should_unset = {
1003+
let inner = data.handle.inner.lock().unwrap();
1004+
let mut grab = inner.keyboard_grab.inner.lock().unwrap();
1005+
if grab.grab.as_ref().is_some_and(|g| g.id() == resource.id()) {
1006+
grab.grab = None;
1007+
true
1008+
} else {
1009+
false
1010+
}
1011+
};
1012+
if should_unset {
1013+
data.keyboard_handle.unset_grab(state);
1014+
}
9891015
}
9901016
}
9911017

@@ -998,13 +1024,21 @@ where
9981024
fn request(
9991025
state: &mut D,
10001026
_client: &Client,
1001-
_resource: &ZwpInputPanelV1,
1027+
resource: &ZwpInputPanelV1,
10021028
request: zwp_input_panel_v1::Request,
10031029
data: &InputPanelUserData,
10041030
_dhandle: &DisplayHandle,
10051031
data_init: &mut DataInit<'_, D>,
10061032
) {
10071033
if let zwp_input_panel_v1::Request::GetInputPanelSurface { id, surface } = request {
1034+
if compositor::give_role(&surface, INPUT_PANEL_SURFACE_ROLE).is_err()
1035+
&& compositor::get_role(&surface) != Some(INPUT_PANEL_SURFACE_ROLE)
1036+
{
1037+
// Protocol requires this raise an error, but doesn't define an error enum.
1038+
resource.post_error(0u32, "Surface already has a role.");
1039+
return;
1040+
}
1041+
10081042
let panel_surface = data_init.init(
10091043
id,
10101044
InputPanelSurfaceUserData {
@@ -1039,12 +1073,23 @@ where
10391073
fn request(
10401074
state: &mut D,
10411075
_client: &Client,
1042-
_resource: &ZwpInputPanelSurfaceV1,
1076+
resource: &ZwpInputPanelSurfaceV1,
10431077
request: zwp_input_panel_surface_v1::Request,
10441078
data: &InputPanelSurfaceUserData,
10451079
_dhandle: &DisplayHandle,
10461080
_data_init: &mut DataInit<'_, D>,
10471081
) {
1082+
let is_active_popup_surface = {
1083+
let inner = data.handle.inner.lock().unwrap();
1084+
inner
1085+
.popup
1086+
.as_ref()
1087+
.is_some_and(|popup| popup.surface_role.id() == resource.id())
1088+
};
1089+
if !is_active_popup_surface {
1090+
return;
1091+
}
1092+
10481093
match request {
10491094
zwp_input_panel_surface_v1::Request::SetOverlayPanel => {
10501095
data.handle.inner.lock().unwrap().popup_enabled = true;
@@ -1061,9 +1106,20 @@ where
10611106
fn destroyed(
10621107
state: &mut D,
10631108
_client: ClientId,
1064-
_resource: &ZwpInputPanelSurfaceV1,
1109+
resource: &ZwpInputPanelSurfaceV1,
10651110
data: &InputPanelSurfaceUserData,
10661111
) {
1112+
let is_active_popup_surface = {
1113+
let inner = data.handle.inner.lock().unwrap();
1114+
inner
1115+
.popup
1116+
.as_ref()
1117+
.is_some_and(|popup| popup.surface_role.id() == resource.id())
1118+
};
1119+
if !is_active_popup_surface {
1120+
return;
1121+
}
1122+
10671123
{
10681124
let mut inner = data.handle.inner.lock().unwrap();
10691125
inner.popup = None;

0 commit comments

Comments
 (0)