simple-im: add support for unicode input
This commit is contained in:
parent
9ac4fea594
commit
481e9b3854
13 changed files with 158 additions and 1 deletions
|
|
@ -1040,6 +1040,10 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::SeatReloadSimpleIm { seat });
|
self.send(&ClientMessage::SeatReloadSimpleIm { seat });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn seat_enable_unicode_input(&self, seat: Seat) {
|
||||||
|
self.send(&ClientMessage::SeatEnableUnicodeInput { seat });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_show_float_pin_icon(&self, show: bool) {
|
pub fn set_show_float_pin_icon(&self, show: bool) {
|
||||||
self.send(&ClientMessage::SetShowFloatPinIcon { show });
|
self.send(&ClientMessage::SetShowFloatPinIcon { show });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -798,6 +798,9 @@ pub enum ClientMessage<'a> {
|
||||||
SeatReloadSimpleIm {
|
SeatReloadSimpleIm {
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
},
|
},
|
||||||
|
SeatEnableUnicodeInput {
|
||||||
|
seat: Seat,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -631,6 +631,13 @@ impl Seat {
|
||||||
pub fn reload_simple_im(self) {
|
pub fn reload_simple_im(self) {
|
||||||
get!().seat_reload_simple_im(self);
|
get!().seat_reload_simple_im(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables Unicode input in the simple, XCompose based input method.
|
||||||
|
///
|
||||||
|
/// This has no effect if the simple IM is not currently active.
|
||||||
|
pub fn enable_unicode_input(self) {
|
||||||
|
get!().seat_enable_unicode_input(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A focus-follows-mouse mode.
|
/// A focus-follows-mouse mode.
|
||||||
|
|
|
||||||
|
|
@ -2301,6 +2301,12 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_seat_enable_unicode_input(&self, seat: Seat) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.enable_unicode_input();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn spaces_change(&self) {
|
fn spaces_change(&self) {
|
||||||
struct V;
|
struct V;
|
||||||
impl NodeVisitorBase for V {
|
impl NodeVisitorBase for V {
|
||||||
|
|
@ -3245,6 +3251,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SeatReloadSimpleIm { seat } => self
|
ClientMessage::SeatReloadSimpleIm { seat } => self
|
||||||
.handle_seat_reload_simple_im(seat)
|
.handle_seat_reload_simple_im(seat)
|
||||||
.wrn("seat_reload_simple_im")?,
|
.wrn("seat_reload_simple_im")?,
|
||||||
|
ClientMessage::SeatEnableUnicodeInput { seat } => self
|
||||||
|
.handle_seat_enable_unicode_input(seat)
|
||||||
|
.wrn("seat_enable_unicode_input")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ pub trait InputMethod {
|
||||||
fn done(self: Rc<Self>, seat: &WlSeatGlobal);
|
fn done(self: Rc<Self>, seat: &WlSeatGlobal);
|
||||||
fn is_simple(&self) -> bool;
|
fn is_simple(&self) -> bool;
|
||||||
fn cancel_simple(&self, seat: &WlSeatGlobal);
|
fn cancel_simple(&self, seat: &WlSeatGlobal);
|
||||||
|
fn enable_unicode_input(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InputMethodKeyboardGrab {
|
pub trait InputMethodKeyboardGrab {
|
||||||
|
|
@ -64,6 +65,12 @@ pub enum TextDisconnectReason {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WlSeatGlobal {
|
impl WlSeatGlobal {
|
||||||
|
pub fn enable_unicode_input(&self) {
|
||||||
|
if let Some(im) = self.input_method.get() {
|
||||||
|
im.enable_unicode_input();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_simple_im_enabled(self: &Rc<Self>, enabled: bool) {
|
pub fn set_simple_im_enabled(self: &Rc<Self>, enabled: bool) {
|
||||||
if self.simple_im_enabled.replace(enabled) == enabled {
|
if self.simple_im_enabled.replace(enabled) == enabled {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ use {
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
fmt::Write,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -36,6 +37,8 @@ pub struct SimpleIm {
|
||||||
table: compose::ComposeTable,
|
table: compose::ComposeTable,
|
||||||
initial_state: compose::State,
|
initial_state: compose::State,
|
||||||
states: RefCell<Vec<State>>,
|
states: RefCell<Vec<State>>,
|
||||||
|
unicode_input: RefCell<UnicodeInput>,
|
||||||
|
unicode_input_enabled: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
|
|
@ -43,6 +46,14 @@ struct State {
|
||||||
char: char,
|
char: char,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct UnicodeInput {
|
||||||
|
text: String,
|
||||||
|
cp: u32,
|
||||||
|
cursor: i32,
|
||||||
|
chars: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl SimpleIm {
|
impl SimpleIm {
|
||||||
pub fn new(ctx: &xkb::Context) -> Option<Rc<Self>> {
|
pub fn new(ctx: &xkb::Context) -> Option<Rc<Self>> {
|
||||||
let table = ctx.compose_table_builder().build(WriteToLog)?;
|
let table = ctx.compose_table_builder().build(WriteToLog)?;
|
||||||
|
|
@ -54,6 +65,8 @@ impl SimpleIm {
|
||||||
states: Default::default(),
|
states: Default::default(),
|
||||||
initial_state: table.create_state(),
|
initial_state: table.create_state(),
|
||||||
table,
|
table,
|
||||||
|
unicode_input: Default::default(),
|
||||||
|
unicode_input_enabled: Default::default(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +107,7 @@ impl InputMethod for SimpleIm {
|
||||||
self.active.set(active);
|
self.active.set(active);
|
||||||
if active {
|
if active {
|
||||||
self.states.borrow_mut().clear();
|
self.states.borrow_mut().clear();
|
||||||
|
self.unicode_input_enabled.set(false);
|
||||||
seat.input_method_grab.set(Some(self));
|
seat.input_method_grab.set(Some(self));
|
||||||
} else {
|
} else {
|
||||||
seat.input_method_grab.take();
|
seat.input_method_grab.take();
|
||||||
|
|
@ -110,6 +124,46 @@ impl InputMethod for SimpleIm {
|
||||||
con.disconnect(TextDisconnectReason::InputMethodDestroyed);
|
con.disconnect(TextDisconnectReason::InputMethodDestroyed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enable_unicode_input(&self) {
|
||||||
|
if !self.active.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(con) = self.con.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if self.unicode_input_enabled.replace(true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.states.borrow_mut().clear();
|
||||||
|
let ui = &mut *self.unicode_input.borrow_mut();
|
||||||
|
ui.cp = 0;
|
||||||
|
ui.chars = 0;
|
||||||
|
ui.flush_preedit(&con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnicodeInput {
|
||||||
|
fn update_text(&mut self) {
|
||||||
|
self.text.clear();
|
||||||
|
if self.chars == 0 {
|
||||||
|
let _ = write!(self.text, "U+");
|
||||||
|
self.cursor = self.text.len() as _;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let _ = write!(self.text, "U+{:x}", self.cp);
|
||||||
|
self.cursor = self.text.len() as _;
|
||||||
|
if let Some(char) = char::from_u32(self.cp) {
|
||||||
|
let _ = write!(self.text, " = {}", char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_preedit(&mut self, con: &TextInputConnection) {
|
||||||
|
self.update_text();
|
||||||
|
con.text_input
|
||||||
|
.send_preedit_string(Some(&self.text), self.cursor, self.cursor);
|
||||||
|
con.text_input.send_done();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputMethodKeyboardGrab for SimpleIm {
|
impl InputMethodKeyboardGrab for SimpleIm {
|
||||||
|
|
@ -122,7 +176,11 @@ impl InputMethodKeyboardGrab for SimpleIm {
|
||||||
};
|
};
|
||||||
let mut buf = [0; 4];
|
let mut buf = [0; 4];
|
||||||
let mut forward_to_node = true;
|
let mut forward_to_node = true;
|
||||||
|
if self.unicode_input_enabled.get() {
|
||||||
|
forward_to_node = false;
|
||||||
|
}
|
||||||
let states = &mut *self.states.borrow_mut();
|
let states = &mut *self.states.borrow_mut();
|
||||||
|
let ui = &mut self.unicode_input.borrow_mut();
|
||||||
let lookup = kb_state.map.lookup_table.lookup(
|
let lookup = kb_state.map.lookup_table.lookup(
|
||||||
kb_state.mods.group,
|
kb_state.mods.group,
|
||||||
kb_state.mods.mods,
|
kb_state.mods.mods,
|
||||||
|
|
@ -132,6 +190,53 @@ impl InputMethodKeyboardGrab for SimpleIm {
|
||||||
let is_control = mods.contains(ModifierMask::CONTROL);
|
let is_control = mods.contains(ModifierMask::CONTROL);
|
||||||
for sym in lookup {
|
for sym in lookup {
|
||||||
let sym = sym.keysym();
|
let sym = sym.keysym();
|
||||||
|
if self.unicode_input_enabled.get() {
|
||||||
|
let is_terminator = matches!(
|
||||||
|
sym,
|
||||||
|
syms::Return | syms::KP_Enter | syms::space | syms::KP_Space
|
||||||
|
);
|
||||||
|
if (is_terminator || (sym == syms::j && is_control))
|
||||||
|
&& ui.chars > 0
|
||||||
|
&& let Some(char) = char::from_u32(ui.cp)
|
||||||
|
{
|
||||||
|
self.unicode_input_enabled.set(false);
|
||||||
|
let s = char.encode_utf8(&mut buf);
|
||||||
|
con.text_input.send_preedit_string(None, 0, 0);
|
||||||
|
con.text_input.send_commit_string(Some(s));
|
||||||
|
con.text_input.send_done();
|
||||||
|
} else if sym == syms::Escape
|
||||||
|
|| (sym == syms::c && is_control)
|
||||||
|
|| (ui.chars == 0 && matches!(sym, syms::w | syms::d) && is_control)
|
||||||
|
{
|
||||||
|
self.unicode_input_enabled.set(false);
|
||||||
|
con.text_input.send_preedit_string(None, 0, 0);
|
||||||
|
con.text_input.send_done();
|
||||||
|
} else if sym == syms::BackSpace && ui.chars > 0 {
|
||||||
|
ui.chars -= 1;
|
||||||
|
ui.cp >>= 4;
|
||||||
|
ui.flush_preedit(&con);
|
||||||
|
} else if sym == syms::w && is_control {
|
||||||
|
ui.chars = 0;
|
||||||
|
ui.cp = 0;
|
||||||
|
ui.flush_preedit(&con);
|
||||||
|
} else if let Some(c) = sym.char()
|
||||||
|
&& ui.chars < 6
|
||||||
|
&& !is_control
|
||||||
|
{
|
||||||
|
let c = match c {
|
||||||
|
'0'..='9' => c as u32 - '0' as u32,
|
||||||
|
'a'..='f' => c as u32 - 'a' as u32 + 10,
|
||||||
|
'A'..='F' => c as u32 - 'A' as u32 + 10,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
ui.cp = (ui.cp << 4) | c;
|
||||||
|
if ui.cp != 0 {
|
||||||
|
ui.chars += 1;
|
||||||
|
ui.flush_preedit(&con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let mut new_state = states
|
let mut new_state = states
|
||||||
.last()
|
.last()
|
||||||
.map(|s| s.state.clone())
|
.map(|s| s.state.clone())
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,10 @@ impl InputMethod for ZwpInputMethodV2 {
|
||||||
fn cancel_simple(&self, _seat: &WlSeatGlobal) {
|
fn cancel_simple(&self, _seat: &WlSeatGlobal) {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enable_unicode_input(&self) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZwpInputMethodV2RequestHandler for ZwpInputMethodV2 {
|
impl ZwpInputMethodV2RequestHandler for ZwpInputMethodV2 {
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ pub enum SimpleCommand {
|
||||||
EnableSimpleIm(bool),
|
EnableSimpleIm(bool),
|
||||||
ToggleSimpleImEnabled,
|
ToggleSimpleImEnabled,
|
||||||
ReloadSimpleIm,
|
ReloadSimpleIm,
|
||||||
|
EnableUnicodeInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,7 @@ impl ActionParser<'_> {
|
||||||
"disable-simple-im" => EnableSimpleIm(false),
|
"disable-simple-im" => EnableSimpleIm(false),
|
||||||
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
|
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
|
||||||
"reload-simple-im" => ReloadSimpleIm,
|
"reload-simple-im" => ReloadSimpleIm,
|
||||||
|
"enable-unicode-input" => EnableUnicodeInput,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,10 @@ impl Action {
|
||||||
let persistent = state.persistent.clone();
|
let persistent = state.persistent.clone();
|
||||||
b.new(move || persistent.seat.reload_simple_im())
|
b.new(move || persistent.seat.reload_simple_im())
|
||||||
}
|
}
|
||||||
|
SimpleCommand::EnableUnicodeInput => {
|
||||||
|
let persistent = state.persistent.clone();
|
||||||
|
b.new(move || persistent.seat.enable_unicode_input())
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Action::Multi { actions } => {
|
Action::Multi { actions } => {
|
||||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||||
|
|
|
||||||
|
|
@ -1843,7 +1843,8 @@
|
||||||
"enable-simple-im",
|
"enable-simple-im",
|
||||||
"disable-simple-im",
|
"disable-simple-im",
|
||||||
"toggle-simple-im-enabled",
|
"toggle-simple-im-enabled",
|
||||||
"reload-simple-im"
|
"reload-simple-im",
|
||||||
|
"enable-unicode-input"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"SimpleIm": {
|
"SimpleIm": {
|
||||||
|
|
|
||||||
|
|
@ -4216,6 +4216,12 @@ The string should have one of the following values:
|
||||||
|
|
||||||
This is useful if you change the XCompose files after starting the compositor.
|
This is useful if you change the XCompose files after starting the compositor.
|
||||||
|
|
||||||
|
- `enable-unicode-input`:
|
||||||
|
|
||||||
|
Enables Unicode input in the simple, XCompose based input method.
|
||||||
|
|
||||||
|
This has no effect if the simple IM is not currently active.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="types-SimpleIm"></a>
|
<a name="types-SimpleIm"></a>
|
||||||
|
|
|
||||||
|
|
@ -1050,6 +1050,11 @@ SimpleActionName:
|
||||||
Reloads the simple, XCompose based input method.
|
Reloads the simple, XCompose based input method.
|
||||||
|
|
||||||
This is useful if you change the XCompose files after starting the compositor.
|
This is useful if you change the XCompose files after starting the compositor.
|
||||||
|
- value: enable-unicode-input
|
||||||
|
description: |
|
||||||
|
Enables Unicode input in the simple, XCompose based input method.
|
||||||
|
|
||||||
|
This has no effect if the simple IM is not currently active.
|
||||||
|
|
||||||
|
|
||||||
Color:
|
Color:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue