ltk_webkit/wpe/
input_method.rsuse std::ffi::{ c_char, c_int, c_uint, CString };
use std::mem::size_of;
use std::ptr;
use std::sync::{ Mutex, OnceLock, Weak };
use glib_sys::{ gpointer, GType };
use gobject_sys::
{
g_object_get_data, g_object_new, g_object_set_data_full, g_object_unref,
g_signal_emit_by_name, g_type_register_static_simple, GObject,
};
use wpe_webkit_sys::
{
webkit_input_method_context_get_type, webkit_web_view_set_input_method_context,
WebKitInputMethodContext, WebKitInputMethodContextClass, WebKitWebView,
};
use super::{ Inner, WebView };
const INNER_KEY: &[ u8 ] = b"ltk-webkit-im-inner\0";
impl WebView
{
pub fn editable_focused( &self ) -> bool
{
self.inner.lock().map( |g| g.editable_focused ).unwrap_or( false )
}
pub fn commit_text( &self, text: &str )
{
let Some( context ) = self.im_context() else { return };
let Ok( text_c ) = CString::new( text ) else { return };
unsafe { g_signal_emit_by_name( context as *mut GObject, b"committed\0".as_ptr() as *const c_char, text_c.as_ptr() ); }
}
pub fn delete_surrounding_text( &self, before: u32, after: u32 )
{
let Some( context ) = self.im_context() else { return };
let offset = -( before as c_int );
let count = ( before + after ) as c_uint;
unsafe { g_signal_emit_by_name( context as *mut GObject, b"delete-surrounding\0".as_ptr() as *const c_char, offset, count ); }
}
fn im_context( &self ) -> Option<*mut WebKitInputMethodContext>
{
let context = self.inner.lock().ok()?.im_context;
if context.is_null() { None } else { Some( context ) }
}
}
pub( crate ) unsafe fn install( webview: *mut WebKitWebView, inner: Weak<Mutex<Inner>> ) -> *mut WebKitInputMethodContext
{
unsafe
{
let context = g_object_new( context_type(), ptr::null::<c_char>() ) as *mut WebKitInputMethodContext;
if context.is_null() { return ptr::null_mut(); }
let data = Box::into_raw( Box::new( inner ) ) as gpointer;
g_object_set_data_full( context as *mut GObject, INNER_KEY.as_ptr() as *const c_char, data, Some( drop_weak ) );
webkit_web_view_set_input_method_context( webview, context );
g_object_unref( context as *mut GObject );
context
}
}
fn context_type() -> GType
{
static TYPE: OnceLock<GType> = OnceLock::new();
*TYPE.get_or_init( || unsafe
{
g_type_register_static_simple(
webkit_input_method_context_get_type(),
b"LtkWebkitInputMethodContext\0".as_ptr() as *const c_char,
size_of::<WebKitInputMethodContextClass>() as c_uint,
Some( class_init ),
size_of::<WebKitInputMethodContext>() as c_uint,
None,
0,
)
} )
}
unsafe extern "C" fn class_init( class: gpointer, _data: gpointer )
{
let class = class as *mut WebKitInputMethodContextClass;
unsafe
{
( *class ).notify_focus_in = Some( on_focus_in );
( *class ).notify_focus_out = Some( on_focus_out );
}
}
unsafe extern "C" fn on_focus_in( context: *mut WebKitInputMethodContext )
{
unsafe { set_editable_focused( context, true ); }
}
unsafe extern "C" fn on_focus_out( context: *mut WebKitInputMethodContext )
{
unsafe { set_editable_focused( context, false ); }
}
unsafe fn set_editable_focused( context: *mut WebKitInputMethodContext, focused: bool )
{
let raw = unsafe { g_object_get_data( context as *mut GObject, INNER_KEY.as_ptr() as *const c_char ) };
if raw.is_null() { return; }
let Some( inner ) = unsafe { &*( raw as *const Weak<Mutex<Inner>> ) }.upgrade() else { return };
if let Ok( mut g ) = inner.lock() { g.editable_focused = focused; };
}
unsafe extern "C" fn drop_weak( data: gpointer )
{
if data.is_null() { return; }
drop( unsafe { Box::from_raw( data as *mut Weak<Mutex<Inner>> ) } );
}