1
0
Fork 0
forked from wry/wry

config: allow configuring the simple IM

This commit is contained in:
Julian Orth 2025-10-16 01:48:47 +02:00
parent 58b9830aaa
commit 2f22a61710
15 changed files with 367 additions and 7 deletions

View file

@ -1026,6 +1026,20 @@ impl ConfigClient {
self.send(&ClientMessage::SeatCopyMark { seat, src, dst });
}
pub fn seat_set_simple_im_enabled(&self, seat: Seat, enabled: bool) {
self.send(&ClientMessage::SeatSetSimpleImEnabled { seat, enabled });
}
pub fn seat_get_simple_im_enabled(&self, seat: Seat) -> bool {
let res = self.send_with_response(&ClientMessage::SeatGetSimpleImEnabled { seat });
get_response!(res, false, SeatGetSimpleImEnabled { enabled });
enabled
}
pub fn seat_reload_simple_im(&self, seat: Seat) {
self.send(&ClientMessage::SeatReloadSimpleIm { seat });
}
pub fn set_show_float_pin_icon(&self, show: bool) {
self.send(&ClientMessage::SetShowFloatPinIcon { show });
}

View file

@ -788,6 +788,16 @@ pub enum ClientMessage<'a> {
workspace: Workspace,
connector: Connector,
},
SeatSetSimpleImEnabled {
seat: Seat,
enabled: bool,
},
SeatGetSimpleImEnabled {
seat: Seat,
},
SeatReloadSimpleIm {
seat: Seat,
},
}
#[derive(Serialize, Deserialize, Debug)]
@ -1020,6 +1030,9 @@ pub enum Response {
GetShowBar {
show: bool,
},
SeatGetSimpleImEnabled {
enabled: bool,
},
}
#[derive(Serialize, Deserialize, Debug)]

View file

@ -603,6 +603,34 @@ impl Seat {
pub fn copy_mark(self, src: u32, dst: u32) {
get!().seat_copy_mark(self, src, dst);
}
/// Sets whether the simple, XCompose based input method is enabled.
///
/// Regardless of this setting, this input method is not used if an external input
/// method is running.
///
/// The default is `true`.
pub fn set_simple_im_enabled(self, enabled: bool) {
get!().seat_set_simple_im_enabled(self, enabled);
}
/// Returns whether the simple, XCompose based input method is enabled.
pub fn simple_im_enabled(self) -> bool {
get!(true).seat_get_simple_im_enabled(self)
}
/// Toggles whether the simple, XCompose based input method is enabled.
pub fn toggle_simple_im_enabled(self) {
let get = get!();
get.seat_set_simple_im_enabled(self, !get.seat_get_simple_im_enabled(self));
}
/// Reloads the simple, XCompose based input method.
///
/// This is useful if you change the XCompose files after starting the compositor.
pub fn reload_simple_im(self) {
get!().seat_reload_simple_im(self);
}
}
/// A focus-follows-mouse mode.

View file

@ -2281,6 +2281,26 @@ impl ConfigProxyHandler {
Ok(())
}
fn handle_seat_set_simple_im_enabled(&self, seat: Seat, enabled: bool) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.set_simple_im_enabled(enabled);
Ok(())
}
fn handle_seat_get_simple_im_enabled(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
self.respond(Response::SeatGetSimpleImEnabled {
enabled: seat.simple_im_enabled(),
});
Ok(())
}
fn handle_seat_reload_simple_im(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.reload_simple_im();
Ok(())
}
fn spaces_change(&self) {
struct V;
impl NodeVisitorBase for V {
@ -3216,6 +3236,15 @@ impl ConfigProxyHandler {
} => self
.handle_show_workspace(seat, workspace, Some(connector))
.wrn("show_workspace_on")?,
ClientMessage::SeatSetSimpleImEnabled { seat, enabled } => self
.handle_seat_set_simple_im_enabled(seat, enabled)
.wrn("seat_set_simple_im_enabled")?,
ClientMessage::SeatGetSimpleImEnabled { seat } => self
.handle_seat_get_simple_im_enabled(seat)
.wrn("seat_get_simple_im_enabled")?,
ClientMessage::SeatReloadSimpleIm { seat } => self
.handle_seat_reload_simple_im(seat)
.wrn("seat_reload_simple_im")?,
}
Ok(())
}

View file

@ -239,6 +239,7 @@ pub struct WlSeatGlobal {
modifiers_listener: EventListener<dyn LedsListener>,
modifiers_forward: EventSource<dyn LedsListener>,
simple_im: CloneCell<Option<Rc<SimpleIm>>>,
simple_im_enabled: Cell<bool>,
}
#[derive(Copy, Clone)]
@ -330,6 +331,7 @@ impl WlSeatGlobal {
modifiers_listener: EventListener::new(slf.clone()),
modifiers_forward: Default::default(),
simple_im: CloneCell::new(simple_im),
simple_im_enabled: Cell::new(true),
});
slf.pointer_cursor.set_owner(slf.clone());
slf.modifiers_listener

View file

@ -2,7 +2,10 @@ use {
crate::{
backend::KeyState,
ifs::{
wl_seat::{WlSeatGlobal, text_input::zwp_text_input_v3::ZwpTextInputV3},
wl_seat::{
WlSeatGlobal,
text_input::{simple_im::SimpleIm, zwp_text_input_v3::ZwpTextInputV3},
},
wl_surface::{WlSurface, zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2},
},
keyboard::KeyboardState,
@ -61,6 +64,42 @@ pub enum TextDisconnectReason {
}
impl WlSeatGlobal {
pub fn set_simple_im_enabled(self: &Rc<Self>, enabled: bool) {
if self.simple_im_enabled.replace(enabled) == enabled {
return;
}
if enabled {
if self.input_method.is_none()
&& let Some(im) = self.simple_im.get()
{
self.set_input_method(im);
}
} else {
if let Some(im) = self.input_method.get()
&& im.is_simple()
{
self.input_method.take();
im.cancel_simple(self);
}
}
}
pub fn simple_im_enabled(&self) -> bool {
self.simple_im_enabled.get()
}
pub fn reload_simple_im(self: &Rc<Self>) {
let im = SimpleIm::new(&self.state.kb_ctx.ctx);
self.simple_im.set(im.clone());
if self.simple_im_enabled.get() && self.can_set_new_im() {
if let Some(im) = im {
self.set_input_method(im);
} else if let Some(old) = self.input_method.take() {
old.cancel_simple(self);
}
}
}
fn can_set_new_im(&self) -> bool {
match self.input_method.get() {
None => true,
@ -82,7 +121,9 @@ impl WlSeatGlobal {
fn remove_input_method(self: &Rc<Self>) {
self.input_method.take();
if let Some(im) = self.simple_im.get() {
if self.simple_im_enabled.get()
&& let Some(im) = self.simple_im.get()
{
self.set_input_method(im);
}
}

View file

@ -85,6 +85,9 @@ pub enum SimpleCommand {
CreateMark,
JumpToMark,
PopMode(bool),
EnableSimpleIm(bool),
ToggleSimpleImEnabled,
ReloadSimpleIm,
}
#[derive(Debug, Clone)]
@ -446,6 +449,11 @@ pub struct Vrr {
pub cursor_hz: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct SimpleIm {
pub enabled: Option<bool>,
}
#[derive(Debug, Clone)]
pub struct Xwayland {
pub scaling_mode: Option<XScalingMode>,
@ -520,6 +528,7 @@ pub struct Config {
pub middle_click_paste: Option<bool>,
pub input_modes: AHashMap<String, InputMode>,
pub workspace_display_order: Option<WorkspaceDisplayOrder>,
pub simple_im: Option<SimpleIm>,
}
#[derive(Debug, Error)]

View file

@ -39,6 +39,7 @@ mod output;
mod output_match;
mod repeat_rate;
pub mod shortcuts;
mod simple_im;
mod status;
mod tearing;
mod theme;

View file

@ -155,6 +155,10 @@ impl ActionParser<'_> {
"jump-to-mark" => JumpToMark,
"clear-modes" => PopMode(false),
"pop-mode" => PopMode(true),
"enable-simple-im" => EnableSimpleIm(true),
"disable-simple-im" => EnableSimpleIm(false),
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
"reload-simple-im" => ReloadSimpleIm,
_ => {
return Err(
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)

View file

@ -30,6 +30,7 @@ use {
ComplexShortcutsParser, ShortcutsParser, ShortcutsParserError,
parse_modified_keysym_str,
},
simple_im::SimpleImParser,
status::StatusParser,
tearing::TearingParser,
theme::ThemeParser,
@ -139,7 +140,13 @@ impl Parser for ConfigParser<'_> {
show_bar,
focus_history_val,
),
(middle_click_paste, input_modes_val, workspace_display_order_val, auto_reload),
(
middle_click_paste,
input_modes_val,
workspace_display_order_val,
auto_reload,
simple_im_val,
),
) = ext.extract((
(
opt(val("keymap")),
@ -194,6 +201,7 @@ impl Parser for ConfigParser<'_> {
opt(val("modes")),
opt(val("workspace-display-order")),
recover(opt(bol("auto-reload"))),
opt(val("simple-im")),
),
))?;
let mut keymap = None;
@ -505,6 +513,15 @@ impl Parser for ConfigParser<'_> {
}
}
}
let mut simple_im = None;
if let Some(value) = simple_im_val {
match value.parse(&mut SimpleImParser(self.0)) {
Ok(v) => simple_im = Some(v),
Err(e) => {
log::warn!("Could not parse simple IM setting: {}", self.0.error(e));
}
}
}
Ok(Config {
keymap,
repeat_rate,
@ -549,6 +566,7 @@ impl Parser for ConfigParser<'_> {
middle_click_paste: middle_click_paste.despan(),
input_modes,
workspace_display_order,
simple_im,
})
}
}

View file

@ -0,0 +1,44 @@
use {
crate::{
config::{
SimpleIm,
context::Context,
extractor::{Extractor, ExtractorError, bol, opt, recover},
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
},
toml::{
toml_span::{DespanExt, Span, Spanned},
toml_value::Value,
},
},
indexmap::IndexMap,
thiserror::Error,
};
#[derive(Debug, Error)]
pub enum SimpleImParserError {
#[error(transparent)]
Expected(#[from] UnexpectedDataType),
#[error(transparent)]
Extract(#[from] ExtractorError),
}
pub struct SimpleImParser<'a>(pub &'a Context<'a>);
impl Parser for SimpleImParser<'_> {
type Value = SimpleIm;
type Error = SimpleImParserError;
const EXPECTED: &'static [DataType] = &[DataType::Table];
fn parse_table(
&mut self,
span: Span,
table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> {
let mut ext = Extractor::new(self.0, span, table);
let (enabled,) = ext.extract((recover(opt(bol("enabled"))),))?;
Ok(SimpleIm {
enabled: enabled.despan(),
})
}
}

View file

@ -224,6 +224,18 @@ impl Action {
let state = state.clone();
b.new(move || state.pop_mode(pop))
}
SimpleCommand::EnableSimpleIm(v) => {
let persistent = state.persistent.clone();
b.new(move || persistent.seat.set_simple_im_enabled(v))
}
SimpleCommand::ToggleSimpleImEnabled => {
let persistent = state.persistent.clone();
b.new(move || persistent.seat.toggle_simple_im_enabled())
}
SimpleCommand::ReloadSimpleIm => {
let persistent = state.persistent.clone();
b.new(move || persistent.seat.reload_simple_im())
}
},
Action::Multi { actions } => {
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
@ -1559,6 +1571,11 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
if let Some(v) = config.workspace_display_order {
set_workspace_display_order(v);
}
if let Some(simple_im) = config.simple_im {
if let Some(enabled) = simple_im.enabled {
persistent.seat.set_simple_im_enabled(enabled);
}
}
}
fn create_command(exec: &Exec) -> Command {

View file

@ -361,7 +361,7 @@
]
},
{
"description": "Sets the log level of the compositor..\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-j = { type = \"set-log-level\", level = \"debug\" }\n ```\n",
"description": "Sets the log level of the compositor.\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-j = { type = \"set-log-level\", level = \"debug\" }\n ```\n",
"type": "object",
"properties": {
"type": {
@ -1049,6 +1049,10 @@
"workspace-display-order": {
"description": "Configures the order of workspaces displayed.\n\nThe default is `manual`.\n\n- Example:\n\n ```toml\n workspace-display-order = \"sorted\"\n ```\n",
"$ref": "#/$defs/WorkspaceDisplayOrder"
},
"simple-im": {
"description": "Configures the simple, XCompose based input method.\n\nBy default, the input method is enabled. \n\nEven if the input method is enabled, it will only be used if there is no\nrunning external IM.\n\n- Example:\n\n ```toml\n [simple-im]\n enabled = false\n ```\n",
"$ref": "#/$defs/SimpleIm"
}
},
"required": []
@ -1835,9 +1839,24 @@
"create-mark",
"jump-to-mark",
"clear-modes",
"pop-mode"
"pop-mode",
"enable-simple-im",
"disable-simple-im",
"toggle-simple-im-enabled",
"reload-simple-im"
]
},
"SimpleIm": {
"description": "Describes the settings of the simple, XCompose based input method.\n\n- Example:\n\n ```toml\n [simple-im]\n enabled = false\n ```\n",
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Whether the input method is enabled.\n\nEven if the input method is enabled, it will only be used if there is no\nrunning external IM.\n"
}
},
"required": []
},
"Status": {
"description": "The configuration of a status program whose output will be shown in the bar.\n\n- Example:\n\n ```toml\n [status]\n format = \"i3bar\"\n exec = \"i3status\"\n ```\n",
"type": "object",

View file

@ -538,7 +538,7 @@ This table is a tagged union. The variant is determined by the `type` field. It
- `set-log-level`:
Sets the log level of the compositor..
Sets the log level of the compositor.
- Example:
@ -2145,6 +2145,24 @@ The table has the following fields:
The value of this field should be a [WorkspaceDisplayOrder](#types-WorkspaceDisplayOrder).
- `simple-im` (optional):
Configures the simple, XCompose based input method.
By default, the input method is enabled.
Even if the input method is enabled, it will only be used if there is no
running external IM.
- Example:
```toml
[simple-im]
enabled = false
```
The value of this field should be a [SimpleIm](#types-SimpleIm).
<a name="types-Connector"></a>
### `Connector`
@ -4177,6 +4195,53 @@ The string should have one of the following values:
Pops the topmost mode from the input-mode stack.
- `enable-simple-im`:
Enables the simple, XCompose based input method.
Even if the input method is enabled, it will only be used if there is no
running external IM.
- `disable-simple-im`:
Disables the simple, XCompose based input method.
- `toggle-simple-im-enabled`:
Toggles whether the simple, XCompose based input method is enabled.
- `reload-simple-im`:
Reloads the simple, XCompose based input method.
This is useful if you change the XCompose files after starting the compositor.
<a name="types-SimpleIm"></a>
### `SimpleIm`
Describes the settings of the simple, XCompose based input method.
- Example:
```toml
[simple-im]
enabled = false
```
Values of this type should be tables.
The table has the following fields:
- `enabled` (optional):
Whether the input method is enabled.
Even if the input method is enabled, it will only be used if there is no
running external IM.
The value of this field should be a boolean.
<a name="types-Status"></a>

View file

@ -497,7 +497,7 @@ Action:
ref: Theme
set-log-level:
description: |
Sets the log level of the compositor..
Sets the log level of the compositor.
- Example:
@ -1033,6 +1033,23 @@ SimpleActionName:
description: Disables all previously set input modes, clearing the input-mode stack.
- value: pop-mode
description: Pops the topmost mode from the input-mode stack.
- value: enable-simple-im
description: |
Enables the simple, XCompose based input method.
Even if the input method is enabled, it will only be used if there is no
running external IM.
- value: disable-simple-im
description: |
Disables the simple, XCompose based input method.
- value: toggle-simple-im-enabled
description: |
Toggles whether the simple, XCompose based input method is enabled.
- value: reload-simple-im
description: |
Reloads the simple, XCompose based input method.
This is useful if you change the XCompose files after starting the compositor.
Color:
@ -2881,6 +2898,23 @@ Config:
```toml
workspace-display-order = "sorted"
```
simple-im:
ref: SimpleIm
required: false
description: |
Configures the simple, XCompose based input method.
By default, the input method is enabled.
Even if the input method is enabled, it will only be used if there is no
running external IM.
- Example:
```toml
[simple-im]
enabled = false
```
Idle:
@ -4195,3 +4229,25 @@ ClientCapabilities:
description: An array of masks that are OR'd.
items:
ref: ClientCapabilities
SimpleIm:
kind: table
description: |
Describes the settings of the simple, XCompose based input method.
- Example:
```toml
[simple-im]
enabled = false
```
fields:
enabled:
kind: boolean
required: false
description: |
Whether the input method is enabled.
Even if the input method is enabled, it will only be used if there is no
running external IM.