1
0
Fork 0

Remove context creation code. Add mpsc Message channel.

This commit is contained in:
Billy Messenger 2020-09-03 10:17:54 -05:00
parent 90a25ea988
commit 2934a4c1fb
8 changed files with 277 additions and 500 deletions

View file

@ -10,30 +10,18 @@ authors = [
] ]
edition = "2018" edition = "2018"
[features]
default = ["gl_renderer"]
gl_renderer = ["gl"]
wgpu_renderer = ["wgpu"]
[dependencies] [dependencies]
log = "0.4.8" log = "0.4.11"
raw-window-handle = "0.3.3"
[dependencies.gl]
gl = "0.14"
optional = true
[dependencies.wgpu]
wgpu = "0.6"
optional = true
[target.'cfg(target_os="linux")'.dependencies] [target.'cfg(target_os="linux")'.dependencies]
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] } xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
x11 = { version = "2.18", features = ["xlib", "glx"] } x11 = { version = "2.18", features = ["xlib"] }
libc = "0.2" libc = "0.2"
[target.'cfg(target_os="windows")'.dependencies] [target.'cfg(target_os="windows")'.dependencies]
winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi"] } winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi"] }
[target.'cfg(target_os="macos")'.dependencies] [target.'cfg(target_os="macos")'.dependencies]
cocoa = "0.20.1" cocoa = "0.23.0"
objc = "0.2.7" objc = "0.2.7"

View file

@ -1,3 +1,5 @@
use std::sync::mpsc;
use baseview::Message; use baseview::Message;
fn main() { fn main() {
@ -8,15 +10,22 @@ fn main() {
parent: baseview::Parent::None, parent: baseview::Parent::None,
}; };
let (message_tx, message_rx) = mpsc::channel::<Message>();
let my_program = MyProgram {}; let my_program = MyProgram {};
baseview::Window::open(window_open_options, my_program); let window = baseview::Window::build(window_open_options, message_tx);
}
// Get raw window handle!
let _raw_handle = window.raw_window_handle();
my_program_loop(my_program, message_rx);
}
struct MyProgram {} struct MyProgram {}
impl baseview::Receiver for MyProgram { fn my_program_loop(_my_program: MyProgram, message_rx: mpsc::Receiver<Message>) {
fn on_message(&mut self, message: Message) { loop {
let message = message_rx.recv().unwrap();
match message { match message {
Message::CursorMotion(x, y) => { Message::CursorMotion(x, y) => {
println!("Cursor moved, x: {}, y: {}", x, y); println!("Cursor moved, x: {}, y: {}", x, y);

View file

@ -32,7 +32,3 @@ pub struct WindowOpenOptions<'a> {
pub parent: Parent, pub parent: Parent,
} }
pub trait Receiver {
fn on_message(&mut self, message: Message);
}

View file

@ -8,12 +8,10 @@ use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString};
use crate::{Message, MouseButtonID, MouseScroll, Receiver, WindowOpenOptions}; use crate::{Message, MouseButtonID, MouseScroll, Receiver, WindowOpenOptions};
pub struct Window<R: Receiver> { pub struct Window {}
receiver: R,
}
impl<R: Receiver> Window<R> { impl Window {
pub fn open(options: WindowOpenOptions, receiver: R) -> Self { pub fn open(options: WindowOpenOptions, message_tx: mpsc::Sender<Message>) -> Self {
unsafe { unsafe {
let _pool = NSAutoreleasePool::new(nil); let _pool = NSAutoreleasePool::new(nil);
@ -44,11 +42,13 @@ impl<R: Receiver> Window<R> {
current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps); current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
app.run(); app.run();
receiver.on_message(Message::Opened(crate::message::WindowInfo { message_tx
width: options.width as u32, .send(Message::Opened(WindowInfo {
height: options.height as u32, width: options.width as u32,
dpi: None, height: options.height as u32,
})); dpi: None,
}))
.unwrap();
Window { receiver } Window { receiver }
} }

View file

@ -2,6 +2,7 @@ extern crate winapi;
use std::ffi::CString; use std::ffi::CString;
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::mpsc;
use self::winapi::shared::guiddef::GUID; use self::winapi::shared::guiddef::GUID;
use self::winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM}; use self::winapi::shared::minwindef::{ATOM, FALSE, LPARAM, LRESULT, UINT, WPARAM};
@ -112,20 +113,19 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
unsafe fn init_gl_context() {} unsafe fn init_gl_context() {}
pub struct Window<R: Receiver> { pub struct Window {
pub(crate) hwnd: HWND, pub(crate) hwnd: HWND,
hdc: HDC, hdc: HDC,
gl_context: HGLRC, gl_context: HGLRC,
window_class: ATOM, window_class: ATOM,
receiver: R,
scaling: Option<f64>, // DPI scale, 96.0 is "default". scaling: Option<f64>, // DPI scale, 96.0 is "default".
r: f32, r: f32,
g: f32, g: f32,
b: f32, b: f32,
} }
impl<R: Receiver> Window<R> { impl Window {
pub fn open(options: WindowOpenOptions, receiver: R) { pub fn open(options: WindowOpenOptions, message_tx: mpsc::Sender<Message>) {
unsafe { unsafe {
let mut window = Window { let mut window = Window {
hwnd: null_mut(), hwnd: null_mut(),
@ -241,13 +241,13 @@ impl<R: Receiver> Window<R> {
SetTimer(hwnd, 4242, 13, None); SetTimer(hwnd, 4242, 13, None);
window message_tx
.receiver .send(Message::Opened(WindowInfo {
.on_message(Message::Opened(crate::message::WindowInfo {
width: options.width as u32, width: options.width as u32,
height: options.height as u32, height: options.height as u32,
dpi: window.scaling, dpi: None,
})); }))
.unwrap();
// todo: decide what to do with the message pump // todo: decide what to do with the message pump
if parent.is_null() { if parent.is_null() {

View file

@ -3,6 +3,3 @@ use xcb_connection::XcbConnection;
mod window; mod window;
pub use window::*; pub use window::*;
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
mod opengl_util;

View file

@ -1,182 +0,0 @@
use std::ffi::{CStr, CString};
use std::os::raw::{c_int, c_void};
use std::ptr::null_mut;
use ::x11::{glx, xlib};
use super::XcbConnection;
pub fn fb_config(xcb_connection: &XcbConnection) -> *mut glx::__GLXFBConfigRec {
// Check GLX version (>= 1.3 needed)
check_glx_version(&xcb_connection);
// Get GLX framebuffer config (requires GLX >= 1.3)
#[rustfmt::skip]
let fb_config = get_glxfbconfig(
&xcb_connection,
&[
glx::GLX_X_RENDERABLE, 1,
glx::GLX_DRAWABLE_TYPE, glx::GLX_WINDOW_BIT,
glx::GLX_RENDER_TYPE, glx::GLX_RGBA_BIT,
glx::GLX_X_VISUAL_TYPE, glx::GLX_TRUE_COLOR,
glx::GLX_RED_SIZE, 8,
glx::GLX_GREEN_SIZE, 8,
glx::GLX_BLUE_SIZE, 8,
glx::GLX_ALPHA_SIZE, 8,
glx::GLX_DEPTH_SIZE, 24,
glx::GLX_STENCIL_SIZE, 8,
glx::GLX_DOUBLEBUFFER, 1,
0
],
);
fb_config
}
pub fn x_visual_info(
xcb_connection: &XcbConnection,
fb_config: *mut glx::__GLXFBConfigRec,
) -> *const xlib::XVisualInfo {
// The GLX framebuffer config holds an XVisualInfo, which we'll need for other X operations.
unsafe { glx::glXGetVisualFromFBConfig(xcb_connection.conn.get_raw_dpy(), fb_config) }
}
pub fn glx_context(
xcb_connection: &XcbConnection,
fb_config: *mut glx::__GLXFBConfigRec,
) -> *mut glx::__GLXcontextRec {
// Load GLX extensions
// We need at least `GLX_ARB_create_context`
let glx_extensions = unsafe {
CStr::from_ptr(glx::glXQueryExtensionsString(
xcb_connection.conn.get_raw_dpy(),
xcb_connection.xlib_display,
))
.to_str()
.unwrap()
};
glx_extensions
.find("GLX_ARB_create_context")
.expect("could not find GLX extension GLX_ARB_create_context");
// With GLX, we don't need a context pre-created in order to load symbols.
// Otherwise, we would need to create a temporary legacy (dummy) GL context to load them.
// (something that has at least GlXCreateContextAttribsARB)
let glx_create_context_attribs: GlXCreateContextAttribsARBProc =
unsafe { std::mem::transmute(load_gl_func("glXCreateContextAttribsARB")) };
// Load all other symbols
unsafe {
gl::load_with(|n| load_gl_func(&n));
}
// Check GL3 support
if !gl::GenVertexArrays::is_loaded() {
panic!("no GL3 support available!");
}
// Create GLX context attributes. (?)
let context_attribs: [c_int; 5] = [
glx::arb::GLX_CONTEXT_MAJOR_VERSION_ARB as c_int,
3,
glx::arb::GLX_CONTEXT_MINOR_VERSION_ARB as c_int,
0,
0,
];
let ctx = unsafe {
glx_create_context_attribs(
xcb_connection.conn.get_raw_dpy(),
fb_config,
null_mut(),
xlib::True,
&context_attribs[0] as *const c_int,
)
};
if ctx.is_null()
/* || ctx_error_occurred */
{
panic!("Error when creating a GL 3.0 context");
}
if unsafe { glx::glXIsDirect(xcb_connection.conn.get_raw_dpy(), ctx) } == 0 {
panic!("Obtained indirect rendering context");
}
ctx
}
pub type GlXCreateContextAttribsARBProc = unsafe extern "C" fn(
dpy: *mut xlib::Display,
fbc: glx::GLXFBConfig,
share_context: glx::GLXContext,
direct: xlib::Bool,
attribs: *const c_int,
) -> glx::GLXContext;
// Check to make sure this system supports the correct version of GLX (>= 1.3 for now)
// For now it just panics if not, but TODO: do correct error handling
fn check_glx_version(xcb_connection: &XcbConnection) {
let raw_display = xcb_connection.conn.get_raw_dpy();
let mut maj: c_int = 0;
let mut min: c_int = 0;
unsafe {
if glx::glXQueryVersion(raw_display, &mut maj as *mut c_int, &mut min as *mut c_int) == 0 {
panic!("Cannot get GLX version");
}
if (maj < 1) || (maj == 1 && min < 3) {
panic!("GLX version >= 1.3 required! (have {}.{})", maj, min);
}
}
}
// Get GLX framebuffer config
// History: https://stackoverflow.com/questions/51558473/whats-the-difference-between-a-glx-visual-and-a-fbconfig
fn get_glxfbconfig(xcb_connection: &XcbConnection, visual_attribs: &[i32]) -> glx::GLXFBConfig {
let raw_display = xcb_connection.conn.get_raw_dpy();
let xlib_display = xcb_connection.xlib_display;
unsafe {
let mut fbcount: c_int = 0;
let fbcs = glx::glXChooseFBConfig(
raw_display,
xlib_display,
visual_attribs.as_ptr(),
&mut fbcount as *mut c_int,
);
if fbcount == 0 {
panic!("Could not find compatible GLX FB config.");
}
// If we get more than one, any of the different configs work. Just choose the first one.
let fbc = *fbcs;
xlib::XFree(fbcs as *mut c_void);
fbc
}
}
pub unsafe fn load_gl_func(name: &str) -> *mut c_void {
let cname = CString::new(name).unwrap();
let ptr: *mut c_void = std::mem::transmute(glx::glXGetProcAddress(cname.as_ptr() as *const u8));
if ptr.is_null() {
panic!("could not load {}", name);
}
ptr
}
pub fn xcb_expose(
window_id: u32,
raw_display: *mut xlib::_XDisplay,
ctx: *mut glx::__GLXcontextRec,
) {
unsafe {
glx::glXMakeCurrent(raw_display, window_id as xlib::XID, ctx);
gl::ClearColor(0.3, 0.8, 0.3, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::Flush();
glx::glXSwapBuffers(raw_display, window_id as xlib::XID);
glx::glXMakeCurrent(raw_display, 0, null_mut());
}
}

View file

@ -1,34 +1,23 @@
// TODO: messy for now, will refactor when I have more of an idea of the API/architecture
// TODO: close window
// TODO: proper error handling (no bare `unwrap`s, no panics)
// TODO: move more OpenGL-related stuff into opengl_util.rs
// TODO: consider researching all unsafe calls here and figuring out what invariants need to be upheld.
// (write safe wrappers?)
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::{c_int, c_void}; use std::os::raw::c_void;
use std::ptr::null_mut; use std::sync::mpsc;
use std::thread;
use ::x11::xlib; use ::x11::xlib;
// use xcb::dri2; // needed later // use xcb::dri2; // needed later
#[cfg(all(not(feature = "wgpu_renderer"), feature = "gl_renderer"))]
use super::opengl_util;
use super::XcbConnection; use super::XcbConnection;
use crate::{Message, MouseButtonID, MouseScroll, Parent, Receiver, WindowOpenOptions}; use crate::{Message, MouseButtonID, MouseScroll, Parent, WindowInfo, WindowOpenOptions};
pub struct Window<R: Receiver> { use raw_window_handle::RawWindowHandle;
xcb_connection: XcbConnection,
pub struct Window {
scaling: Option<f64>, // DPI scale, 96.0 is "default". scaling: Option<f64>, // DPI scale, 96.0 is "default".
receiver: R, raw_handle: RawWindowHandle,
#[cfg(all(not(feature = "wgpu_renderer"), feature = "gl_renderer"))]
ctx: *mut x11::glx::__GLXcontextRec,
} }
impl<R: Receiver> Window<R> { impl Window {
pub fn open(options: WindowOpenOptions, receiver: R) -> Self { pub fn build(options: WindowOpenOptions, message_tx: mpsc::Sender<Message>) -> Self {
// Convert the parent to a X11 window ID if we're given one // Convert the parent to a X11 window ID if we're given one
let parent = match options.parent { let parent = match options.parent {
Parent::None => None, Parent::None => None,
@ -39,12 +28,6 @@ impl<R: Receiver> Window<R> {
// Connect to the X server // Connect to the X server
let xcb_connection = XcbConnection::new(); let xcb_connection = XcbConnection::new();
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
let fb_config = opengl_util::fb_config(&xcb_connection);
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
let x_visual_info: *const xlib::XVisualInfo =
{ opengl_util::x_visual_info(&xcb_connection, fb_config) };
// Load up DRI2 extensions. // Load up DRI2 extensions.
// See also: https://www.x.org/releases/X11R7.7/doc/dri2proto/dri2proto.txt // See also: https://www.x.org/releases/X11R7.7/doc/dri2proto/dri2proto.txt
/* /*
@ -60,7 +43,12 @@ impl<R: Receiver> Window<R> {
// Get screen information (?) // Get screen information (?)
let setup = xcb_connection.conn.get_setup(); let setup = xcb_connection.conn.get_setup();
let screen = unsafe { setup.roots().nth((*x_visual_info).screen as usize).unwrap() }; let screen = setup
.roots()
.nth(xcb_connection.xlib_display as usize)
.unwrap();
let foreground = xcb_connection.conn.generate_id();
// Convert parent into something that X understands // Convert parent into something that X understands
let parent_id = if let Some(p) = parent { let parent_id = if let Some(p) = parent {
@ -69,24 +57,30 @@ impl<R: Receiver> Window<R> {
screen.root() screen.root()
}; };
// Create a colormap xcb::create_gc(
let colormap = xcb_connection.conn.generate_id(); &xcb_connection.conn,
unsafe { foreground,
xcb::create_colormap( parent_id,
&xcb_connection.conn, &[
xcb::COLORMAP_ALLOC_NONE as u8, (xcb::GC_FOREGROUND, screen.black_pixel()),
colormap, (xcb::GC_GRAPHICS_EXPOSURES, 0),
parent_id, ],
(*x_visual_info).visualid as u32, );
);
}
// Create window, connecting to the parent if we have one
let window_id = xcb_connection.conn.generate_id(); let window_id = xcb_connection.conn.generate_id();
let cw_values = [ xcb::create_window(
(xcb::CW_BACK_PIXEL, screen.white_pixel()), &xcb_connection.conn,
(xcb::CW_BORDER_PIXEL, screen.black_pixel()), xcb::COPY_FROM_PARENT as u8,
( window_id,
parent_id,
0,
0,
options.width as u16,
options.height as u16,
10,
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(),
&[(
xcb::CW_EVENT_MASK, xcb::CW_EVENT_MASK,
xcb::EVENT_MASK_EXPOSURE xcb::EVENT_MASK_EXPOSURE
| xcb::EVENT_MASK_POINTER_MOTION | xcb::EVENT_MASK_POINTER_MOTION
@ -94,40 +88,10 @@ impl<R: Receiver> Window<R> {
| xcb::EVENT_MASK_BUTTON_RELEASE | xcb::EVENT_MASK_BUTTON_RELEASE
| xcb::EVENT_MASK_KEY_PRESS | xcb::EVENT_MASK_KEY_PRESS
| xcb::EVENT_MASK_KEY_RELEASE, | xcb::EVENT_MASK_KEY_RELEASE,
), )],
(xcb::CW_COLORMAP, colormap),
];
xcb::create_window(
// Connection
&xcb_connection.conn,
// Depth
unsafe { *x_visual_info }.depth as u8,
// Window ID
window_id,
// Parent ID
parent_id,
// x
0,
// y
0,
// width
options.width as u16,
// height
options.height as u16,
// border width
0,
// class
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
// visual
unsafe { *x_visual_info }.visualid as u32,
// value list
&cw_values,
); );
xcb::map_window(&xcb_connection.conn, window_id);
// Don't need the visual info anymore xcb_connection.conn.flush();
unsafe {
xlib::XFree(x_visual_info as *mut c_void);
}
// Change window title // Change window title
let title = options.title; let title = options.title;
@ -152,217 +116,222 @@ impl<R: Receiver> Window<R> {
}; };
*/ */
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))] // What does this do?
let ctx = opengl_util::glx_context(&xcb_connection, fb_config);
// Display the window
xcb::map_window(&xcb_connection.conn, window_id);
xcb_connection.conn.flush();
unsafe { unsafe {
xlib::XSync(xcb_connection.conn.get_raw_dpy(), xlib::False); xlib::XSync(xcb_connection.conn.get_raw_dpy(), xlib::False);
} }
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))] let raw_handle = RawWindowHandle::Xcb(raw_window_handle::unix::XcbHandle {
connection: xcb_connection.conn.get_raw_conn() as *mut c_void,
..raw_window_handle::unix::XcbHandle::empty()
});
let mut x11_window = Self { let mut x11_window = Self {
xcb_connection,
scaling: None, scaling: None,
ctx, raw_handle,
receiver,
}; };
x11_window.scaling = x11_window x11_window.scaling =
.get_scaling_xft() get_scaling_xft(&xcb_connection).or(get_scaling_screen_dimensions(&xcb_connection));
.or(x11_window.get_scaling_screen_dimensions());
println!("Scale factor: {:?}", x11_window.scaling); println!("Scale factor: {:?}", x11_window.scaling);
x11_window message_tx
.receiver .send(Message::Opened(WindowInfo {
.on_message(Message::Opened(crate::message::WindowInfo {
width: options.width as u32, width: options.width as u32,
height: options.height as u32, height: options.height as u32,
dpi: x11_window.scaling, dpi: x11_window.scaling,
})); }))
x11_window.handle_events(window_id);
x11_window.receiver.on_message(Message::WillClose);
return x11_window;
}
// Event loop
fn handle_events(&mut self, window_id: u32) {
let raw_display = self.xcb_connection.conn.get_raw_dpy();
loop {
let ev = self.xcb_connection.conn.wait_for_event();
if let Some(event) = ev {
let event_type = event.response_type() & !0x80;
// For all of the keyboard and mouse events, you can fetch
// `x`, `y`, `detail`, and `state`.
// - `x` and `y` are the position inside the window where the cursor currently is
// when the event happened.
// - `detail` will tell you which keycode was pressed/released (for keyboard events)
// or which mouse button was pressed/released (for mouse events).
// For mouse events, here's what the value means (at least on my current mouse):
// 1 = left mouse button
// 2 = middle mouse button (scroll wheel)
// 3 = right mouse button
// 4 = scroll wheel up
// 5 = scroll wheel down
// 8 = lower side button ("back" button)
// 9 = upper side button ("forward" button)
// Note that you *will* get a "button released" event for even the scroll wheel
// events, which you can probably ignore.
// - `state` will tell you the state of the main three mouse buttons and some of
// the keyboard modifier keys at the time of the event.
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
match event_type {
xcb::EXPOSE => {
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
opengl_util::xcb_expose(window_id, raw_display, self.ctx);
}
xcb::MOTION_NOTIFY => {
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) };
let detail = event.detail();
if detail != 4 && detail != 5 {
self.receiver.on_message(Message::CursorMotion(
event.event_x() as i32,
event.event_y() as i32,
));
}
}
xcb::BUTTON_PRESS => {
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
match detail {
4 => {
self.receiver.on_message(Message::MouseScroll(MouseScroll {
x_delta: 0.0,
y_delta: 1.0,
}));
}
5 => {
self.receiver.on_message(Message::MouseScroll(MouseScroll {
x_delta: 0.0,
y_delta: -1.0,
}));
}
detail => {
let button_id = mouse_id(detail);
self.receiver.on_message(Message::MouseDown(button_id));
}
}
}
xcb::BUTTON_RELEASE => {
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
if detail != 4 && detail != 5 {
let button_id = mouse_id(detail);
self.receiver.on_message(Message::MouseUp(button_id));
}
}
xcb::KEY_PRESS => {
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
let detail = event.detail();
self.receiver.on_message(Message::KeyDown(detail));
}
xcb::KEY_RELEASE => {
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
let detail = event.detail();
self.receiver.on_message(Message::KeyUp(detail));
}
_ => {
println!("Unhandled event type: {:?}", event_type);
}
}
}
}
}
// Try to get the scaling with this function first.
// If this gives you `None`, fall back to `get_scaling_screen_dimensions`.
// If neither work, I guess just assume 96.0 and don't do any scaling.
fn get_scaling_xft(&self) -> Option<f64> {
use std::ffi::CString;
use x11::xlib::{
XResourceManagerString, XrmDestroyDatabase, XrmGetResource, XrmGetStringDatabase,
XrmValue,
};
let display = self.xcb_connection.conn.get_raw_dpy();
unsafe {
let rms = XResourceManagerString(display);
if !rms.is_null() {
let db = XrmGetStringDatabase(rms);
if !db.is_null() {
let mut value = XrmValue {
size: 0,
addr: std::ptr::null_mut(),
};
let mut value_type: *mut libc::c_char = std::ptr::null_mut();
let name_c_str = CString::new("Xft.dpi").unwrap();
let c_str = CString::new("Xft.Dpi").unwrap();
let dpi = if XrmGetResource(
db,
name_c_str.as_ptr(),
c_str.as_ptr(),
&mut value_type,
&mut value,
) != 0
&& !value.addr.is_null()
{
let value_addr: &CStr = CStr::from_ptr(value.addr);
value_addr.to_str().ok();
let value_str = value_addr.to_str().ok()?;
let value_f64 = value_str.parse().ok()?;
Some(value_f64)
} else {
None
};
XrmDestroyDatabase(db);
return dpi;
}
}
}
None
}
// Try to get the scaling with `get_scaling_xft` first.
// Only use this function as a fallback.
// If neither work, I guess just assume 96.0 and don't do any scaling.
fn get_scaling_screen_dimensions(&self) -> Option<f64> {
// Figure out screen information
let setup = self.xcb_connection.conn.get_setup();
let screen = setup
.roots()
.nth(self.xcb_connection.xlib_display as usize)
.unwrap(); .unwrap();
// Get the DPI from the screen struct thread::spawn(move || {
// run_event_loop(xcb_connection, message_tx);
// there are 2.54 centimeters to an inch; so there are 25.4 millimeters. });
// dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
// = N pixels / (M inch / 25.4)
// = N * 25.4 pixels / M inch
let width_px = screen.width_in_pixels() as f64;
let width_mm = screen.width_in_millimeters() as f64;
let height_px = screen.height_in_pixels() as f64;
let height_mm = screen.height_in_millimeters() as f64;
let _xres = width_px * 25.4 / width_mm;
let yres = height_px * 25.4 / height_mm;
// TODO: choose between `xres` and `yres`? (probably both are the same?) x11_window
Some(yres) }
pub fn raw_window_handle(&self) -> RawWindowHandle {
self.raw_handle
}
}
// Try to get the scaling with this function first.
// If this gives you `None`, fall back to `get_scaling_screen_dimensions`.
// If neither work, I guess just assume 96.0 and don't do any scaling.
fn get_scaling_xft(xcb_connection: &XcbConnection) -> Option<f64> {
use std::ffi::CString;
use x11::xlib::{
XResourceManagerString, XrmDestroyDatabase, XrmGetResource, XrmGetStringDatabase, XrmValue,
};
let display = xcb_connection.conn.get_raw_dpy();
unsafe {
let rms = XResourceManagerString(display);
if !rms.is_null() {
let db = XrmGetStringDatabase(rms);
if !db.is_null() {
let mut value = XrmValue {
size: 0,
addr: std::ptr::null_mut(),
};
let mut value_type: *mut libc::c_char = std::ptr::null_mut();
let name_c_str = CString::new("Xft.dpi").unwrap();
let c_str = CString::new("Xft.Dpi").unwrap();
let dpi = if XrmGetResource(
db,
name_c_str.as_ptr(),
c_str.as_ptr(),
&mut value_type,
&mut value,
) != 0
&& !value.addr.is_null()
{
let value_addr: &CStr = CStr::from_ptr(value.addr);
value_addr.to_str().ok();
let value_str = value_addr.to_str().ok()?;
let value_f64 = value_str.parse().ok()?;
Some(value_f64)
} else {
None
};
XrmDestroyDatabase(db);
return dpi;
}
}
}
None
}
// Try to get the scaling with `get_scaling_xft` first.
// Only use this function as a fallback.
// If neither work, I guess just assume 96.0 and don't do any scaling.
fn get_scaling_screen_dimensions(xcb_connection: &XcbConnection) -> Option<f64> {
// Figure out screen information
let setup = xcb_connection.conn.get_setup();
let screen = setup
.roots()
.nth(xcb_connection.xlib_display as usize)
.unwrap();
// Get the DPI from the screen struct
//
// there are 2.54 centimeters to an inch; so there are 25.4 millimeters.
// dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
// = N pixels / (M inch / 25.4)
// = N * 25.4 pixels / M inch
let width_px = screen.width_in_pixels() as f64;
let width_mm = screen.width_in_millimeters() as f64;
let height_px = screen.height_in_pixels() as f64;
let height_mm = screen.height_in_millimeters() as f64;
let _xres = width_px * 25.4 / width_mm;
let yres = height_px * 25.4 / height_mm;
// TODO: choose between `xres` and `yres`? (probably both are the same?)
Some(yres)
}
// Event loop
fn run_event_loop(xcb_connection: XcbConnection, message_tx: mpsc::Sender<Message>) {
//let raw_display = self.xcb_connection.conn.get_raw_dpy();
loop {
let ev = xcb_connection.conn.wait_for_event();
if let Some(event) = ev {
let event_type = event.response_type() & !0x80;
// For all of the keyboard and mouse events, you can fetch
// `x`, `y`, `detail`, and `state`.
// - `x` and `y` are the position inside the window where the cursor currently is
// when the event happened.
// - `detail` will tell you which keycode was pressed/released (for keyboard events)
// or which mouse button was pressed/released (for mouse events).
// For mouse events, here's what the value means (at least on my current mouse):
// 1 = left mouse button
// 2 = middle mouse button (scroll wheel)
// 3 = right mouse button
// 4 = scroll wheel up
// 5 = scroll wheel down
// 8 = lower side button ("back" button)
// 9 = upper side button ("forward" button)
// Note that you *will* get a "button released" event for even the scroll wheel
// events, which you can probably ignore.
// - `state` will tell you the state of the main three mouse buttons and some of
// the keyboard modifier keys at the time of the event.
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
match event_type {
xcb::EXPOSE => {
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
opengl_util::xcb_expose(window_id, raw_display, self.ctx);
}
xcb::MOTION_NOTIFY => {
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) };
let detail = event.detail();
if detail != 4 && detail != 5 {
message_tx
.send(Message::CursorMotion(
event.event_x() as i32,
event.event_y() as i32,
))
.unwrap();
}
}
xcb::BUTTON_PRESS => {
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
match detail {
4 => {
message_tx
.send(Message::MouseScroll(MouseScroll {
x_delta: 0.0,
y_delta: 1.0,
}))
.unwrap();
}
5 => {
message_tx
.send(Message::MouseScroll(MouseScroll {
x_delta: 0.0,
y_delta: -1.0,
}))
.unwrap();
}
detail => {
let button_id = mouse_id(detail);
message_tx.send(Message::MouseDown(button_id)).unwrap();
}
}
}
xcb::BUTTON_RELEASE => {
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
if detail != 4 && detail != 5 {
let button_id = mouse_id(detail);
message_tx.send(Message::MouseUp(button_id)).unwrap();
}
}
xcb::KEY_PRESS => {
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
let detail = event.detail();
message_tx.send(Message::KeyDown(detail)).unwrap();
}
xcb::KEY_RELEASE => {
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
let detail = event.detail();
message_tx.send(Message::KeyUp(detail)).unwrap();
}
_ => {
println!("Unhandled event type: {:?}", event_type);
}
}
}
} }
} }