mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-23 11:21:30 +11:00
Support typed keys on x11
* Added not-yet-working draft implementation of proper X11 support for reading correct typed characters * Made shift work for capital letters with X11, but compose key is still broken * Compose key and numpad now work correctly in X11 * XIM and XIC are now freed when a window destructor runs * Ran cargo fmt on x11.rs * Removed commented-out empty-string Closes #200
This commit is contained in:
parent
05cb97aeb0
commit
2b2540067c
|
@ -39,7 +39,7 @@ features = [
|
|||
|
||||
[features]
|
||||
default = ["wayland", "x11"]
|
||||
x11 = ["x11-dl", "xkb"]
|
||||
x11 = ["x11-dl", "xkb", "libc"]
|
||||
wayland = ["wayland-client", "wayland-protocols", "wayland-cursor", "tempfile", "xkb", "xkbcommon-sys"]
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "redox", windows)))'.dependencies]
|
||||
|
@ -49,7 +49,8 @@ wayland-cursor = {version = "0.28", optional = true}
|
|||
tempfile = {version = "3.2", optional = true}
|
||||
xkb = {version = "0.2.1", optional = true}
|
||||
xkbcommon-sys = {version = "0.7.5", optional = true}
|
||||
x11-dl = {version = "2.18.3", optional = true}
|
||||
x11-dl = {version = "2.19.1", optional = true}
|
||||
libc = {version = "0.2.107", optional = true}
|
||||
|
||||
[target.x86_64-unknown-redox.dependencies]
|
||||
orbclient = "0.3.20"
|
||||
|
|
|
@ -33,7 +33,7 @@ fn main() {
|
|||
WindowOptions::default(),
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("{}", e);
|
||||
panic!("{:?}", e);
|
||||
});
|
||||
|
||||
let keys_data = KeyVec::new(RefCell::new(Vec::new()));
|
||||
|
@ -55,7 +55,7 @@ fn main() {
|
|||
let mut keys = keys_data.borrow_mut();
|
||||
|
||||
for t in keys.iter() {
|
||||
println!("keys {}", t);
|
||||
println!("Code point: {}, Character: {:?}", *t, char::from_u32(*t));
|
||||
}
|
||||
|
||||
keys.clear();
|
||||
|
|
|
@ -12,16 +12,20 @@ use crate::Result;
|
|||
use crate::{CursorStyle, MenuHandle, UnixMenu};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::CString;
|
||||
use std::ffi::{c_void, CStr, CString};
|
||||
use std::mem;
|
||||
use std::os::raw;
|
||||
use std::os::raw::{c_char, c_long, c_uchar, c_uint, c_ulong};
|
||||
use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong};
|
||||
use std::ptr;
|
||||
|
||||
use crate::buffer_helper;
|
||||
use crate::mouse_handler;
|
||||
|
||||
use super::common::Menu;
|
||||
use x11_dl::xlib::{
|
||||
KeyPressMask, KeyReleaseMask, KeySym, Status, XEvent, XIMPreeditNothing, XIMStatusNothing,
|
||||
XKeyEvent, XNClientWindow, XNFocusWindow, XNInputStyle, XrmDatabase, XIC, XIM,
|
||||
};
|
||||
|
||||
// NOTE: the x11-dl crate does not define Button6 or Button7
|
||||
const Button6: c_uint = xlib::Button5 + 1;
|
||||
|
@ -113,6 +117,8 @@ impl DisplayInfo {
|
|||
|
||||
fn setup(transparency: bool) -> Result<DisplayInfo> {
|
||||
unsafe {
|
||||
libc::setlocale(libc::LC_ALL, "\0".as_ptr() as *const c_char); //needed to make compose key work
|
||||
|
||||
let lib = xlib::Xlib::open()
|
||||
.map_err(|e| Error::WindowCreate(format!("failed to load Xlib: {:?}", e)))?;
|
||||
|
||||
|
@ -286,6 +292,9 @@ pub struct Window {
|
|||
d: DisplayInfo,
|
||||
|
||||
handle: xlib::Window,
|
||||
xim: XIM,
|
||||
xic: XIC,
|
||||
|
||||
ximage: *mut xlib::XImage,
|
||||
draw_buffer: Vec<u32>,
|
||||
|
||||
|
@ -384,6 +393,43 @@ impl Window {
|
|||
&mut attributes,
|
||||
);
|
||||
|
||||
let empty_string = b"\0";
|
||||
(d.lib.XSetLocaleModifiers)(empty_string.as_ptr() as *const i8);
|
||||
|
||||
let xim = (d.lib.XOpenIM)(
|
||||
d.display,
|
||||
0 as XrmDatabase,
|
||||
0 as *mut c_char,
|
||||
0 as *mut c_char,
|
||||
);
|
||||
if (xim as usize) == 0 {
|
||||
return Err(Error::WindowCreate(
|
||||
"Failed to setup X IM via XOpenIM.".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
let xn_input_style = CString::new(XNInputStyle).unwrap();
|
||||
let xn_client_window = CString::new(XNClientWindow).unwrap();
|
||||
let xn_focus_window = CString::new(XNFocusWindow).unwrap();
|
||||
let xic = (d.lib.XCreateIC)(
|
||||
xim,
|
||||
xn_input_style.as_ptr(),
|
||||
XIMPreeditNothing | XIMStatusNothing,
|
||||
xn_client_window.as_ptr(),
|
||||
handle as c_ulong,
|
||||
xn_focus_window.as_ptr(),
|
||||
handle as c_ulong,
|
||||
std::ptr::null_mut::<c_void>(),
|
||||
);
|
||||
if (xic as usize) == 0 {
|
||||
return Err(Error::WindowCreate(
|
||||
"Failed to setup X IC via XCreateIC.".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
(d.lib.XSetICFocus)(xic);
|
||||
(d.lib.XSelectInput)(d.display, handle, KeyPressMask | KeyReleaseMask);
|
||||
|
||||
d.gc = (d.lib.XCreateGC)(d.display, handle, 0, ptr::null_mut());
|
||||
|
||||
if handle == 0 {
|
||||
|
@ -461,6 +507,8 @@ impl Window {
|
|||
Ok(Window {
|
||||
d,
|
||||
handle,
|
||||
xim,
|
||||
xic,
|
||||
ximage,
|
||||
draw_buffer,
|
||||
width: width as u32,
|
||||
|
@ -904,6 +952,12 @@ impl Window {
|
|||
|
||||
(self.d.lib.XNextEvent)(self.d.display, &mut event);
|
||||
|
||||
//skip any events that need to get eaten by X to do compose key, e.g. if the user types compose key + a + ' then all of these events need to get eaten and processed in xlib
|
||||
//XFilterEvent will do the processing for these cases, and returns whether or not it handled an event
|
||||
if (self.d.lib.XFilterEvent)(&mut event as *mut XEvent, 0) != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't process any more messages if we hit a termination event
|
||||
if self.raw_process_one_event(event) == ProcessEventResult::Termination {
|
||||
return;
|
||||
|
@ -911,7 +965,7 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn raw_process_one_event(&mut self, ev: xlib::XEvent) -> ProcessEventResult {
|
||||
unsafe fn raw_process_one_event(&mut self, mut ev: xlib::XEvent) -> ProcessEventResult {
|
||||
// FIXME: we cannot handle multiple windows here!
|
||||
if ev.any.window != self.handle {
|
||||
return ProcessEventResult::Ok;
|
||||
|
@ -930,6 +984,7 @@ impl Window {
|
|||
|
||||
xlib::KeyPress => {
|
||||
self.process_key(ev, true /* is_down */);
|
||||
self.emit_code_point_chars_to_callback(&mut ev.key);
|
||||
}
|
||||
|
||||
xlib::KeyRelease => {
|
||||
|
@ -1008,24 +1063,34 @@ impl Window {
|
|||
}
|
||||
|
||||
self.update_key_state(sym, is_down);
|
||||
}
|
||||
|
||||
// unicode callback...
|
||||
fn emit_code_point_chars_to_callback(&mut self, event: &mut XKeyEvent) {
|
||||
const BUFFER_SIZE: usize = 32;
|
||||
|
||||
if !is_down {
|
||||
return;
|
||||
}
|
||||
if let Some(callback) = &mut self.key_handler.key_callback {
|
||||
let mut buff: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
let str = unsafe {
|
||||
let mut keysym: KeySym = std::mem::zeroed();
|
||||
let mut status: Status = 0;
|
||||
let length_in_bytes = (self.d.lib.Xutf8LookupString)(
|
||||
self.xic,
|
||||
event as *mut XKeyEvent,
|
||||
buff.as_mut_ptr() as *mut c_char,
|
||||
(BUFFER_SIZE - 1) as c_int,
|
||||
(&mut keysym) as *mut KeySym,
|
||||
(&mut status) as *mut Status,
|
||||
);
|
||||
&buff[0..(length_in_bytes as usize + 1)]
|
||||
};
|
||||
|
||||
let keysym = xkb::Keysym(sym as u32);
|
||||
let code_point = keysym.utf32();
|
||||
// Taken from GLFW
|
||||
if code_point == 0 {
|
||||
return;
|
||||
} else if code_point < 32 || (code_point > 126 && code_point < 160) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref mut callback) = self.key_handler.key_callback {
|
||||
callback.add_char(code_point);
|
||||
if let Ok(cstr) = CStr::from_bytes_with_nul(str) {
|
||||
if let Ok(str) = cstr.to_str() {
|
||||
for c in str.chars() {
|
||||
callback.add_char(c as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1199,6 +1264,8 @@ impl Drop for Window {
|
|||
// probably pointless ]
|
||||
// XSaveContext(s_display, info->window, s_context, (XPointer)0);
|
||||
|
||||
(self.d.lib.XDestroyIC)(self.xic);
|
||||
(self.d.lib.XCloseIM)(self.xim);
|
||||
(self.d.lib.XDestroyWindow)(self.d.display, self.handle);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue