git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/text_input.rs (5.4K)

  1 // SPDX-FileCopyrightText: © 2021 The River Developers
  2 // SPDX-License-Identifier: GPL-3.0-only
  3 
  4 use crate::ffi;
  5 use crate::server::{WlListener, wl_signal_add, WlList};
  6 use crate::seat::Seat;
  7 
  8 #[repr(C)]
  9 pub struct TextInput {
 10     pub link: ffi::wl_list,
 11     pub wlr_text_input: *mut ffi::wlr_text_input_v3,
 12 
 13     pub enable: ffi::wl_listener,
 14     pub commit: ffi::wl_listener,
 15     pub disable: ffi::wl_listener,
 16     pub destroy: ffi::wl_listener,
 17 }
 18 
 19 unsafe fn connect_listener(
 20     signal: *mut ffi::wl_signal,
 21     listener: *mut ffi::wl_listener,
 22     callback: unsafe extern "C" fn(listener: *mut ffi::wl_listener, data: *mut std::ffi::c_void),
 23 ) {
 24     let wl_lis = listener as *mut WlListener;
 25     (*wl_lis).notify = Some(callback);
 26     wl_signal_add(signal, listener);
 27 }
 28 
 29 unsafe fn wl_listener_remove_safe(listener: *mut ffi::wl_listener) {
 30     let prev = (*listener).link.prev;
 31     let next = (*listener).link.next;
 32     if !prev.is_null() && !next.is_null() && prev != listener as *mut ffi::wl_list && next != listener as *mut ffi::wl_list {
 33         ffi::wl_list_remove(&mut (*listener).link);
 34         (*listener).link.prev = std::ptr::null_mut();
 35         (*listener).link.next = std::ptr::null_mut();
 36     }
 37 }
 38 
 39 impl TextInput {
 40     pub unsafe fn create(wlr_text_input: *mut ffi::wlr_text_input_v3) -> Result<(), &'static str> {
 41         let wlr_seat = (*wlr_text_input).seat;
 42         let seat = ffi::river_wlr_seat_get_data(wlr_seat) as *mut Seat;
 43         if seat.is_null() {
 44             return Err("Seat is null");
 45         }
 46 
 47         let seat_name = std::ffi::CStr::from_ptr(ffi::river_wlr_seat_get_name(wlr_seat))
 48             .to_str()
 49             .unwrap_or("unknown");
 50         log::debug!("new text input on seat {}", seat_name);
 51 
 52         let mut text_input = Box::new(TextInput {
 53             link: std::mem::zeroed(),
 54             wlr_text_input,
 55             enable: std::mem::zeroed(),
 56             commit: std::mem::zeroed(),
 57             disable: std::mem::zeroed(),
 58             destroy: std::mem::zeroed(),
 59         });
 60 
 61         ffi::wl_list_init(&mut text_input.link);
 62 
 63         let raw = Box::into_raw(text_input);
 64 
 65         // Append to seat.relay.text_inputs
 66         let text_inputs_list = &mut (*seat).relay.text_inputs as *mut ffi::wl_list as *mut WlList;
 67         crate::server::wl_list_insert((*text_inputs_list).prev, &mut (*raw).link as *mut ffi::wl_list as *mut WlList);
 68 
 69         connect_listener(&mut (*wlr_text_input).events.enable, &mut (*raw).enable, handle_enable);
 70         connect_listener(&mut (*wlr_text_input).events.commit, &mut (*raw).commit, handle_commit);
 71         connect_listener(&mut (*wlr_text_input).events.disable, &mut (*raw).disable, handle_disable);
 72         connect_listener(&mut (*wlr_text_input).events.destroy, &mut (*raw).destroy, handle_destroy);
 73 
 74         Ok(())
 75     }
 76 }
 77 
 78 unsafe extern "C" fn handle_enable(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
 79     let text_input = crate::container_of!(listener, TextInput, enable);
 80     let seat = ffi::river_wlr_seat_get_data((*(*text_input).wlr_text_input).seat) as *mut Seat;
 81     if seat.is_null() {
 82         return;
 83     }
 84 
 85     if (*(*text_input).wlr_text_input).focused_surface.is_null() {
 86         log::error!("client requested to enable text input without focus, ignoring request");
 87         return;
 88     }
 89 
 90     if !(*seat).relay.text_input.is_null() {
 91         if text_input != (*seat).relay.text_input {
 92             log::error!("client requested to enable more than one text input on a single seat, ignoring request");
 93             return;
 94         }
 95     }
 96 
 97     (*seat).relay.text_input = text_input;
 98 
 99     let input_method = (*seat).relay.input_method;
100     if !input_method.is_null() {
101         ffi::wlr_input_method_v2_send_activate(input_method);
102         (*seat).relay.send_input_method_state();
103     }
104 }
105 
106 unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
107     let text_input = crate::container_of!(listener, TextInput, commit);
108     let seat = ffi::river_wlr_seat_get_data((*(*text_input).wlr_text_input).seat) as *mut Seat;
109     if seat.is_null() {
110         return;
111     }
112 
113     if (*seat).relay.text_input != text_input {
114         log::error!("inactive text input tried to commit an update, client bug?");
115         return;
116     }
117 
118     if !(*seat).relay.input_method.is_null() {
119         (*seat).relay.send_input_method_state();
120     }
121 }
122 
123 unsafe extern "C" fn handle_disable(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
124     let text_input = crate::container_of!(listener, TextInput, disable);
125     let seat = ffi::river_wlr_seat_get_data((*(*text_input).wlr_text_input).seat) as *mut Seat;
126     if seat.is_null() {
127         return;
128     }
129 
130     if (*seat).relay.text_input == text_input {
131         (*seat).relay.disable_text_input();
132     }
133 }
134 
135 unsafe extern "C" fn handle_destroy(listener: *mut ffi::wl_listener, _data: *mut std::ffi::c_void) {
136     let text_input = crate::container_of!(listener, TextInput, destroy);
137     let seat = ffi::river_wlr_seat_get_data((*(*text_input).wlr_text_input).seat) as *mut Seat;
138     if seat.is_null() {
139         return;
140     }
141 
142     if (*seat).relay.text_input == text_input {
143         (*seat).relay.disable_text_input();
144     }
145 
146     wl_listener_remove_safe(&mut (*text_input).enable);
147     wl_listener_remove_safe(&mut (*text_input).commit);
148     wl_listener_remove_safe(&mut (*text_input).disable);
149     wl_listener_remove_safe(&mut (*text_input).destroy);
150 
151     ffi::wl_list_remove(&mut (*text_input).link);
152 
153     let _ = Box::from_raw(text_input);
154 }