use std::error::Error;
use std::future::Future;
use std::marker::Send;
use pm_bin::log::info;
use xkeysym::{KeyCode, Keysym};
use zbus::{
Connection, ObjectServer, fdo, interface, object_server::SignalEmitter, zvariant::Value,
};
use super::{IBusModifierState, LookupTable, ibus_serde::make_ibus_text};
pub trait IBusEngine: Send + Sync {
fn process_key_event(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
_keyval: Keysym,
_keycode: KeyCode,
_state: IBusModifierState,
) -> impl Future<Output = fdo::Result<bool>> + Send {
async { Ok(false) }
}
fn set_cursor_location(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
_x: i32,
_y: i32,
_w: i32,
_h: i32,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn focus_in(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn focus_out(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn reset(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn enable(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn disable(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn candidate_clicked(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
_index: u32,
_button: u32,
_state: u32,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn page_up(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn page_down(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn cursor_up(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
fn cursor_down(
&mut self,
_se: SignalEmitter<'_>,
_server: &ObjectServer,
) -> impl Future<Output = fdo::Result<()>> + Send {
async { Ok(()) }
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum IBusPreeditFocusMode {
Clear,
Commit,
}
impl From<IBusPreeditFocusMode> for u32 {
fn from(value: IBusPreeditFocusMode) -> Self {
match value {
IBusPreeditFocusMode::Clear => 0,
IBusPreeditFocusMode::Commit => 1,
}
}
}
pub trait IBusEngineBackend: IBusEngine + 'static {
fn commit_text(
se: &SignalEmitter<'_>,
text: String,
) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
fn update_lookup_table(
se: &SignalEmitter<'_>,
table: &LookupTable,
visible: bool,
) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
fn update_preedit_text(
se: &SignalEmitter<'_>,
text: String,
cursor_pos: u32,
visible: bool,
mode: IBusPreeditFocusMode,
) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
fn update_auxiliary_text(
se: &SignalEmitter<'_>,
text: String,
visible: bool,
) -> impl std::future::Future<Output = zbus::Result<()>> + Send;
}
impl<T: IBusEngine + 'static> IBusEngineBackend for T {
async fn commit_text(se: &SignalEmitter<'_>, text: String) -> zbus::Result<()> {
Engine::<Self>::commit_text(se, make_ibus_text(text)).await
}
async fn update_lookup_table(
se: &SignalEmitter<'_>,
table: &LookupTable,
visible: bool,
) -> zbus::Result<()> {
Engine::<Self>::update_lookup_table(se, table.serialize(), visible).await
}
async fn update_preedit_text(
se: &SignalEmitter<'_>,
text: String,
cursor_pos: u32,
visible: bool,
mode: IBusPreeditFocusMode,
) -> zbus::Result<()> {
Engine::<Self>::update_preedit_text(
se,
make_ibus_text(text),
cursor_pos,
visible,
mode.into(),
)
.await
}
async fn update_auxiliary_text(
se: &SignalEmitter<'_>,
text: String,
visible: bool,
) -> zbus::Result<()> {
Engine::<Self>::update_auxiliary_text(se, make_ibus_text(text), visible).await
}
}
#[derive(Debug, Clone)]
pub(crate) struct Engine<T: IBusEngine + 'static> {
e: T,
_op: String,
}
#[interface(name = "org.freedesktop.IBus.Engine")]
impl<T: IBusEngine + 'static> Engine<T> {
async fn process_key_event(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
keyval: u32,
keycode: u32,
state: u32,
) -> fdo::Result<bool> {
self.e
.process_key_event(
se,
server,
keyval.into(),
keycode.into(),
IBusModifierState::new_with_raw_value(state),
)
.await
}
async fn set_cursor_location(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
x: i32,
y: i32,
w: i32,
h: i32,
) -> fdo::Result<()> {
self.e.set_cursor_location(se, server, x, y, w, h).await
}
fn process_hand_writing_event(&mut self, _coordinates: Vec<f64>) -> fdo::Result<()> {
Ok(())
}
fn cancel_hand_writing(&mut self, _n_strokes: u32) -> fdo::Result<()> {
Ok(())
}
fn set_capabilities(&mut self, _caps: u32) -> fdo::Result<()> {
Ok(())
}
fn property_activate(&mut self, _name: String, _state: u32) -> fdo::Result<()> {
Ok(())
}
fn property_show(&mut self, _name: String) -> fdo::Result<()> {
Ok(())
}
fn property_hide(&mut self, _name: String) -> fdo::Result<()> {
Ok(())
}
async fn candidate_clicked(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
index: u32,
button: u32,
state: u32,
) -> fdo::Result<()> {
self.e
.candidate_clicked(se, server, index, button, state)
.await
}
async fn focus_in(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.focus_in(se, server).await
}
fn focus_in_id(&mut self, _object_path: String, _client: String) -> fdo::Result<()> {
Ok(())
}
async fn focus_out(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.focus_out(se, server).await
}
fn focus_out_id(&mut self, _object_path: String) -> fdo::Result<()> {
Ok(())
}
async fn reset(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.reset(se, server).await
}
async fn enable(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.enable(se, server).await
}
async fn disable(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.disable(se, server).await
}
async fn page_up(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.page_up(se, server).await
}
async fn page_down(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.page_down(se, server).await
}
async fn cursor_up(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.cursor_up(se, server).await
}
async fn cursor_down(
&mut self,
#[zbus(signal_emitter)] se: SignalEmitter<'_>,
#[zbus(object_server)] server: &ObjectServer,
) -> fdo::Result<()> {
self.e.cursor_down(se, server).await
}
fn set_surrounding_text(
&mut self,
_text: Value,
_cursor_pos: u32,
_anchor_pos: u32,
) -> fdo::Result<()> {
Ok(())
}
fn panel_extension_received(&mut self, _event: Value) -> fdo::Result<()> {
Ok(())
}
fn panel_extension_register_keys(&mut self, _data: Value) -> fdo::Result<()> {
Ok(())
}
#[zbus(signal)]
async fn commit_text(se: &SignalEmitter<'_>, text: Value<'_>) -> zbus::Result<()>;
#[zbus(signal)]
async fn update_preedit_text(
se: &SignalEmitter<'_>,
text: Value<'_>,
cursor_pos: u32,
visible: bool,
mode: u32,
) -> zbus::Result<()>;
#[zbus(signal)]
async fn update_auxiliary_text(
se: &SignalEmitter<'_>,
text: Value<'_>,
visible: bool,
) -> zbus::Result<()>;
#[zbus(signal)]
async fn update_lookup_table(
se: &SignalEmitter<'_>,
table: Value<'_>,
visible: bool,
) -> zbus::Result<()>;
#[zbus(signal)]
async fn register_properties(se: &SignalEmitter<'_>, props: Value<'_>) -> zbus::Result<()>;
#[zbus(signal)]
async fn update_property(se: &SignalEmitter<'_>, prop: Value<'_>) -> zbus::Result<()>;
#[zbus(signal)]
pub async fn forward_key_event(
se: &SignalEmitter<'_>,
keyval: u32,
keycode: u32,
state: u32,
) -> zbus::Result<()>;
#[zbus(signal)]
async fn panel_extension(se: &SignalEmitter<'_>, data: Value<'_>) -> zbus::Result<()>;
#[zbus(property)]
fn content_type(&self) -> (u32, u32) {
(0, 0)
}
#[zbus(property)]
fn set_content_type(&mut self, _t: (u32, u32)) -> fdo::Result<()> {
Ok(())
}
#[zbus(property)]
fn focus_id(&self) -> bool {
false
}
#[zbus(property)]
fn active_surrounding_text(&self) -> bool {
false
}
}
impl<T: IBusEngine + 'static> Engine<T> {
pub async fn new(c: &Connection, e: T) -> Result<String, Box<dyn Error>> {
let object_path = format!("/org/freedesktop/IBus/Engine/{}", 1);
let o = Engine {
e,
_op: object_path.clone(),
};
c.object_server().at(object_path.clone(), o).await?;
info!("创建 engine 成功: {}", object_path);
Ok(object_path)
}
}