mirror of
https://github.com/italicsjenga/rust_minifb.git
synced 2024-12-23 19:31: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]
|
[features]
|
||||||
default = ["wayland", "x11"]
|
default = ["wayland", "x11"]
|
||||||
x11 = ["x11-dl", "xkb"]
|
x11 = ["x11-dl", "xkb", "libc"]
|
||||||
wayland = ["wayland-client", "wayland-protocols", "wayland-cursor", "tempfile", "xkb", "xkbcommon-sys"]
|
wayland = ["wayland-client", "wayland-protocols", "wayland-cursor", "tempfile", "xkb", "xkbcommon-sys"]
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "macos", target_os = "redox", windows)))'.dependencies]
|
[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}
|
tempfile = {version = "3.2", optional = true}
|
||||||
xkb = {version = "0.2.1", optional = true}
|
xkb = {version = "0.2.1", optional = true}
|
||||||
xkbcommon-sys = {version = "0.7.5", 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]
|
[target.x86_64-unknown-redox.dependencies]
|
||||||
orbclient = "0.3.20"
|
orbclient = "0.3.20"
|
||||||
|
|
|
@ -33,7 +33,7 @@ fn main() {
|
||||||
WindowOptions::default(),
|
WindowOptions::default(),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
panic!("{}", e);
|
panic!("{:?}", e);
|
||||||
});
|
});
|
||||||
|
|
||||||
let keys_data = KeyVec::new(RefCell::new(Vec::new()));
|
let keys_data = KeyVec::new(RefCell::new(Vec::new()));
|
||||||
|
@ -55,7 +55,7 @@ fn main() {
|
||||||
let mut keys = keys_data.borrow_mut();
|
let mut keys = keys_data.borrow_mut();
|
||||||
|
|
||||||
for t in keys.iter() {
|
for t in keys.iter() {
|
||||||
println!("keys {}", t);
|
println!("Code point: {}, Character: {:?}", *t, char::from_u32(*t));
|
||||||
}
|
}
|
||||||
|
|
||||||
keys.clear();
|
keys.clear();
|
||||||
|
|
|
@ -12,16 +12,20 @@ use crate::Result;
|
||||||
use crate::{CursorStyle, MenuHandle, UnixMenu};
|
use crate::{CursorStyle, MenuHandle, UnixMenu};
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::ffi::CString;
|
use std::ffi::{c_void, CStr, CString};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw;
|
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 std::ptr;
|
||||||
|
|
||||||
use crate::buffer_helper;
|
use crate::buffer_helper;
|
||||||
use crate::mouse_handler;
|
use crate::mouse_handler;
|
||||||
|
|
||||||
use super::common::Menu;
|
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
|
// NOTE: the x11-dl crate does not define Button6 or Button7
|
||||||
const Button6: c_uint = xlib::Button5 + 1;
|
const Button6: c_uint = xlib::Button5 + 1;
|
||||||
|
@ -113,6 +117,8 @@ impl DisplayInfo {
|
||||||
|
|
||||||
fn setup(transparency: bool) -> Result<DisplayInfo> {
|
fn setup(transparency: bool) -> Result<DisplayInfo> {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
libc::setlocale(libc::LC_ALL, "\0".as_ptr() as *const c_char); //needed to make compose key work
|
||||||
|
|
||||||
let lib = xlib::Xlib::open()
|
let lib = xlib::Xlib::open()
|
||||||
.map_err(|e| Error::WindowCreate(format!("failed to load Xlib: {:?}", e)))?;
|
.map_err(|e| Error::WindowCreate(format!("failed to load Xlib: {:?}", e)))?;
|
||||||
|
|
||||||
|
@ -286,6 +292,9 @@ pub struct Window {
|
||||||
d: DisplayInfo,
|
d: DisplayInfo,
|
||||||
|
|
||||||
handle: xlib::Window,
|
handle: xlib::Window,
|
||||||
|
xim: XIM,
|
||||||
|
xic: XIC,
|
||||||
|
|
||||||
ximage: *mut xlib::XImage,
|
ximage: *mut xlib::XImage,
|
||||||
draw_buffer: Vec<u32>,
|
draw_buffer: Vec<u32>,
|
||||||
|
|
||||||
|
@ -384,6 +393,43 @@ impl Window {
|
||||||
&mut attributes,
|
&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());
|
d.gc = (d.lib.XCreateGC)(d.display, handle, 0, ptr::null_mut());
|
||||||
|
|
||||||
if handle == 0 {
|
if handle == 0 {
|
||||||
|
@ -461,6 +507,8 @@ impl Window {
|
||||||
Ok(Window {
|
Ok(Window {
|
||||||
d,
|
d,
|
||||||
handle,
|
handle,
|
||||||
|
xim,
|
||||||
|
xic,
|
||||||
ximage,
|
ximage,
|
||||||
draw_buffer,
|
draw_buffer,
|
||||||
width: width as u32,
|
width: width as u32,
|
||||||
|
@ -904,6 +952,12 @@ impl Window {
|
||||||
|
|
||||||
(self.d.lib.XNextEvent)(self.d.display, &mut event);
|
(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
|
// Don't process any more messages if we hit a termination event
|
||||||
if self.raw_process_one_event(event) == ProcessEventResult::Termination {
|
if self.raw_process_one_event(event) == ProcessEventResult::Termination {
|
||||||
return;
|
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!
|
// FIXME: we cannot handle multiple windows here!
|
||||||
if ev.any.window != self.handle {
|
if ev.any.window != self.handle {
|
||||||
return ProcessEventResult::Ok;
|
return ProcessEventResult::Ok;
|
||||||
|
@ -930,6 +984,7 @@ impl Window {
|
||||||
|
|
||||||
xlib::KeyPress => {
|
xlib::KeyPress => {
|
||||||
self.process_key(ev, true /* is_down */);
|
self.process_key(ev, true /* is_down */);
|
||||||
|
self.emit_code_point_chars_to_callback(&mut ev.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
xlib::KeyRelease => {
|
xlib::KeyRelease => {
|
||||||
|
@ -1008,24 +1063,34 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_key_state(sym, is_down);
|
self.update_key_state(sym, is_down);
|
||||||
|
|
||||||
// unicode callback...
|
|
||||||
|
|
||||||
if !is_down {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let keysym = xkb::Keysym(sym as u32);
|
fn emit_code_point_chars_to_callback(&mut self, event: &mut XKeyEvent) {
|
||||||
let code_point = keysym.utf32();
|
const BUFFER_SIZE: usize = 32;
|
||||||
// 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 {
|
if let Some(callback) = &mut self.key_handler.key_callback {
|
||||||
callback.add_char(code_point);
|
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)]
|
||||||
|
};
|
||||||
|
|
||||||
|
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 ]
|
// probably pointless ]
|
||||||
// XSaveContext(s_display, info->window, s_context, (XPointer)0);
|
// 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);
|
(self.d.lib.XDestroyWindow)(self.d.display, self.handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue