Merge pull request #32 from BillyDM/master
Add beginnings of cross-platform window events struct and AppWindow trait.
This commit is contained in:
commit
00c18dd91e
|
@ -6,21 +6,22 @@ authors = [
|
||||||
"Charles Saracco <crsaracco@gmail.com>",
|
"Charles Saracco <crsaracco@gmail.com>",
|
||||||
"Mirko Covizzi <mrkcvzz@gmail.com>",
|
"Mirko Covizzi <mrkcvzz@gmail.com>",
|
||||||
"Micah Johnston <micah@glowcoil.com>",
|
"Micah Johnston <micah@glowcoil.com>",
|
||||||
|
"Billy Messenger <billydm@protonmail.com>",
|
||||||
]
|
]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gl = "0.14.0"
|
log = "0.4.11"
|
||||||
log = "0.4.8"
|
raw-window-handle = "0.3.3"
|
||||||
|
|
||||||
[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.3", 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"
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use baseview::Event;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let window_open_options = baseview::WindowOpenOptions {
|
let window_open_options = baseview::WindowOpenOptions {
|
||||||
title: "baseview",
|
title: "baseview",
|
||||||
|
@ -6,5 +10,64 @@ fn main() {
|
||||||
parent: baseview::Parent::None,
|
parent: baseview::Parent::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
baseview::Window::open(window_open_options);
|
let (_app_message_tx, app_message_rx) = mpsc::channel::<()>();
|
||||||
|
|
||||||
|
// Send _app_message_tx to a separate thread, then send messages to the GUI thread.
|
||||||
|
|
||||||
|
let _ = baseview::Window::<MyProgram>::open(window_open_options, app_message_rx);
|
||||||
|
}
|
||||||
|
struct MyProgram {}
|
||||||
|
|
||||||
|
impl baseview::AppWindow for MyProgram {
|
||||||
|
type AppMessage = ();
|
||||||
|
|
||||||
|
fn build(_window_handle: baseview::RawWindow, window_info: &baseview::WindowInfo) -> Self {
|
||||||
|
println!("Window info: {:?}", window_info);
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self) {}
|
||||||
|
|
||||||
|
fn on_event(&mut self, event: Event) {
|
||||||
|
match event {
|
||||||
|
Event::CursorMotion(x, y) => {
|
||||||
|
println!("Cursor moved, x: {}, y: {}", x, y);
|
||||||
|
}
|
||||||
|
Event::MouseDown(button_id) => {
|
||||||
|
println!("Mouse down, button id: {:?}", button_id);
|
||||||
|
}
|
||||||
|
Event::MouseUp(button_id) => {
|
||||||
|
println!("Mouse up, button id: {:?}", button_id);
|
||||||
|
}
|
||||||
|
Event::MouseScroll(mouse_scroll) => {
|
||||||
|
println!("Mouse scroll, {:?}", mouse_scroll);
|
||||||
|
}
|
||||||
|
Event::MouseClick(mouse_click) => {
|
||||||
|
println!("Mouse click, {:?}", mouse_click);
|
||||||
|
}
|
||||||
|
Event::KeyDown(keycode) => {
|
||||||
|
println!("Key down, keycode: {}", keycode);
|
||||||
|
}
|
||||||
|
Event::KeyUp(keycode) => {
|
||||||
|
println!("Key up, keycode: {}", keycode);
|
||||||
|
}
|
||||||
|
Event::CharacterInput(char_code) => {
|
||||||
|
println!("Character input, char_code: {}", char_code);
|
||||||
|
}
|
||||||
|
Event::WindowResized(window_info) => {
|
||||||
|
println!("Window resized, {:?}", window_info);
|
||||||
|
}
|
||||||
|
Event::WindowFocus => {
|
||||||
|
println!("Window focused");
|
||||||
|
}
|
||||||
|
Event::WindowUnfocus => {
|
||||||
|
println!("Window unfocused");
|
||||||
|
}
|
||||||
|
Event::WillClose => {
|
||||||
|
println!("Window will close");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_app_message(&mut self, _message: Self::AppMessage) {}
|
||||||
}
|
}
|
||||||
|
|
46
src/event.rs
Normal file
46
src/event.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
pub enum MouseButtonID {
|
||||||
|
Left,
|
||||||
|
Middle,
|
||||||
|
Right,
|
||||||
|
Back,
|
||||||
|
Forward,
|
||||||
|
Other(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct MouseScroll {
|
||||||
|
pub x_delta: f64,
|
||||||
|
pub y_delta: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct MouseClick {
|
||||||
|
pub id: MouseButtonID,
|
||||||
|
pub click_count: usize,
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WindowInfo {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub scale: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Event {
|
||||||
|
CursorMotion(i32, i32), // new (x, y) relative to window
|
||||||
|
MouseDown(MouseButtonID),
|
||||||
|
MouseUp(MouseButtonID),
|
||||||
|
MouseScroll(MouseScroll),
|
||||||
|
MouseClick(MouseClick),
|
||||||
|
KeyDown(u8), // keycode
|
||||||
|
KeyUp(u8), // keycode
|
||||||
|
CharacterInput(u32), // character code
|
||||||
|
WindowResized(WindowInfo), // new (width, height)
|
||||||
|
WindowFocus,
|
||||||
|
WindowUnfocus,
|
||||||
|
WillClose,
|
||||||
|
}
|
25
src/lib.rs
25
src/lib.rs
|
@ -15,6 +15,9 @@ mod macos;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub use macos::*;
|
pub use macos::*;
|
||||||
|
|
||||||
|
mod event;
|
||||||
|
pub use event::*;
|
||||||
|
|
||||||
pub enum Parent {
|
pub enum Parent {
|
||||||
None,
|
None,
|
||||||
AsIfParented,
|
AsIfParented,
|
||||||
|
@ -29,3 +32,25 @@ pub struct WindowOpenOptions<'a> {
|
||||||
|
|
||||||
pub parent: Parent,
|
pub parent: Parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AppWindow {
|
||||||
|
type AppMessage;
|
||||||
|
|
||||||
|
fn build(window_handle: RawWindow, window_info: &WindowInfo) -> Self;
|
||||||
|
|
||||||
|
fn draw(&mut self);
|
||||||
|
fn on_event(&mut self, event: Event);
|
||||||
|
fn on_app_message(&mut self, message: Self::AppMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper for a `RawWindowHandle`. Some context creators expect an `&impl HasRawWindowHandle`.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct RawWindow {
|
||||||
|
pub raw_window_handle: raw_window_handle::RawWindowHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow {
|
||||||
|
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
|
||||||
|
self.raw_window_handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,19 @@ use cocoa::appkit::{
|
||||||
use cocoa::base::{nil, NO};
|
use cocoa::base::{nil, NO};
|
||||||
use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString};
|
use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString};
|
||||||
|
|
||||||
use crate::WindowOpenOptions;
|
use crate::{AppWindow, Event, MouseButtonID, MouseScroll, WindowOpenOptions};
|
||||||
|
|
||||||
pub struct Window;
|
pub struct Window<A: AppWindow> {
|
||||||
|
app_window: A,
|
||||||
|
app_message_rx: mpsc::Receiver<A::AppMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Window {
|
impl<A: Application> Window<A> {
|
||||||
pub fn open(options: WindowOpenOptions) -> Self {
|
pub fn open(
|
||||||
|
options: WindowOpenOptions,
|
||||||
|
app_window: A,
|
||||||
|
app_message_rx: mpsc::Receiver<A::AppMessage>,
|
||||||
|
) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _pool = NSAutoreleasePool::new(nil);
|
let _pool = NSAutoreleasePool::new(nil);
|
||||||
|
|
||||||
|
@ -42,7 +49,10 @@ impl Window {
|
||||||
current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
|
current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
|
||||||
app.run();
|
app.run();
|
||||||
|
|
||||||
Window
|
Window {
|
||||||
|
app_window,
|
||||||
|
app_message_rx,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
@ -14,23 +15,24 @@ use self::winapi::um::wingdi::{
|
||||||
PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR,
|
PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR,
|
||||||
};
|
};
|
||||||
use self::winapi::um::winuser::{
|
use self::winapi::um::winuser::{
|
||||||
AdjustWindowRectEx, CreateWindowExA, DefWindowProcA, DestroyWindow, DispatchMessageA, GetDC,
|
AdjustWindowRectEx, CreateWindowExA, DefWindowProcA, DestroyWindow, DispatchEventA, EventBoxA,
|
||||||
GetMessageA, GetWindowLongPtrA, MessageBoxA, PeekMessageA, PostMessageA, RegisterClassA,
|
GetDC, GetEventA, GetWindowLongPtrA, PeekEventA, PostEventA, RegisterClassA, ReleaseDC,
|
||||||
ReleaseDC, SetTimer, SetWindowLongPtrA, TranslateMessage, UnregisterClassA, CS_OWNDC,
|
SetTimer, SetWindowLongPtrA, TranslateEvent, UnregisterClassA, CS_OWNDC, GWLP_USERDATA,
|
||||||
GWLP_USERDATA, MB_ICONERROR, MB_OK, MB_TOPMOST, MSG, PM_REMOVE, WM_CREATE, WM_QUIT,
|
MB_ICONERROR, MB_OK, MB_TOPMOST, MSG, PM_REMOVE, WM_CREATE, WM_QUIT, WM_SHOWWINDOW, WM_TIMER,
|
||||||
WM_SHOWWINDOW, WM_TIMER, WNDCLASSA, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX,
|
WNDCLASSA, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX,
|
||||||
WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
|
WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::winapi::ctypes::c_void;
|
use self::winapi::ctypes::c_void;
|
||||||
use crate::Parent::WithParent;
|
use crate::Parent::WithParent;
|
||||||
use crate::{handle_message, WindowOpenOptions};
|
use crate::{handle_message, WindowOpenOptions};
|
||||||
|
use crate::{AppWindow, Event, MouseButtonID, MouseScroll};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
unsafe fn message_box(title: &str, msg: &str) {
|
unsafe fn message_box(title: &str, msg: &str) {
|
||||||
let title = (title.to_owned() + "\0").as_ptr() as *const i8;
|
let title = (title.to_owned() + "\0").as_ptr() as *const i8;
|
||||||
let msg = (msg.to_owned() + "\0").as_ptr() as *const i8;
|
let msg = (msg.to_owned() + "\0").as_ptr() as *const i8;
|
||||||
MessageBoxA(null_mut(), msg, title, MB_ICONERROR | MB_OK | MB_TOPMOST);
|
EventBoxA(null_mut(), msg, title, MB_ICONERROR | MB_OK | MB_TOPMOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn generate_guid() -> String {
|
unsafe fn generate_guid() -> String {
|
||||||
|
@ -61,7 +63,7 @@ unsafe extern "system" fn wnd_proc(
|
||||||
let win_ptr = GetWindowLongPtrA(hwnd, GWLP_USERDATA) as *const c_void;
|
let win_ptr = GetWindowLongPtrA(hwnd, GWLP_USERDATA) as *const c_void;
|
||||||
match msg {
|
match msg {
|
||||||
WM_CREATE => {
|
WM_CREATE => {
|
||||||
PostMessageA(hwnd, WM_SHOWWINDOW, 0, 0);
|
PostEventA(hwnd, WM_SHOWWINDOW, 0, 0);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -111,24 +113,34 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
|
||||||
|
|
||||||
unsafe fn init_gl_context() {}
|
unsafe fn init_gl_context() {}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window<A: AppWindow> {
|
||||||
pub(crate) hwnd: HWND,
|
pub(crate) hwnd: HWND,
|
||||||
hdc: HDC,
|
hdc: HDC,
|
||||||
gl_context: HGLRC,
|
gl_context: HGLRC,
|
||||||
window_class: ATOM,
|
window_class: ATOM,
|
||||||
|
app_window: A,
|
||||||
|
app_message_rx: mpsc::Receiver<A::AppMessage>,
|
||||||
|
scaling: Option<f64>, // DPI scale, 96.0 is "default".
|
||||||
r: f32,
|
r: f32,
|
||||||
g: f32,
|
g: f32,
|
||||||
b: f32,
|
b: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl<A: AppWindow> Window<A> {
|
||||||
pub fn open(options: WindowOpenOptions) {
|
pub fn open(
|
||||||
|
options: WindowOpenOptions,
|
||||||
|
app_window: A,
|
||||||
|
app_message_rx: mpsc::Receiver<A::AppMessage>,
|
||||||
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut window = Window {
|
let mut window = Window {
|
||||||
hwnd: null_mut(),
|
hwnd: null_mut(),
|
||||||
hdc: null_mut(),
|
hdc: null_mut(),
|
||||||
gl_context: null_mut(),
|
gl_context: null_mut(),
|
||||||
window_class: 0,
|
window_class: 0,
|
||||||
|
app_window,
|
||||||
|
app_message_rx,
|
||||||
|
scaling: None,
|
||||||
r: 0.3,
|
r: 0.3,
|
||||||
g: 0.8,
|
g: 0.8,
|
||||||
b: 0.3,
|
b: 0.3,
|
||||||
|
@ -240,11 +252,11 @@ impl Window {
|
||||||
if parent.is_null() {
|
if parent.is_null() {
|
||||||
let mut msg: MSG = std::mem::zeroed();
|
let mut msg: MSG = std::mem::zeroed();
|
||||||
loop {
|
loop {
|
||||||
let status = GetMessageA(&mut msg, hwnd, 0, 0);
|
let status = GetEventA(&mut msg, hwnd, 0, 0);
|
||||||
if status == -1 {
|
if status == -1 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
TranslateMessage(&mut msg);
|
TranslateEvent(&mut msg);
|
||||||
handle_message(Arc::clone(&win_p), msg.message, msg.wParam, msg.lParam);
|
handle_message(Arc::clone(&win_p), msg.message, msg.wParam, msg.lParam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,6 +264,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
|
self.app_window.on_event(Event::WillClose);
|
||||||
|
|
||||||
// todo: see https://github.com/wrl/rutabaga/blob/f30ff67e157375cafdbafe5fb549f1790443a3a8/src/platform/win/window.c#L402
|
// todo: see https://github.com/wrl/rutabaga/blob/f30ff67e157375cafdbafe5fb549f1790443a3a8/src/platform/win/window.c#L402
|
||||||
unsafe {
|
unsafe {
|
||||||
wglMakeCurrent(null_mut(), null_mut());
|
wglMakeCurrent(null_mut(), null_mut());
|
||||||
|
@ -270,10 +284,11 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_mouse_motion(&mut self, x: i32, y: i32) {
|
pub(crate) fn handle_mouse_motion(&mut self, x: i32, y: i32) {
|
||||||
println!("{}, {}", x, y);
|
|
||||||
let r = (x as f32) / 1000.0;
|
let r = (x as f32) / 1000.0;
|
||||||
let g = (y as f32) / 1000.0;
|
let g = (y as f32) / 1000.0;
|
||||||
self.r = r;
|
self.r = r;
|
||||||
self.g = g;
|
self.g = g;
|
||||||
|
|
||||||
|
self.app_window.on_message(Event::CursorMotion(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
mod window;
|
|
||||||
pub use window::*;
|
|
||||||
|
|
||||||
mod xcb_connection;
|
mod xcb_connection;
|
||||||
use xcb_connection::XcbConnection;
|
use xcb_connection::XcbConnection;
|
||||||
|
|
||||||
mod opengl_util;
|
mod window;
|
||||||
|
pub use window::*;
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
use std::ffi::CString;
|
|
||||||
use std::os::raw::{c_int, c_void};
|
|
||||||
|
|
||||||
use ::x11::{glx, xlib};
|
|
||||||
|
|
||||||
use super::XcbConnection;
|
|
||||||
|
|
||||||
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
|
|
||||||
pub 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
|
|
||||||
pub 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
|
|
||||||
}
|
|
|
@ -1,29 +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_ulong, c_void};
|
||||||
use std::ptr::null_mut;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use ::x11::{glx, xlib};
|
|
||||||
// use xcb::dri2; // needed later
|
|
||||||
|
|
||||||
use super::opengl_util;
|
|
||||||
use super::XcbConnection;
|
use super::XcbConnection;
|
||||||
use crate::Parent;
|
use crate::{
|
||||||
use crate::WindowOpenOptions;
|
AppWindow, Event, MouseButtonID, MouseScroll, Parent, RawWindow, WindowInfo, WindowOpenOptions,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Window {
|
use raw_window_handle::RawWindowHandle;
|
||||||
|
|
||||||
|
pub struct Window<A: AppWindow> {
|
||||||
|
scaling: f64,
|
||||||
xcb_connection: XcbConnection,
|
xcb_connection: XcbConnection,
|
||||||
scaling: Option<f64>, // DPI scale, 96.0 is "default".
|
app_window: A,
|
||||||
|
app_message_rx: mpsc::Receiver<A::AppMessage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl<A: AppWindow> Window<A> {
|
||||||
pub fn open(options: WindowOpenOptions) -> Self {
|
pub fn open(options: WindowOpenOptions, app_message_rx: mpsc::Receiver<A::AppMessage>) -> 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,
|
||||||
|
@ -34,49 +28,14 @@ impl Window {
|
||||||
// Connect to the X server
|
// Connect to the X server
|
||||||
let xcb_connection = XcbConnection::new();
|
let xcb_connection = XcbConnection::new();
|
||||||
|
|
||||||
// Check GLX version (>= 1.3 needed)
|
|
||||||
opengl_util::check_glx_version(&xcb_connection);
|
|
||||||
|
|
||||||
// Get GLX framebuffer config (requires GLX >= 1.3)
|
|
||||||
#[rustfmt::skip]
|
|
||||||
let fb_config = opengl_util::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
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// The GLX framebuffer config holds an XVisualInfo, which we'll need for other X operations.
|
|
||||||
let x_visual_info: *const xlib::XVisualInfo =
|
|
||||||
unsafe { glx::glXGetVisualFromFBConfig(xcb_connection.conn.get_raw_dpy(), fb_config) };
|
|
||||||
|
|
||||||
// Load up DRI2 extensions.
|
|
||||||
// See also: https://www.x.org/releases/X11R7.7/doc/dri2proto/dri2proto.txt
|
|
||||||
/*
|
|
||||||
// needed later when we handle events
|
|
||||||
let dri2_ev = {
|
|
||||||
xcb_connection.conn.prefetch_extension_data(dri2::id());
|
|
||||||
match xcb_connection.conn.get_extension_data(dri2::id()) {
|
|
||||||
None => panic!("could not load dri2 extension"),
|
|
||||||
Some(r) => r.first_event(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -85,24 +44,30 @@ impl Window {
|
||||||
screen.root()
|
screen.root()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a colormap
|
xcb::create_gc(
|
||||||
let colormap = xcb_connection.conn.generate_id();
|
|
||||||
unsafe {
|
|
||||||
xcb::create_colormap(
|
|
||||||
&xcb_connection.conn,
|
&xcb_connection.conn,
|
||||||
xcb::COLORMAP_ALLOC_NONE as u8,
|
foreground,
|
||||||
colormap,
|
|
||||||
parent_id,
|
parent_id,
|
||||||
(*x_visual_info).visualid as u32,
|
&[
|
||||||
|
(xcb::GC_FOREGROUND, screen.black_pixel()),
|
||||||
|
(xcb::GC_GRAPHICS_EXPOSURES, 0),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// 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, // x coordinate of the new window
|
||||||
|
0, // y coordinate of the new window
|
||||||
|
options.width as u16, // window width
|
||||||
|
options.height as u16, // window height
|
||||||
|
0, // window border
|
||||||
|
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
|
||||||
|
@ -110,40 +75,9 @@ impl Window {
|
||||||
| 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
|
|
||||||
unsafe {
|
|
||||||
xlib::XFree(x_visual_info as *mut c_void);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change window title
|
// Change window title
|
||||||
let title = options.title;
|
let title = options.title;
|
||||||
|
@ -153,103 +87,51 @@ impl Window {
|
||||||
window_id,
|
window_id,
|
||||||
xcb::ATOM_WM_NAME,
|
xcb::ATOM_WM_NAME,
|
||||||
xcb::ATOM_STRING,
|
xcb::ATOM_STRING,
|
||||||
8,
|
8, // view data as 8-bit
|
||||||
title.as_bytes(),
|
title.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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: opengl_util::GlXCreateContextAttribsARBProc =
|
|
||||||
unsafe { std::mem::transmute(opengl_util::load_gl_func("glXCreateContextAttribsARB")) };
|
|
||||||
|
|
||||||
// Load all other symbols
|
|
||||||
unsafe {
|
|
||||||
gl::load_with(|n| opengl_util::load_gl_func(&n));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check GL3 support
|
|
||||||
if !gl::GenVertexArrays::is_loaded() {
|
|
||||||
panic!("no GL3 support available!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This requires a global, which is a no. Figure out if there's a better way to do it.
|
|
||||||
/*
|
|
||||||
// installing an event handler to check if error is generated
|
|
||||||
unsafe {
|
|
||||||
ctx_error_occurred = false;
|
|
||||||
}
|
|
||||||
let old_handler = unsafe {
|
|
||||||
xlib::XSetErrorHandler(Some(ctx_error_handler))
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the window
|
|
||||||
xcb::map_window(&xcb_connection.conn, window_id);
|
|
||||||
xcb_connection.conn.flush();
|
xcb_connection.conn.flush();
|
||||||
unsafe {
|
|
||||||
xlib::XSync(xcb_connection.conn.get_raw_dpy(), xlib::False);
|
let raw_handle = RawWindowHandle::Xlib(raw_window_handle::unix::XlibHandle {
|
||||||
}
|
window: window_id as c_ulong,
|
||||||
|
display: xcb_connection.conn.get_raw_dpy() as *mut c_void,
|
||||||
|
..raw_window_handle::unix::XlibHandle::empty()
|
||||||
|
});
|
||||||
|
|
||||||
|
let raw_window = RawWindow {
|
||||||
|
raw_window_handle: raw_handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
let scaling = get_scaling_xft(&xcb_connection)
|
||||||
|
.or(get_scaling_screen_dimensions(&xcb_connection))
|
||||||
|
.unwrap_or(1.0);
|
||||||
|
|
||||||
|
let window_info = WindowInfo {
|
||||||
|
width: options.width as u32,
|
||||||
|
height: options.height as u32,
|
||||||
|
scale: scaling,
|
||||||
|
};
|
||||||
|
|
||||||
|
let app_window = A::build(raw_window, &window_info);
|
||||||
|
|
||||||
let mut x11_window = Self {
|
let mut x11_window = Self {
|
||||||
|
scaling,
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
scaling: None,
|
app_window,
|
||||||
|
app_message_rx,
|
||||||
};
|
};
|
||||||
|
|
||||||
x11_window.scaling = x11_window
|
x11_window.run_event_loop();
|
||||||
.get_scaling_xft()
|
|
||||||
.or(x11_window.get_scaling_screen_dimensions());
|
|
||||||
println!("Scale factor: {:?}", x11_window.scaling);
|
|
||||||
x11_window.handle_events(window_id, ctx);
|
|
||||||
|
|
||||||
return x11_window;
|
x11_window
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event loop
|
// Event loop
|
||||||
fn handle_events(&self, window_id: u32, ctx: *mut x11::glx::__GLXcontextRec) {
|
fn run_event_loop(&mut self) {
|
||||||
let raw_display = self.xcb_connection.conn.get_raw_dpy();
|
|
||||||
loop {
|
loop {
|
||||||
|
// somehow poll self.app_message_rx for messages at the same time
|
||||||
|
|
||||||
let ev = self.xcb_connection.conn.wait_for_event();
|
let ev = self.xcb_connection.conn.wait_for_event();
|
||||||
if let Some(event) = ev {
|
if let Some(event) = ev {
|
||||||
let event_type = event.response_type() & !0x80;
|
let event_type = event.response_type() & !0x80;
|
||||||
|
@ -275,65 +157,63 @@ impl Window {
|
||||||
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
|
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
|
||||||
|
|
||||||
match event_type {
|
match event_type {
|
||||||
xcb::EXPOSE => unsafe {
|
xcb::EXPOSE => {
|
||||||
glx::glXMakeCurrent(raw_display, window_id as xlib::XID, ctx);
|
self.app_window.draw();
|
||||||
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());
|
|
||||||
},
|
|
||||||
xcb::MOTION_NOTIFY => {
|
xcb::MOTION_NOTIFY => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) };
|
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) };
|
||||||
let x = event.event_x();
|
|
||||||
let y = event.event_y();
|
|
||||||
let detail = event.detail();
|
let detail = event.detail();
|
||||||
let state = event.state();
|
|
||||||
println!("Mouse motion: ({}, {}) -- {} / {}", x, y, detail, state);
|
if detail != 4 && detail != 5 {
|
||||||
|
self.app_window.on_event(Event::CursorMotion(
|
||||||
|
event.event_x() as i32,
|
||||||
|
event.event_y() as i32,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xcb::BUTTON_PRESS => {
|
xcb::BUTTON_PRESS => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
|
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
|
||||||
let x = event.event_x();
|
|
||||||
let y = event.event_y();
|
|
||||||
let detail = event.detail();
|
let detail = event.detail();
|
||||||
let state = event.state();
|
|
||||||
println!(
|
match detail {
|
||||||
"Mouse button pressed: ({}, {}) -- {} / {}",
|
4 => {
|
||||||
x, y, detail, state
|
self.app_window.on_event(Event::MouseScroll(MouseScroll {
|
||||||
);
|
x_delta: 0.0,
|
||||||
|
y_delta: 1.0,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
5 => {
|
||||||
|
self.app_window.on_event(Event::MouseScroll(MouseScroll {
|
||||||
|
x_delta: 0.0,
|
||||||
|
y_delta: -1.0,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
detail => {
|
||||||
|
let button_id = mouse_id(detail);
|
||||||
|
self.app_window.on_event(Event::MouseDown(button_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xcb::BUTTON_RELEASE => {
|
xcb::BUTTON_RELEASE => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::ButtonReleaseEvent>(&event) };
|
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
|
||||||
let x = event.event_x();
|
|
||||||
let y = event.event_y();
|
|
||||||
let detail = event.detail();
|
let detail = event.detail();
|
||||||
let state = event.state();
|
|
||||||
println!(
|
if detail != 4 && detail != 5 {
|
||||||
"Mouse button released: ({}, {}) -- {} / {}",
|
let button_id = mouse_id(detail);
|
||||||
x, y, detail, state
|
self.app_window.on_event(Event::MouseUp(button_id));
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
xcb::KEY_PRESS => {
|
xcb::KEY_PRESS => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
|
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
|
||||||
let x = event.event_x();
|
|
||||||
let y = event.event_y();
|
|
||||||
let detail = event.detail();
|
let detail = event.detail();
|
||||||
let state = event.state();
|
|
||||||
println!(
|
self.app_window.on_event(Event::KeyDown(detail));
|
||||||
"Keyboard key pressed: ({}, {}) -- {} / {}",
|
|
||||||
x, y, detail, state
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
xcb::KEY_RELEASE => {
|
xcb::KEY_RELEASE => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
|
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
|
||||||
let x = event.event_x();
|
|
||||||
let y = event.event_y();
|
|
||||||
let detail = event.detail();
|
let detail = event.detail();
|
||||||
let state = event.state();
|
|
||||||
println!(
|
self.app_window.on_event(Event::KeyUp(detail));
|
||||||
"Keyboard key released: ({}, {}) -- {} / {}",
|
|
||||||
x, y, detail, state
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unhandled event type: {:?}", event_type);
|
println!("Unhandled event type: {:?}", event_type);
|
||||||
|
@ -342,18 +222,18 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to get the scaling with this function first.
|
// Try to get the scaling with this function first.
|
||||||
// If this gives you `None`, fall back to `get_scaling_screen_dimensions`.
|
// 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.
|
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
||||||
fn get_scaling_xft(&self) -> Option<f64> {
|
fn get_scaling_xft(xcb_connection: &XcbConnection) -> Option<f64> {
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use x11::xlib::{
|
use x11::xlib::{
|
||||||
XResourceManagerString, XrmDestroyDatabase, XrmGetResource, XrmGetStringDatabase,
|
XResourceManagerString, XrmDestroyDatabase, XrmGetResource, XrmGetStringDatabase, XrmValue,
|
||||||
XrmValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let display = self.xcb_connection.conn.get_raw_dpy();
|
let display = xcb_connection.conn.get_raw_dpy();
|
||||||
unsafe {
|
unsafe {
|
||||||
let rms = XResourceManagerString(display);
|
let rms = XResourceManagerString(display);
|
||||||
if !rms.is_null() {
|
if !rms.is_null() {
|
||||||
|
@ -380,8 +260,9 @@ impl Window {
|
||||||
let value_addr: &CStr = CStr::from_ptr(value.addr);
|
let value_addr: &CStr = CStr::from_ptr(value.addr);
|
||||||
value_addr.to_str().ok();
|
value_addr.to_str().ok();
|
||||||
let value_str = value_addr.to_str().ok()?;
|
let value_str = value_addr.to_str().ok()?;
|
||||||
let value_f64 = value_str.parse().ok()?;
|
let value_f64: f64 = value_str.parse().ok()?;
|
||||||
Some(value_f64)
|
let dpi_to_scale = value_f64 / 96.0;
|
||||||
|
Some(dpi_to_scale)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -397,12 +278,12 @@ impl Window {
|
||||||
// Try to get the scaling with `get_scaling_xft` first.
|
// Try to get the scaling with `get_scaling_xft` first.
|
||||||
// Only use this function as a fallback.
|
// Only use this function as a fallback.
|
||||||
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
||||||
fn get_scaling_screen_dimensions(&self) -> Option<f64> {
|
fn get_scaling_screen_dimensions(xcb_connection: &XcbConnection) -> Option<f64> {
|
||||||
// Figure out screen information
|
// Figure out screen information
|
||||||
let setup = self.xcb_connection.conn.get_setup();
|
let setup = xcb_connection.conn.get_setup();
|
||||||
let screen = setup
|
let screen = setup
|
||||||
.roots()
|
.roots()
|
||||||
.nth(self.xcb_connection.xlib_display as usize)
|
.nth(xcb_connection.xlib_display as usize)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Get the DPI from the screen struct
|
// Get the DPI from the screen struct
|
||||||
|
@ -418,7 +299,19 @@ impl Window {
|
||||||
let _xres = width_px * 25.4 / width_mm;
|
let _xres = width_px * 25.4 / width_mm;
|
||||||
let yres = height_px * 25.4 / height_mm;
|
let yres = height_px * 25.4 / height_mm;
|
||||||
|
|
||||||
|
let yscale = yres / 96.0;
|
||||||
|
|
||||||
// TODO: choose between `xres` and `yres`? (probably both are the same?)
|
// TODO: choose between `xres` and `yres`? (probably both are the same?)
|
||||||
Some(yres)
|
Some(yscale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_id(id: u8) -> MouseButtonID {
|
||||||
|
match id {
|
||||||
|
1 => MouseButtonID::Left,
|
||||||
|
2 => MouseButtonID::Middle,
|
||||||
|
3 => MouseButtonID::Right,
|
||||||
|
6 => MouseButtonID::Back,
|
||||||
|
7 => MouseButtonID::Forward,
|
||||||
|
id => MouseButtonID::Other(id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue