Add compiler arguments for choosing between OpenGL or wgpu. Add Receiver and Message types.
This commit is contained in:
parent
efce8c5ec9
commit
9234f050c0
17
Cargo.toml
17
Cargo.toml
|
@ -6,16 +6,29 @@ 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"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["gl_renderer"]
|
||||||
|
gl_renderer = ["gl"]
|
||||||
|
wgpu_renderer = ["wgpu"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gl = "0.14.0"
|
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
|
||||||
|
[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.3", features = ["xlib", "glx"]}
|
x11 = { version = "2.18", features = ["xlib", "glx"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|
||||||
[target.'cfg(target_os="windows")'.dependencies]
|
[target.'cfg(target_os="windows")'.dependencies]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use baseview::Message;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let window_open_options = baseview::WindowOpenOptions {
|
let window_open_options = baseview::WindowOpenOptions {
|
||||||
title: "baseview",
|
title: "baseview",
|
||||||
|
@ -6,5 +8,57 @@ fn main() {
|
||||||
parent: baseview::Parent::None,
|
parent: baseview::Parent::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
baseview::Window::open(window_open_options);
|
let my_program = MyProgram {};
|
||||||
|
|
||||||
|
baseview::Window::open(window_open_options, my_program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MyProgram {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl baseview::Receiver for MyProgram {
|
||||||
|
fn on_message(&mut self, message: Message) {
|
||||||
|
match message {
|
||||||
|
Message::CursorMotion(x, y) => {
|
||||||
|
println!("Cursor moved, x: {}, y: {}", x, y);
|
||||||
|
},
|
||||||
|
Message::MouseDown(button_id) => {
|
||||||
|
println!("Mouse down, button id: {:?}", button_id);
|
||||||
|
},
|
||||||
|
Message::MouseUp(button_id) => {
|
||||||
|
println!("Mouse up, button id: {:?}", button_id);
|
||||||
|
},
|
||||||
|
Message::MouseScroll(mouse_scroll) => {
|
||||||
|
println!("Mouse scroll, {:?}", mouse_scroll);
|
||||||
|
},
|
||||||
|
Message::MouseClick(mouse_click) => {
|
||||||
|
println!("Mouse click, {:?}", mouse_click);
|
||||||
|
}
|
||||||
|
Message::KeyDown(keycode) => {
|
||||||
|
println!("Key down, keycode: {}", keycode);
|
||||||
|
},
|
||||||
|
Message::KeyUp(keycode) => {
|
||||||
|
println!("Key up, keycode: {}", keycode);
|
||||||
|
},
|
||||||
|
Message::CharacterInput(char_code) => {
|
||||||
|
println!("Character input, char_code: {}", char_code);
|
||||||
|
},
|
||||||
|
Message::WindowResized(window_info) => {
|
||||||
|
println!("Window resized, {:?}", window_info);
|
||||||
|
},
|
||||||
|
Message::WindowFocus => {
|
||||||
|
println!("Window focused");
|
||||||
|
},
|
||||||
|
Message::WindowUnfocus => {
|
||||||
|
println!("Window unfocused");
|
||||||
|
},
|
||||||
|
Message::Opened(window_info) => {
|
||||||
|
println!("Window opened, {:?}", window_info);
|
||||||
|
},
|
||||||
|
Message::WillClose => {
|
||||||
|
println!("Window will close");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,9 @@ mod macos;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub use macos::*;
|
pub use macos::*;
|
||||||
|
|
||||||
|
mod message;
|
||||||
|
pub use message::*;
|
||||||
|
|
||||||
pub enum Parent {
|
pub enum Parent {
|
||||||
None,
|
None,
|
||||||
AsIfParented,
|
AsIfParented,
|
||||||
|
@ -29,3 +32,7 @@ pub struct WindowOpenOptions<'a> {
|
||||||
|
|
||||||
pub parent: Parent,
|
pub parent: Parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Receiver {
|
||||||
|
fn on_message(&mut self, message: Message);
|
||||||
|
}
|
|
@ -6,12 +6,14 @@ 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::{WindowOpenOptions, Message, Receiver, MouseButtonID, MouseScroll};
|
||||||
|
|
||||||
pub struct Window;
|
pub struct Window<R: Receiver> {
|
||||||
|
receiver: R,
|
||||||
|
}
|
||||||
|
|
||||||
impl Window {
|
impl<R: Receiver> Window<R> {
|
||||||
pub fn open(options: WindowOpenOptions) -> Self {
|
pub fn open(options: WindowOpenOptions, receiver: R) -> Self {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _pool = NSAutoreleasePool::new(nil);
|
let _pool = NSAutoreleasePool::new(nil);
|
||||||
|
|
||||||
|
@ -42,7 +44,17 @@ impl Window {
|
||||||
current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
|
current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
|
||||||
app.run();
|
app.run();
|
||||||
|
|
||||||
Window
|
receiver.on_message(Message::Opened(
|
||||||
|
crate::message::WindowInfo {
|
||||||
|
width: options.width as u32,
|
||||||
|
height: options.height as u32,
|
||||||
|
dpi: None,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
Window {
|
||||||
|
receiver,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
src/message.rs
Normal file
54
src/message.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#[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 enum MouseClickType {
|
||||||
|
Single,
|
||||||
|
Double,
|
||||||
|
Triple,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct MouseClick {
|
||||||
|
pub id: MouseButtonID,
|
||||||
|
pub click_type: MouseClickType,
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WindowInfo {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub dpi: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Message {
|
||||||
|
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,
|
||||||
|
Opened(WindowInfo),
|
||||||
|
WillClose,
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ use self::winapi::um::winuser::{
|
||||||
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::{Message, Receiver, 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) {
|
||||||
|
@ -111,24 +112,28 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
|
||||||
|
|
||||||
unsafe fn init_gl_context() {}
|
unsafe fn init_gl_context() {}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window<R: Receiver> {
|
||||||
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".
|
||||||
r: f32,
|
r: f32,
|
||||||
g: f32,
|
g: f32,
|
||||||
b: f32,
|
b: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl<R: Receiver> Window<R> {
|
||||||
pub fn open(options: WindowOpenOptions) {
|
pub fn open(options: WindowOpenOptions, receiver: R) {
|
||||||
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,
|
||||||
|
receiver,
|
||||||
|
scaling: None,
|
||||||
r: 0.3,
|
r: 0.3,
|
||||||
g: 0.8,
|
g: 0.8,
|
||||||
b: 0.3,
|
b: 0.3,
|
||||||
|
@ -236,6 +241,14 @@ impl Window {
|
||||||
|
|
||||||
SetTimer(hwnd, 4242, 13, None);
|
SetTimer(hwnd, 4242, 13, None);
|
||||||
|
|
||||||
|
window.receiver.on_message(Message::Opened(
|
||||||
|
crate::message::WindowInfo {
|
||||||
|
width: options.width as u32,
|
||||||
|
height: options.height as u32,
|
||||||
|
dpi: window.scaling,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
// 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() {
|
||||||
let mut msg: MSG = std::mem::zeroed();
|
let mut msg: MSG = std::mem::zeroed();
|
||||||
|
@ -252,6 +265,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
|
self.receiver.on_message(Message::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 +285,14 @@ 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.receiver.on_message(Message::CursorMotion(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
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::*;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
|
||||||
|
mod opengl_util;
|
|
@ -1,10 +1,114 @@
|
||||||
use std::ffi::CString;
|
use std::ffi::{CString, CStr};
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
use ::x11::{glx, xlib};
|
use ::x11::{glx, xlib};
|
||||||
|
|
||||||
use super::XcbConnection;
|
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(
|
pub type GlXCreateContextAttribsARBProc = unsafe extern "C" fn(
|
||||||
dpy: *mut xlib::Display,
|
dpy: *mut xlib::Display,
|
||||||
fbc: glx::GLXFBConfig,
|
fbc: glx::GLXFBConfig,
|
||||||
|
@ -15,7 +119,7 @@ pub type GlXCreateContextAttribsARBProc = unsafe extern "C" fn(
|
||||||
|
|
||||||
// Check to make sure this system supports the correct version of GLX (>= 1.3 for now)
|
// 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
|
// For now it just panics if not, but TODO: do correct error handling
|
||||||
pub fn check_glx_version(xcb_connection: &XcbConnection) {
|
fn check_glx_version(xcb_connection: &XcbConnection) {
|
||||||
let raw_display = xcb_connection.conn.get_raw_dpy();
|
let raw_display = xcb_connection.conn.get_raw_dpy();
|
||||||
let mut maj: c_int = 0;
|
let mut maj: c_int = 0;
|
||||||
let mut min: c_int = 0;
|
let mut min: c_int = 0;
|
||||||
|
@ -32,7 +136,7 @@ pub fn check_glx_version(xcb_connection: &XcbConnection) {
|
||||||
|
|
||||||
// Get GLX framebuffer config
|
// Get GLX framebuffer config
|
||||||
// History: https://stackoverflow.com/questions/51558473/whats-the-difference-between-a-glx-visual-and-a-fbconfig
|
// 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 {
|
fn get_glxfbconfig(xcb_connection: &XcbConnection, visual_attribs: &[i32]) -> glx::GLXFBConfig {
|
||||||
let raw_display = xcb_connection.conn.get_raw_dpy();
|
let raw_display = xcb_connection.conn.get_raw_dpy();
|
||||||
let xlib_display = xcb_connection.xlib_display;
|
let xlib_display = xcb_connection.xlib_display;
|
||||||
|
|
||||||
|
@ -64,3 +168,18 @@ pub unsafe fn load_gl_func(name: &str) -> *mut c_void {
|
||||||
}
|
}
|
||||||
ptr
|
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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,21 +9,26 @@ use std::ffi::CStr;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::{c_int, c_void};
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
use ::x11::{glx, 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::opengl_util;
|
||||||
use super::XcbConnection;
|
|
||||||
use crate::Parent;
|
|
||||||
use crate::WindowOpenOptions;
|
|
||||||
|
|
||||||
pub struct Window {
|
use super::XcbConnection;
|
||||||
|
use crate::{Parent, WindowOpenOptions, Message, Receiver, MouseButtonID, MouseScroll};
|
||||||
|
|
||||||
|
pub struct Window<R: Receiver> {
|
||||||
xcb_connection: XcbConnection,
|
xcb_connection: XcbConnection,
|
||||||
scaling: Option<f64>, // DPI scale, 96.0 is "default".
|
scaling: Option<f64>, // DPI scale, 96.0 is "default".
|
||||||
|
receiver: R,
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "wgpu_renderer"), feature = "gl_renderer"))]
|
||||||
|
ctx: *mut x11::glx::__GLXcontextRec,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl<R: Receiver> Window<R> {
|
||||||
pub fn open(options: WindowOpenOptions) -> Self {
|
pub fn open(options: WindowOpenOptions, receiver: R) -> 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,32 +39,12 @@ 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)
|
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
|
||||||
opengl_util::check_glx_version(&xcb_connection);
|
let fb_config = opengl_util::fb_config(&xcb_connection);
|
||||||
|
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
|
||||||
// Get GLX framebuffer config (requires GLX >= 1.3)
|
let x_visual_info: *const xlib::XVisualInfo = {
|
||||||
#[rustfmt::skip]
|
opengl_util::x_visual_info(&xcb_connection, fb_config)
|
||||||
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.
|
// 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
|
||||||
|
@ -157,36 +142,6 @@ impl Window {
|
||||||
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.
|
// 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
|
// installing an event handler to check if error is generated
|
||||||
|
@ -198,32 +153,8 @@ impl Window {
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Create GLX context attributes. (?)
|
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
|
||||||
let context_attribs: [c_int; 5] = [
|
let ctx = opengl_util::glx_context(&xcb_connection, fb_config);
|
||||||
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
|
// Display the window
|
||||||
xcb::map_window(&xcb_connection.conn, window_id);
|
xcb::map_window(&xcb_connection.conn, window_id);
|
||||||
|
@ -232,22 +163,36 @@ impl Window {
|
||||||
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 mut x11_window = Self {
|
let mut x11_window = Self {
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
scaling: None,
|
scaling: None,
|
||||||
|
ctx,
|
||||||
|
receiver,
|
||||||
};
|
};
|
||||||
|
|
||||||
x11_window.scaling = x11_window
|
x11_window.scaling = x11_window
|
||||||
.get_scaling_xft()
|
.get_scaling_xft()
|
||||||
.or(x11_window.get_scaling_screen_dimensions());
|
.or(x11_window.get_scaling_screen_dimensions());
|
||||||
println!("Scale factor: {:?}", x11_window.scaling);
|
println!("Scale factor: {:?}", x11_window.scaling);
|
||||||
x11_window.handle_events(window_id, ctx);
|
|
||||||
|
x11_window.receiver.on_message(Message::Opened(
|
||||||
|
crate::message::WindowInfo {
|
||||||
|
width: options.width as u32,
|
||||||
|
height: options.height as u32,
|
||||||
|
dpi: x11_window.scaling,
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
x11_window.handle_events(window_id);
|
||||||
|
|
||||||
|
x11_window.receiver.on_message(Message::WillClose);
|
||||||
|
|
||||||
return x11_window;
|
return x11_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event loop
|
// Event loop
|
||||||
fn handle_events(&self, window_id: u32, ctx: *mut x11::glx::__GLXcontextRec) {
|
fn handle_events(&mut self, window_id: u32) {
|
||||||
let raw_display = self.xcb_connection.conn.get_raw_dpy();
|
let raw_display = self.xcb_connection.conn.get_raw_dpy();
|
||||||
loop {
|
loop {
|
||||||
let ev = self.xcb_connection.conn.wait_for_event();
|
let ev = self.xcb_connection.conn.wait_for_event();
|
||||||
|
@ -275,65 +220,68 @@ 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);
|
#[cfg(all(feature = "gl_renderer", not(feature = "wgpu_renderer")))]
|
||||||
gl::ClearColor(0.3, 0.8, 0.3, 1.0);
|
opengl_util::xcb_expose(window_id, raw_display, self.ctx);
|
||||||
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.receiver.on_message(Message::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.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 => {
|
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.receiver.on_message(Message::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.receiver.on_message(Message::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.receiver.on_message(Message::KeyUp(detail));
|
||||||
"Keyboard key released: ({}, {}) -- {} / {}",
|
|
||||||
x, y, detail, state
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unhandled event type: {:?}", event_type);
|
println!("Unhandled event type: {:?}", event_type);
|
||||||
|
@ -422,3 +370,14 @@ impl Window {
|
||||||
Some(yres)
|
Some(yres)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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