Merge branch 'master' into macos-scrollwheel
This commit is contained in:
commit
3dd255f238
6
.github/workflows/rust.yml
vendored
6
.github/workflows/rust.yml
vendored
|
@ -21,7 +21,9 @@ jobs:
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
- name: Build
|
- name: Build with default features
|
||||||
run: cargo build --examples --workspace --verbose
|
run: cargo build --examples --workspace --verbose
|
||||||
|
- name: Build again with all features
|
||||||
|
run: cargo build --examples --workspace --all-features --verbose
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --examples --workspace --verbose
|
run: cargo test --examples --workspace --all-features --verbose
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -9,22 +9,28 @@ authors = [
|
||||||
"Billy Messenger <billydm@protonmail.com>",
|
"Billy Messenger <billydm@protonmail.com>",
|
||||||
"Anton Lazarev <https://antonok.com>",
|
"Anton Lazarev <https://antonok.com>",
|
||||||
"Joakim Frostegård <joakim.frostegard@gmail.com>",
|
"Joakim Frostegård <joakim.frostegard@gmail.com>",
|
||||||
|
"Robbert van der Helm <mail@robbertvanderhelm.nl>",
|
||||||
]
|
]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
opengl = ["uuid", "x11/glx"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
keyboard-types = { version = "0.5.0", default-features = false }
|
keyboard-types = { version = "0.6.1", default-features = false }
|
||||||
raw-window-handle = "0.3.3"
|
raw-window-handle = "0.4.2"
|
||||||
|
|
||||||
[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", "xcursor"] }
|
x11 = { version = "2.18", features = ["xlib", "xcursor"] }
|
||||||
xcb-util = { version = "0.3", features = ["icccm"] }
|
xcb-util = { version = "0.3", features = ["icccm"] }
|
||||||
nix = "0.18"
|
nix = "0.22.0"
|
||||||
|
|
||||||
[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"] }
|
||||||
|
uuid = { version = "0.8", features = ["v4"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(target_os="macos")'.dependencies]
|
[target.'cfg(target_os="macos")'.dependencies]
|
||||||
cocoa = "0.24.0"
|
cocoa = "0.24.0"
|
||||||
|
|
15
README.md
15
README.md
|
@ -10,13 +10,14 @@ Interested in learning more about the project? Join us on [discord](https://disc
|
||||||
|
|
||||||
Below is a proposed list of milestones (roughly in-order) and their status. Subject to change at any time.
|
Below is a proposed list of milestones (roughly in-order) and their status. Subject to change at any time.
|
||||||
|
|
||||||
| Feature | Windows | Mac OS | Linux |
|
| Feature | Windows | Mac OS | Linux |
|
||||||
| ----------------------------------------------- | ------------------ | ------------------ | ------------------ |
|
| ----------------------------------------------------- | ------------------ | ------------------ | ------------------ |
|
||||||
| Spawns a window, no parent | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
| Spawns a window, no parent | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
| Cross-platform API for window spawning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
| Cross-platform API for window spawning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
| Can find DPI scale factor | | :heavy_check_mark: | :heavy_check_mark: |
|
| Can find DPI scale factor | | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
| Basic event handling (mouse, keyboard) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
| Basic event handling (mouse, keyboard) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
| Parent window support | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
| Parent window support | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
|
| OpenGL context creation (behind the `opengl` feature) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ fn main() {
|
||||||
title: "baseview".into(),
|
title: "baseview".into(),
|
||||||
size: baseview::Size::new(512.0, 512.0),
|
size: baseview::Size::new(512.0, 512.0),
|
||||||
scale: WindowScalePolicy::SystemScaleFactor,
|
scale: WindowScalePolicy::SystemScaleFactor,
|
||||||
|
|
||||||
|
// TODO: Add an example that uses the OpenGL context
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_config: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut tx, rx) = RingBuffer::new(128);
|
let (mut tx, rx) = RingBuffer::new(128);
|
||||||
|
|
146
src/gl/macos.rs
Normal file
146
src/gl/macos.rs
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||||
|
|
||||||
|
use cocoa::appkit::{
|
||||||
|
NSOpenGLContext, NSOpenGLContextParameter, NSOpenGLPFAAccelerated, NSOpenGLPFAAlphaSize,
|
||||||
|
NSOpenGLPFAColorSize, NSOpenGLPFADepthSize, NSOpenGLPFADoubleBuffer, NSOpenGLPFAMultisample,
|
||||||
|
NSOpenGLPFAOpenGLProfile, NSOpenGLPFASampleBuffers, NSOpenGLPFASamples, NSOpenGLPFAStencilSize,
|
||||||
|
NSOpenGLPixelFormat, NSOpenGLProfileVersion3_2Core, NSOpenGLProfileVersion4_1Core,
|
||||||
|
NSOpenGLProfileVersionLegacy, NSOpenGLView, NSView,
|
||||||
|
};
|
||||||
|
use cocoa::base::{id, nil, YES};
|
||||||
|
|
||||||
|
use core_foundation::base::TCFType;
|
||||||
|
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
|
||||||
|
use core_foundation::string::CFString;
|
||||||
|
|
||||||
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
|
use super::{GlConfig, GlError, Profile};
|
||||||
|
|
||||||
|
pub type CreationFailedError = ();
|
||||||
|
pub struct GlContext {
|
||||||
|
view: id,
|
||||||
|
context: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlContext {
|
||||||
|
pub unsafe fn create(
|
||||||
|
parent: &impl HasRawWindowHandle, config: GlConfig,
|
||||||
|
) -> Result<GlContext, GlError> {
|
||||||
|
let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
return Err(GlError::InvalidWindowHandle);
|
||||||
|
};
|
||||||
|
|
||||||
|
if handle.ns_view.is_null() {
|
||||||
|
return Err(GlError::InvalidWindowHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent_view = handle.ns_view as id;
|
||||||
|
|
||||||
|
let version = if config.version < (3, 2) && config.profile == Profile::Compatibility {
|
||||||
|
NSOpenGLProfileVersionLegacy
|
||||||
|
} else if config.version == (3, 2) && config.profile == Profile::Core {
|
||||||
|
NSOpenGLProfileVersion3_2Core
|
||||||
|
} else if config.version > (3, 2) && config.profile == Profile::Core {
|
||||||
|
NSOpenGLProfileVersion4_1Core
|
||||||
|
} else {
|
||||||
|
return Err(GlError::VersionNotSupported);
|
||||||
|
};
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let mut attrs = vec![
|
||||||
|
NSOpenGLPFAOpenGLProfile as u32, version as u32,
|
||||||
|
NSOpenGLPFAColorSize as u32, (config.red_bits + config.blue_bits + config.green_bits) as u32,
|
||||||
|
NSOpenGLPFAAlphaSize as u32, config.alpha_bits as u32,
|
||||||
|
NSOpenGLPFADepthSize as u32, config.depth_bits as u32,
|
||||||
|
NSOpenGLPFAStencilSize as u32, config.stencil_bits as u32,
|
||||||
|
NSOpenGLPFAAccelerated as u32,
|
||||||
|
];
|
||||||
|
|
||||||
|
if config.samples.is_some() {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
attrs.extend_from_slice(&[
|
||||||
|
NSOpenGLPFAMultisample as u32,
|
||||||
|
NSOpenGLPFASampleBuffers as u32, 1,
|
||||||
|
NSOpenGLPFASamples as u32, config.samples.unwrap() as u32,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.double_buffer {
|
||||||
|
attrs.push(NSOpenGLPFADoubleBuffer as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs.push(0);
|
||||||
|
|
||||||
|
let pixel_format = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attrs);
|
||||||
|
|
||||||
|
if pixel_format == nil {
|
||||||
|
return Err(GlError::CreationFailed(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let view =
|
||||||
|
NSOpenGLView::alloc(nil).initWithFrame_pixelFormat_(parent_view.frame(), pixel_format);
|
||||||
|
|
||||||
|
if view == nil {
|
||||||
|
return Err(GlError::CreationFailed(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||||
|
|
||||||
|
let () = msg_send![view, retain];
|
||||||
|
NSOpenGLView::display_(view);
|
||||||
|
parent_view.addSubview_(view);
|
||||||
|
|
||||||
|
let context: id = msg_send![view, openGLContext];
|
||||||
|
let () = msg_send![context, retain];
|
||||||
|
|
||||||
|
context.setValues_forParameter_(
|
||||||
|
&(config.vsync as i32),
|
||||||
|
NSOpenGLContextParameter::NSOpenGLCPSwapInterval,
|
||||||
|
);
|
||||||
|
|
||||||
|
let () = msg_send![pixel_format, release];
|
||||||
|
|
||||||
|
Ok(GlContext { view, context })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_current(&self) {
|
||||||
|
self.context.makeCurrentContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_not_current(&self) {
|
||||||
|
NSOpenGLContext::clearCurrentContext(self.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
|
let symbol_name = CFString::from_str(symbol).unwrap();
|
||||||
|
let framework_name = CFString::from_str("com.apple.opengl").unwrap();
|
||||||
|
let framework =
|
||||||
|
unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) };
|
||||||
|
let addr = unsafe {
|
||||||
|
CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
|
||||||
|
};
|
||||||
|
addr as *const c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_buffers(&self) {
|
||||||
|
unsafe {
|
||||||
|
self.context.flushBuffer();
|
||||||
|
let () = msg_send![self.view, setNeedsDisplay: YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GlContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let () = msg_send![self.context, release];
|
||||||
|
let () = msg_send![self.view, release];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
src/gl/mod.rs
Normal file
109
src/gl/mod.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
// On X11 creating the context is a two step process
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
use raw_window_handle::HasRawWindowHandle;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
mod win;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use win as platform;
|
||||||
|
|
||||||
|
// We need to use this directly within the X11 window creation to negotiate the correct visual
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub(crate) mod x11;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub(crate) use self::x11 as platform;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod macos;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use macos as platform;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct GlConfig {
|
||||||
|
pub version: (u8, u8),
|
||||||
|
pub profile: Profile,
|
||||||
|
pub red_bits: u8,
|
||||||
|
pub blue_bits: u8,
|
||||||
|
pub green_bits: u8,
|
||||||
|
pub alpha_bits: u8,
|
||||||
|
pub depth_bits: u8,
|
||||||
|
pub stencil_bits: u8,
|
||||||
|
pub samples: Option<u8>,
|
||||||
|
pub srgb: bool,
|
||||||
|
pub double_buffer: bool,
|
||||||
|
pub vsync: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GlConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
GlConfig {
|
||||||
|
version: (3, 2),
|
||||||
|
profile: Profile::Core,
|
||||||
|
red_bits: 8,
|
||||||
|
blue_bits: 8,
|
||||||
|
green_bits: 8,
|
||||||
|
alpha_bits: 8,
|
||||||
|
depth_bits: 24,
|
||||||
|
stencil_bits: 8,
|
||||||
|
samples: None,
|
||||||
|
srgb: true,
|
||||||
|
double_buffer: true,
|
||||||
|
vsync: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Profile {
|
||||||
|
Compatibility,
|
||||||
|
Core,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GlError {
|
||||||
|
InvalidWindowHandle,
|
||||||
|
VersionNotSupported,
|
||||||
|
CreationFailed(platform::CreationFailedError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlContext {
|
||||||
|
context: platform::GlContext,
|
||||||
|
phantom: PhantomData<*mut ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlContext {
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub(crate) unsafe fn create(
|
||||||
|
parent: &impl HasRawWindowHandle, config: GlConfig,
|
||||||
|
) -> Result<GlContext, GlError> {
|
||||||
|
platform::GlContext::create(parent, config)
|
||||||
|
.map(|context| GlContext { context, phantom: PhantomData })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The X11 version needs to be set up in a different way compared to the Windows and macOS
|
||||||
|
/// versions. So the platform-specific versions should be used to construct the context within
|
||||||
|
/// baseview, and then this object can be passed to the user.
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub(crate) fn new(context: platform::GlContext) -> GlContext {
|
||||||
|
GlContext { context, phantom: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_current(&self) {
|
||||||
|
self.context.make_current();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_not_current(&self) {
|
||||||
|
self.context.make_not_current();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
|
self.context.get_proc_address(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_buffers(&self) {
|
||||||
|
self.context.swap_buffers();
|
||||||
|
}
|
||||||
|
}
|
306
src/gl/win.rs
Normal file
306
src/gl/win.rs
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
use std::ffi::{c_void, CString, OsStr};
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
|
||||||
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||||
|
|
||||||
|
use winapi::shared::minwindef::{HINSTANCE, HMODULE};
|
||||||
|
use winapi::shared::ntdef::WCHAR;
|
||||||
|
use winapi::shared::windef::{HDC, HGLRC, HWND};
|
||||||
|
use winapi::um::libloaderapi::{FreeLibrary, GetProcAddress, LoadLibraryA};
|
||||||
|
use winapi::um::wingdi::{
|
||||||
|
wglCreateContext, wglDeleteContext, wglGetProcAddress, wglMakeCurrent, ChoosePixelFormat,
|
||||||
|
DescribePixelFormat, SetPixelFormat, SwapBuffers, PFD_DOUBLEBUFFER, PFD_DRAW_TO_WINDOW,
|
||||||
|
PFD_MAIN_PLANE, PFD_SUPPORT_OPENGL, PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR,
|
||||||
|
};
|
||||||
|
use winapi::um::winnt::IMAGE_DOS_HEADER;
|
||||||
|
use winapi::um::winuser::{
|
||||||
|
CreateWindowExW, DefWindowProcW, DestroyWindow, GetDC, RegisterClassW, ReleaseDC,
|
||||||
|
UnregisterClassW, CS_OWNDC, CW_USEDEFAULT, WNDCLASSW,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{GlConfig, GlError, Profile};
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt
|
||||||
|
|
||||||
|
type WglCreateContextAttribsARB = extern "system" fn(HDC, HGLRC, *const i32) -> HGLRC;
|
||||||
|
|
||||||
|
const WGL_CONTEXT_MAJOR_VERSION_ARB: i32 = 0x2091;
|
||||||
|
const WGL_CONTEXT_MINOR_VERSION_ARB: i32 = 0x2092;
|
||||||
|
const WGL_CONTEXT_PROFILE_MASK_ARB: i32 = 0x9126;
|
||||||
|
|
||||||
|
const WGL_CONTEXT_CORE_PROFILE_BIT_ARB: i32 = 0x00000001;
|
||||||
|
const WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB: i32 = 0x00000002;
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt
|
||||||
|
|
||||||
|
type WglChoosePixelFormatARB =
|
||||||
|
extern "system" fn(HDC, *const i32, *const f32, u32, *mut i32, *mut u32) -> i32;
|
||||||
|
|
||||||
|
const WGL_DRAW_TO_WINDOW_ARB: i32 = 0x2001;
|
||||||
|
const WGL_ACCELERATION_ARB: i32 = 0x2003;
|
||||||
|
const WGL_SUPPORT_OPENGL_ARB: i32 = 0x2010;
|
||||||
|
const WGL_DOUBLE_BUFFER_ARB: i32 = 0x2011;
|
||||||
|
const WGL_PIXEL_TYPE_ARB: i32 = 0x2013;
|
||||||
|
const WGL_RED_BITS_ARB: i32 = 0x2015;
|
||||||
|
const WGL_GREEN_BITS_ARB: i32 = 0x2017;
|
||||||
|
const WGL_BLUE_BITS_ARB: i32 = 0x2019;
|
||||||
|
const WGL_ALPHA_BITS_ARB: i32 = 0x201B;
|
||||||
|
const WGL_DEPTH_BITS_ARB: i32 = 0x2022;
|
||||||
|
const WGL_STENCIL_BITS_ARB: i32 = 0x2023;
|
||||||
|
|
||||||
|
const WGL_FULL_ACCELERATION_ARB: i32 = 0x2027;
|
||||||
|
const WGL_TYPE_RGBA_ARB: i32 = 0x202B;
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_multisample.txt
|
||||||
|
|
||||||
|
const WGL_SAMPLE_BUFFERS_ARB: i32 = 0x2041;
|
||||||
|
const WGL_SAMPLES_ARB: i32 = 0x2042;
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt
|
||||||
|
|
||||||
|
const WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20A9;
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/EXT/WGL_EXT_swap_control.txt
|
||||||
|
|
||||||
|
type WglSwapIntervalEXT = extern "system" fn(i32) -> i32;
|
||||||
|
|
||||||
|
pub type CreationFailedError = ();
|
||||||
|
pub struct GlContext {
|
||||||
|
hwnd: HWND,
|
||||||
|
hdc: HDC,
|
||||||
|
hglrc: HGLRC,
|
||||||
|
gl_library: HMODULE,
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
static __ImageBase: IMAGE_DOS_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlContext {
|
||||||
|
pub unsafe fn create(
|
||||||
|
parent: &impl HasRawWindowHandle, config: GlConfig,
|
||||||
|
) -> Result<GlContext, GlError> {
|
||||||
|
let handle = if let RawWindowHandle::Win32(handle) = parent.raw_window_handle() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
return Err(GlError::InvalidWindowHandle);
|
||||||
|
};
|
||||||
|
|
||||||
|
if handle.hwnd.is_null() {
|
||||||
|
return Err(GlError::InvalidWindowHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create temporary window and context to load function pointers
|
||||||
|
|
||||||
|
let class_name_str = format!("raw-gl-context-window-{}", uuid::Uuid::new_v4().to_simple());
|
||||||
|
let mut class_name: Vec<WCHAR> = OsStr::new(&class_name_str).encode_wide().collect();
|
||||||
|
class_name.push(0);
|
||||||
|
|
||||||
|
let hinstance = &__ImageBase as *const IMAGE_DOS_HEADER as HINSTANCE;
|
||||||
|
|
||||||
|
let wnd_class = WNDCLASSW {
|
||||||
|
style: CS_OWNDC,
|
||||||
|
lpfnWndProc: Some(DefWindowProcW),
|
||||||
|
hInstance: hinstance,
|
||||||
|
lpszClassName: class_name.as_ptr(),
|
||||||
|
..std::mem::zeroed()
|
||||||
|
};
|
||||||
|
|
||||||
|
let class = RegisterClassW(&wnd_class);
|
||||||
|
if class == 0 {
|
||||||
|
return Err(GlError::CreationFailed(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let hwnd_tmp = CreateWindowExW(
|
||||||
|
0,
|
||||||
|
class as *const WCHAR,
|
||||||
|
[0].as_ptr(),
|
||||||
|
0,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
hinstance,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if hwnd_tmp.is_null() {
|
||||||
|
return Err(GlError::CreationFailed(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let hdc_tmp = GetDC(hwnd_tmp);
|
||||||
|
|
||||||
|
let pfd_tmp = PIXELFORMATDESCRIPTOR {
|
||||||
|
nSize: std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u16,
|
||||||
|
nVersion: 1,
|
||||||
|
dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||||
|
iPixelType: PFD_TYPE_RGBA,
|
||||||
|
cColorBits: 32,
|
||||||
|
cAlphaBits: 8,
|
||||||
|
cDepthBits: 24,
|
||||||
|
cStencilBits: 8,
|
||||||
|
iLayerType: PFD_MAIN_PLANE,
|
||||||
|
..std::mem::zeroed()
|
||||||
|
};
|
||||||
|
|
||||||
|
SetPixelFormat(hdc_tmp, ChoosePixelFormat(hdc_tmp, &pfd_tmp), &pfd_tmp);
|
||||||
|
|
||||||
|
let hglrc_tmp = wglCreateContext(hdc_tmp);
|
||||||
|
if hglrc_tmp.is_null() {
|
||||||
|
ReleaseDC(hwnd_tmp, hdc_tmp);
|
||||||
|
UnregisterClassW(class as *const WCHAR, hinstance);
|
||||||
|
DestroyWindow(hwnd_tmp);
|
||||||
|
return Err(GlError::CreationFailed(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
wglMakeCurrent(hdc_tmp, hglrc_tmp);
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let wglCreateContextAttribsARB: Option<WglCreateContextAttribsARB> = {
|
||||||
|
let symbol = CString::new("wglCreateContextAttribsARB").unwrap();
|
||||||
|
let addr = wglGetProcAddress(symbol.as_ptr());
|
||||||
|
if !addr.is_null() {
|
||||||
|
Some(std::mem::transmute(addr))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let wglChoosePixelFormatARB: Option<WglChoosePixelFormatARB> = {
|
||||||
|
let symbol = CString::new("wglChoosePixelFormatARB").unwrap();
|
||||||
|
let addr = wglGetProcAddress(symbol.as_ptr());
|
||||||
|
if !addr.is_null() {
|
||||||
|
Some(std::mem::transmute(addr))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let wglSwapIntervalEXT: Option<WglSwapIntervalEXT> = {
|
||||||
|
let symbol = CString::new("wglSwapIntervalEXT").unwrap();
|
||||||
|
let addr = wglGetProcAddress(symbol.as_ptr());
|
||||||
|
if !addr.is_null() {
|
||||||
|
Some(std::mem::transmute(addr))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
wglMakeCurrent(hdc_tmp, std::ptr::null_mut());
|
||||||
|
ReleaseDC(hwnd_tmp, hdc_tmp);
|
||||||
|
UnregisterClassW(class as *const WCHAR, hinstance);
|
||||||
|
DestroyWindow(hwnd_tmp);
|
||||||
|
|
||||||
|
// Create actual context
|
||||||
|
|
||||||
|
let hwnd = handle.hwnd as HWND;
|
||||||
|
|
||||||
|
let hdc = GetDC(hwnd);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let pixel_format_attribs = [
|
||||||
|
WGL_DRAW_TO_WINDOW_ARB, 1,
|
||||||
|
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
|
||||||
|
WGL_SUPPORT_OPENGL_ARB, 1,
|
||||||
|
WGL_DOUBLE_BUFFER_ARB, config.double_buffer as i32,
|
||||||
|
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||||
|
WGL_RED_BITS_ARB, config.red_bits as i32,
|
||||||
|
WGL_GREEN_BITS_ARB, config.green_bits as i32,
|
||||||
|
WGL_BLUE_BITS_ARB, config.blue_bits as i32,
|
||||||
|
WGL_ALPHA_BITS_ARB, config.alpha_bits as i32,
|
||||||
|
WGL_DEPTH_BITS_ARB, config.depth_bits as i32,
|
||||||
|
WGL_STENCIL_BITS_ARB, config.stencil_bits as i32,
|
||||||
|
WGL_SAMPLE_BUFFERS_ARB, config.samples.is_some() as i32,
|
||||||
|
WGL_SAMPLES_ARB, config.samples.unwrap_or(0) as i32,
|
||||||
|
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, config.srgb as i32,
|
||||||
|
0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut pixel_format = 0;
|
||||||
|
let mut num_formats = 0;
|
||||||
|
wglChoosePixelFormatARB.unwrap()(
|
||||||
|
hdc,
|
||||||
|
pixel_format_attribs.as_ptr(),
|
||||||
|
std::ptr::null(),
|
||||||
|
1,
|
||||||
|
&mut pixel_format,
|
||||||
|
&mut num_formats,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut pfd: PIXELFORMATDESCRIPTOR = std::mem::zeroed();
|
||||||
|
DescribePixelFormat(
|
||||||
|
hdc,
|
||||||
|
pixel_format,
|
||||||
|
std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u32,
|
||||||
|
&mut pfd,
|
||||||
|
);
|
||||||
|
SetPixelFormat(hdc, pixel_format, &pfd);
|
||||||
|
|
||||||
|
let profile_mask = match config.profile {
|
||||||
|
Profile::Core => WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||||
|
Profile::Compatibility => WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let ctx_attribs = [
|
||||||
|
WGL_CONTEXT_MAJOR_VERSION_ARB, config.version.0 as i32,
|
||||||
|
WGL_CONTEXT_MINOR_VERSION_ARB, config.version.1 as i32,
|
||||||
|
WGL_CONTEXT_PROFILE_MASK_ARB, profile_mask,
|
||||||
|
0
|
||||||
|
];
|
||||||
|
|
||||||
|
let hglrc =
|
||||||
|
wglCreateContextAttribsARB.unwrap()(hdc, std::ptr::null_mut(), ctx_attribs.as_ptr());
|
||||||
|
if hglrc.is_null() {
|
||||||
|
return Err(GlError::CreationFailed(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let gl_library_name = CString::new("opengl32.dll").unwrap();
|
||||||
|
let gl_library = LoadLibraryA(gl_library_name.as_ptr());
|
||||||
|
|
||||||
|
wglMakeCurrent(hdc, hglrc);
|
||||||
|
wglSwapIntervalEXT.unwrap()(config.vsync as i32);
|
||||||
|
wglMakeCurrent(hdc, std::ptr::null_mut());
|
||||||
|
|
||||||
|
Ok(GlContext { hwnd, hdc, hglrc, gl_library })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_current(&self) {
|
||||||
|
wglMakeCurrent(self.hdc, self.hglrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_not_current(&self) {
|
||||||
|
wglMakeCurrent(self.hdc, std::ptr::null_mut());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
|
let symbol = CString::new(symbol).unwrap();
|
||||||
|
let addr = unsafe { wglGetProcAddress(symbol.as_ptr()) as *const c_void };
|
||||||
|
if !addr.is_null() {
|
||||||
|
addr
|
||||||
|
} else {
|
||||||
|
unsafe { GetProcAddress(self.gl_library, symbol.as_ptr()) as *const c_void }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_buffers(&self) {
|
||||||
|
unsafe {
|
||||||
|
SwapBuffers(self.hdc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GlContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
wglMakeCurrent(std::ptr::null_mut(), std::ptr::null_mut());
|
||||||
|
wglDeleteContext(self.hglrc);
|
||||||
|
ReleaseDC(self.hwnd, self.hdc);
|
||||||
|
FreeLibrary(self.gl_library);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
253
src/gl/x11.rs
Normal file
253
src/gl/x11.rs
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
use std::ffi::{c_void, CString};
|
||||||
|
use std::os::raw::{c_int, c_ulong};
|
||||||
|
|
||||||
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||||
|
|
||||||
|
use x11::glx;
|
||||||
|
use x11::xlib;
|
||||||
|
|
||||||
|
use super::{GlConfig, GlError, Profile};
|
||||||
|
|
||||||
|
mod errors;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CreationFailedError {
|
||||||
|
InvalidFBConfig,
|
||||||
|
NoVisual,
|
||||||
|
GetProcAddressFailed,
|
||||||
|
MakeCurrentFailed,
|
||||||
|
ContextCreationFailed,
|
||||||
|
X11Error(errors::XLibError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<errors::XLibError> for GlError {
|
||||||
|
fn from(e: errors::XLibError) -> Self {
|
||||||
|
GlError::CreationFailed(CreationFailedError::X11Error(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/GLX_ARB_create_context.txt
|
||||||
|
|
||||||
|
type GlXCreateContextAttribsARB = unsafe extern "C" fn(
|
||||||
|
dpy: *mut xlib::Display,
|
||||||
|
fbc: glx::GLXFBConfig,
|
||||||
|
share_context: glx::GLXContext,
|
||||||
|
direct: xlib::Bool,
|
||||||
|
attribs: *const c_int,
|
||||||
|
) -> glx::GLXContext;
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_swap_control.txt
|
||||||
|
|
||||||
|
type GlXSwapIntervalEXT =
|
||||||
|
unsafe extern "C" fn(dpy: *mut xlib::Display, drawable: glx::GLXDrawable, interval: i32);
|
||||||
|
|
||||||
|
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt
|
||||||
|
|
||||||
|
const GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB: i32 = 0x20B2;
|
||||||
|
|
||||||
|
fn get_proc_address(symbol: &str) -> *const c_void {
|
||||||
|
let symbol = CString::new(symbol).unwrap();
|
||||||
|
unsafe { glx::glXGetProcAddress(symbol.as_ptr() as *const u8).unwrap() as *const c_void }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlContext {
|
||||||
|
window: c_ulong,
|
||||||
|
display: *mut xlib::_XDisplay,
|
||||||
|
context: glx::GLXContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The frame buffer configuration along with the general OpenGL configuration to somewhat minimize
|
||||||
|
/// misuse.
|
||||||
|
pub struct FbConfig {
|
||||||
|
gl_config: GlConfig,
|
||||||
|
fb_config: *mut glx::__GLXFBConfigRec,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The configuration a window should be created with after calling
|
||||||
|
/// [GlContext::get_fb_config_and_visual].
|
||||||
|
pub struct WindowConfig {
|
||||||
|
pub depth: u8,
|
||||||
|
pub visual: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlContext {
|
||||||
|
/// Creating an OpenGL context under X11 works slightly different. Different OpenGL
|
||||||
|
/// configurations require different framebuffer configurations, and to be able to use that
|
||||||
|
/// context with a window the window needs to be created with a matching visual. This means that
|
||||||
|
/// you need to decide on the framebuffer config before creating the window, ask the X11 server
|
||||||
|
/// for a matching visual for that framebuffer config, crate the window with that visual, and
|
||||||
|
/// only then create the OpenGL context.
|
||||||
|
///
|
||||||
|
/// Use [Self::get_fb_config_and_visual] to create both of these things.
|
||||||
|
pub unsafe fn create(
|
||||||
|
parent: &impl HasRawWindowHandle, config: FbConfig,
|
||||||
|
) -> Result<GlContext, GlError> {
|
||||||
|
let handle = if let RawWindowHandle::Xlib(handle) = parent.raw_window_handle() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
return Err(GlError::InvalidWindowHandle);
|
||||||
|
};
|
||||||
|
|
||||||
|
if handle.display.is_null() {
|
||||||
|
return Err(GlError::InvalidWindowHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
let display = handle.display as *mut xlib::_XDisplay;
|
||||||
|
|
||||||
|
errors::XErrorHandler::handle(display, |error_handler| {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let glXCreateContextAttribsARB: GlXCreateContextAttribsARB = {
|
||||||
|
let addr = get_proc_address("glXCreateContextAttribsARB");
|
||||||
|
if addr.is_null() {
|
||||||
|
return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
|
||||||
|
} else {
|
||||||
|
std::mem::transmute(addr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let glXSwapIntervalEXT: GlXSwapIntervalEXT = {
|
||||||
|
let addr = get_proc_address("glXSwapIntervalEXT");
|
||||||
|
if addr.is_null() {
|
||||||
|
return Err(GlError::CreationFailed(CreationFailedError::GetProcAddressFailed));
|
||||||
|
} else {
|
||||||
|
std::mem::transmute(addr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
error_handler.check()?;
|
||||||
|
|
||||||
|
let profile_mask = match config.gl_config.profile {
|
||||||
|
Profile::Core => glx::arb::GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||||
|
Profile::Compatibility => glx::arb::GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let ctx_attribs = [
|
||||||
|
glx::arb::GLX_CONTEXT_MAJOR_VERSION_ARB, config.gl_config.version.0 as i32,
|
||||||
|
glx::arb::GLX_CONTEXT_MINOR_VERSION_ARB, config.gl_config.version.1 as i32,
|
||||||
|
glx::arb::GLX_CONTEXT_PROFILE_MASK_ARB, profile_mask,
|
||||||
|
0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let context = glXCreateContextAttribsARB(
|
||||||
|
display,
|
||||||
|
config.fb_config,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
1,
|
||||||
|
ctx_attribs.as_ptr(),
|
||||||
|
);
|
||||||
|
|
||||||
|
error_handler.check()?;
|
||||||
|
|
||||||
|
if context.is_null() {
|
||||||
|
return Err(GlError::CreationFailed(CreationFailedError::ContextCreationFailed));
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = glx::glXMakeCurrent(display, handle.window, context);
|
||||||
|
error_handler.check()?;
|
||||||
|
if res == 0 {
|
||||||
|
return Err(GlError::CreationFailed(CreationFailedError::MakeCurrentFailed));
|
||||||
|
}
|
||||||
|
|
||||||
|
glXSwapIntervalEXT(display, handle.window, config.gl_config.vsync as i32);
|
||||||
|
error_handler.check()?;
|
||||||
|
|
||||||
|
if glx::glXMakeCurrent(display, 0, std::ptr::null_mut()) == 0 {
|
||||||
|
error_handler.check()?;
|
||||||
|
return Err(GlError::CreationFailed(CreationFailedError::MakeCurrentFailed));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(GlContext { window: handle.window, display, context })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a matching framebuffer config and window visual for the given OpenGL configuration.
|
||||||
|
/// This needs to be passed to [Self::create] along with a handle to a window that was created
|
||||||
|
/// using the visual also returned from this function.
|
||||||
|
pub unsafe fn get_fb_config_and_visual(
|
||||||
|
display: *mut xlib::_XDisplay, config: GlConfig,
|
||||||
|
) -> Result<(FbConfig, WindowConfig), GlError> {
|
||||||
|
errors::XErrorHandler::handle(display, |error_handler| {
|
||||||
|
let screen = xlib::XDefaultScreen(display);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let fb_attribs = [
|
||||||
|
glx::GLX_X_RENDERABLE, 1,
|
||||||
|
glx::GLX_X_VISUAL_TYPE, glx::GLX_TRUE_COLOR,
|
||||||
|
glx::GLX_DRAWABLE_TYPE, glx::GLX_WINDOW_BIT,
|
||||||
|
glx::GLX_RENDER_TYPE, glx::GLX_RGBA_BIT,
|
||||||
|
glx::GLX_RED_SIZE, config.red_bits as i32,
|
||||||
|
glx::GLX_GREEN_SIZE, config.green_bits as i32,
|
||||||
|
glx::GLX_BLUE_SIZE, config.blue_bits as i32,
|
||||||
|
glx::GLX_ALPHA_SIZE, config.alpha_bits as i32,
|
||||||
|
glx::GLX_DEPTH_SIZE, config.depth_bits as i32,
|
||||||
|
glx::GLX_STENCIL_SIZE, config.stencil_bits as i32,
|
||||||
|
glx::GLX_DOUBLEBUFFER, config.double_buffer as i32,
|
||||||
|
glx::GLX_SAMPLE_BUFFERS, config.samples.is_some() as i32,
|
||||||
|
glx::GLX_SAMPLES, config.samples.unwrap_or(0) as i32,
|
||||||
|
GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, config.srgb as i32,
|
||||||
|
0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut n_configs = 0;
|
||||||
|
let fb_config =
|
||||||
|
glx::glXChooseFBConfig(display, screen, fb_attribs.as_ptr(), &mut n_configs);
|
||||||
|
|
||||||
|
error_handler.check()?;
|
||||||
|
if n_configs <= 0 || fb_config.is_null() {
|
||||||
|
return Err(GlError::CreationFailed(CreationFailedError::InvalidFBConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have a matching framebuffer config, we need to know which visual matches
|
||||||
|
// thsi config so the window is compatible with the OpenGL context we're about to create
|
||||||
|
let fb_config = *fb_config;
|
||||||
|
let visual = glx::glXGetVisualFromFBConfig(display, fb_config);
|
||||||
|
if visual.is_null() {
|
||||||
|
return Err(GlError::CreationFailed(CreationFailedError::NoVisual));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
FbConfig { fb_config, gl_config: config },
|
||||||
|
WindowConfig { depth: (*visual).depth as u8, visual: (*visual).visualid as u32 },
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_current(&self) {
|
||||||
|
errors::XErrorHandler::handle(self.display, |error_handler| {
|
||||||
|
let res = glx::glXMakeCurrent(self.display, self.window, self.context);
|
||||||
|
error_handler.check().unwrap();
|
||||||
|
if res == 0 {
|
||||||
|
panic!("make_current failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn make_not_current(&self) {
|
||||||
|
errors::XErrorHandler::handle(self.display, |error_handler| {
|
||||||
|
let res = glx::glXMakeCurrent(self.display, 0, std::ptr::null_mut());
|
||||||
|
error_handler.check().unwrap();
|
||||||
|
if res == 0 {
|
||||||
|
panic!("make_not_current failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_proc_address(&self, symbol: &str) -> *const c_void {
|
||||||
|
get_proc_address(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swap_buffers(&self) {
|
||||||
|
errors::XErrorHandler::handle(self.display, |error_handler| {
|
||||||
|
unsafe {
|
||||||
|
glx::glXSwapBuffers(self.display, self.window);
|
||||||
|
}
|
||||||
|
error_handler.check().unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GlContext {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
122
src/gl/x11/errors.rs
Normal file
122
src/gl/x11/errors.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use x11::xlib;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::panic::AssertUnwindSafe;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
/// Used as part of [`XerrorHandler::handle()`]. When an X11 error occurs during this function,
|
||||||
|
/// the error gets copied to this RefCell after which the program is allowed to resume. The
|
||||||
|
/// error can then be converted to a regular Rust Result value afterwards.
|
||||||
|
static CURRENT_X11_ERROR: RefCell<Option<xlib::XErrorEvent>> = RefCell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A helper struct for safe X11 error handling
|
||||||
|
pub struct XErrorHandler<'a> {
|
||||||
|
display: *mut xlib::Display,
|
||||||
|
error: &'a RefCell<Option<xlib::XErrorEvent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> XErrorHandler<'a> {
|
||||||
|
/// Syncs and checks if any previous X11 calls returned an error
|
||||||
|
pub fn check(&mut self) -> Result<(), XLibError> {
|
||||||
|
// Flush all possible previous errors
|
||||||
|
unsafe {
|
||||||
|
xlib::XSync(self.display, 0);
|
||||||
|
}
|
||||||
|
let error = self.error.borrow_mut().take();
|
||||||
|
|
||||||
|
match error {
|
||||||
|
None => Ok(()),
|
||||||
|
Some(inner) => Err(XLibError { inner }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up a temporary X11 error handler for the duration of the given closure, and allows
|
||||||
|
/// that closure to check on the latest X11 error at any time
|
||||||
|
pub fn handle<T, F: FnOnce(&mut XErrorHandler) -> T>(
|
||||||
|
display: *mut xlib::Display, handler: F,
|
||||||
|
) -> T {
|
||||||
|
unsafe extern "C" fn error_handler(
|
||||||
|
_dpy: *mut xlib::Display, err: *mut xlib::XErrorEvent,
|
||||||
|
) -> i32 {
|
||||||
|
// SAFETY: the error pointer should be safe to copy
|
||||||
|
let err = *err;
|
||||||
|
|
||||||
|
CURRENT_X11_ERROR.with(|error| {
|
||||||
|
let mut error = error.borrow_mut();
|
||||||
|
match error.as_mut() {
|
||||||
|
// If multiple errors occur, keep the first one since that's likely going to be the
|
||||||
|
// cause of the other errors
|
||||||
|
Some(_) => 1,
|
||||||
|
None => {
|
||||||
|
*error = Some(err);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush all possible previous errors
|
||||||
|
unsafe {
|
||||||
|
xlib::XSync(display, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CURRENT_X11_ERROR.with(|error| {
|
||||||
|
// Make sure to clear any errors from the last call to this function
|
||||||
|
*error.borrow_mut() = None;
|
||||||
|
|
||||||
|
let old_handler = unsafe { xlib::XSetErrorHandler(Some(error_handler)) };
|
||||||
|
let panic_result = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||||
|
let mut h = XErrorHandler { display, error };
|
||||||
|
handler(&mut h)
|
||||||
|
}));
|
||||||
|
// Whatever happened, restore old error handler
|
||||||
|
unsafe { xlib::XSetErrorHandler(old_handler) };
|
||||||
|
|
||||||
|
match panic_result {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => std::panic::resume_unwind(e),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct XLibError {
|
||||||
|
inner: xlib::XErrorEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XLibError {
|
||||||
|
pub fn get_display_name(&self, buf: &mut [u8]) -> &CStr {
|
||||||
|
unsafe {
|
||||||
|
xlib::XGetErrorText(
|
||||||
|
self.inner.display,
|
||||||
|
self.inner.error_code.into(),
|
||||||
|
buf.as_mut_ptr().cast(),
|
||||||
|
(buf.len() - 1) as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf.last_mut().unwrap() = 0;
|
||||||
|
// SAFETY: whatever XGetErrorText did or not, we guaranteed there is a nul byte at the end of the buffer
|
||||||
|
unsafe { CStr::from_ptr(buf.as_mut_ptr().cast()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for XLibError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut buf = [0; 255];
|
||||||
|
let display_name = self.get_display_name(&mut buf).to_string_lossy();
|
||||||
|
|
||||||
|
f.debug_struct("XLibError")
|
||||||
|
.field("error_code", &self.inner.error_code)
|
||||||
|
.field("error_message", &display_name)
|
||||||
|
.field("minor_code", &self.inner.minor_code)
|
||||||
|
.field("request_code", &self.inner.request_code)
|
||||||
|
.field("type", &self.inner.type_)
|
||||||
|
.field("resource_id", &self.inner.resourceid)
|
||||||
|
.field("serial", &self.inner.serial)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
//! Keyboard types.
|
//! Keyboard types.
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
use keyboard_types::{Code, Location};
|
use keyboard_types::{Code, Location};
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
|
|
|
@ -12,6 +12,9 @@ mod window;
|
||||||
mod window_info;
|
mod window_info;
|
||||||
mod window_open_options;
|
mod window_open_options;
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
pub mod gl;
|
||||||
|
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
pub use mouse_cursor::MouseCursor;
|
pub use mouse_cursor::MouseCursor;
|
||||||
pub use window::*;
|
pub use window::*;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::ptr;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ use keyboard_types::KeyboardEvent;
|
||||||
|
|
||||||
use objc::{msg_send, runtime::Object, sel, sel_impl};
|
use objc::{msg_send, runtime::Object, sel, sel_impl};
|
||||||
|
|
||||||
use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle};
|
use raw_window_handle::{AppKitHandle, HasRawWindowHandle, RawWindowHandle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, EventStatus, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
Event, EventStatus, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
||||||
|
@ -26,6 +27,12 @@ use crate::{
|
||||||
use super::keyboard::KeyboardState;
|
use super::keyboard::KeyboardState;
|
||||||
use super::view::{create_view, BASEVIEW_STATE_IVAR};
|
use super::view::{create_view, BASEVIEW_STATE_IVAR};
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
use crate::{
|
||||||
|
gl::{GlConfig, GlContext},
|
||||||
|
window::RawWindowHandleWrapper,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct WindowHandle {
|
pub struct WindowHandle {
|
||||||
raw_window_handle: Option<RawWindowHandle>,
|
raw_window_handle: Option<RawWindowHandle>,
|
||||||
close_requested: Arc<AtomicBool>,
|
close_requested: Arc<AtomicBool>,
|
||||||
|
@ -37,7 +44,7 @@ pub struct WindowHandle {
|
||||||
|
|
||||||
impl WindowHandle {
|
impl WindowHandle {
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
if let Some(_) = self.raw_window_handle.take() {
|
if self.raw_window_handle.take().is_some() {
|
||||||
self.close_requested.store(true, Ordering::Relaxed);
|
self.close_requested.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +62,7 @@ unsafe impl HasRawWindowHandle for WindowHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RawWindowHandle::MacOS(MacOSHandle { ..MacOSHandle::empty() })
|
RawWindowHandle::AppKit(AppKitHandle::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +109,9 @@ pub struct Window {
|
||||||
/// Our subclassed NSView
|
/// Our subclassed NSView
|
||||||
ns_view: id,
|
ns_view: id,
|
||||||
close_requested: bool,
|
close_requested: bool,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context: Option<GlContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -114,7 +124,7 @@ impl Window {
|
||||||
{
|
{
|
||||||
let pool = unsafe { NSAutoreleasePool::new(nil) };
|
let pool = unsafe { NSAutoreleasePool::new(nil) };
|
||||||
|
|
||||||
let handle = if let RawWindowHandle::MacOS(handle) = parent.raw_window_handle() {
|
let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() {
|
||||||
handle
|
handle
|
||||||
} else {
|
} else {
|
||||||
panic!("Not a macOS window");
|
panic!("Not a macOS window");
|
||||||
|
@ -122,7 +132,17 @@ impl Window {
|
||||||
|
|
||||||
let ns_view = unsafe { create_view(&options) };
|
let ns_view = unsafe { create_view(&options) };
|
||||||
|
|
||||||
let window = Window { ns_app: None, ns_window: None, ns_view, close_requested: false };
|
let window = Window {
|
||||||
|
ns_app: None,
|
||||||
|
ns_window: None,
|
||||||
|
ns_view,
|
||||||
|
close_requested: false,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context: options
|
||||||
|
.gl_config
|
||||||
|
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
|
||||||
|
};
|
||||||
|
|
||||||
let window_handle = Self::init(true, window, build);
|
let window_handle = Self::init(true, window, build);
|
||||||
|
|
||||||
|
@ -146,7 +166,17 @@ impl Window {
|
||||||
|
|
||||||
let ns_view = unsafe { create_view(&options) };
|
let ns_view = unsafe { create_view(&options) };
|
||||||
|
|
||||||
let window = Window { ns_app: None, ns_window: None, ns_view, close_requested: false };
|
let window = Window {
|
||||||
|
ns_app: None,
|
||||||
|
ns_window: None,
|
||||||
|
ns_view,
|
||||||
|
close_requested: false,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context: options
|
||||||
|
.gl_config
|
||||||
|
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
|
||||||
|
};
|
||||||
|
|
||||||
let window_handle = Self::init(true, window, build);
|
let window_handle = Self::init(true, window, build);
|
||||||
|
|
||||||
|
@ -217,6 +247,11 @@ impl Window {
|
||||||
ns_window: Some(ns_window),
|
ns_window: Some(ns_window),
|
||||||
ns_view,
|
ns_view,
|
||||||
close_requested: false,
|
close_requested: false,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context: options
|
||||||
|
.gl_config
|
||||||
|
.map(|gl_config| Self::create_gl_context(Some(ns_window), ns_view, gl_config)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = Self::init(false, window, build);
|
let _ = Self::init(false, window, build);
|
||||||
|
@ -266,6 +301,21 @@ impl Window {
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
self.close_requested = true;
|
self.close_requested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
pub fn gl_context(&self) -> Option<&GlContext> {
|
||||||
|
self.gl_context.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
fn create_gl_context(ns_window: Option<id>, ns_view: id, config: GlConfig) -> GlContext {
|
||||||
|
let mut handle = AppKitHandle::empty();
|
||||||
|
handle.ns_window = ns_window.unwrap_or(ptr::null_mut()) as *mut c_void;
|
||||||
|
handle.ns_view = ns_view as *mut c_void;
|
||||||
|
let handle = RawWindowHandleWrapper { handle: RawWindowHandle::AppKit(handle) };
|
||||||
|
|
||||||
|
unsafe { GlContext::create(&handle, config).expect("Could not create OpenGL context") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct WindowState {
|
pub(super) struct WindowState {
|
||||||
|
@ -373,7 +423,7 @@ impl WindowState {
|
||||||
// Clear ivar before triggering WindowEvent::WillClose. Otherwise, if the
|
// Clear ivar before triggering WindowEvent::WillClose. Otherwise, if the
|
||||||
// handler of the event causes another call to release, this function could be
|
// handler of the event causes another call to release, this function could be
|
||||||
// called again, leading to a double free.
|
// called again, leading to a double free.
|
||||||
ns_view_obj.set_ivar(BASEVIEW_STATE_IVAR, ::std::ptr::null() as *const c_void);
|
ns_view_obj.set_ivar(BASEVIEW_STATE_IVAR, ptr::null() as *const c_void);
|
||||||
|
|
||||||
window_state.trigger_event(Event::Window(WindowEvent::WillClose));
|
window_state.trigger_event(Event::Window(WindowEvent::WillClose));
|
||||||
|
|
||||||
|
@ -386,12 +436,12 @@ impl WindowState {
|
||||||
|
|
||||||
unsafe impl HasRawWindowHandle for Window {
|
unsafe impl HasRawWindowHandle for Window {
|
||||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||||
let ns_window = self.ns_window.unwrap_or(::std::ptr::null_mut()) as *mut c_void;
|
let ns_window = self.ns_window.unwrap_or(ptr::null_mut()) as *mut c_void;
|
||||||
|
|
||||||
RawWindowHandle::MacOS(MacOSHandle {
|
let mut handle = AppKitHandle::empty();
|
||||||
ns_window,
|
handle.ns_window = ns_window;
|
||||||
ns_view: self.ns_view as *mut c_void,
|
handle.ns_view = self.ns_view as *mut c_void;
|
||||||
..MacOSHandle::empty()
|
|
||||||
})
|
RawWindowHandle::AppKit(handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
@ -29,15 +28,15 @@ use winapi::shared::minwindef::{HKL, INT, LPARAM, UINT, WPARAM};
|
||||||
use winapi::shared::ntdef::SHORT;
|
use winapi::shared::ntdef::SHORT;
|
||||||
use winapi::shared::windef::HWND;
|
use winapi::shared::windef::HWND;
|
||||||
use winapi::um::winuser::{
|
use winapi::um::winuser::{
|
||||||
GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, VkKeyScanW,
|
GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, MAPVK_VK_TO_CHAR,
|
||||||
MAPVK_VK_TO_CHAR, MAPVK_VSC_TO_VK_EX, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN,
|
MAPVK_VSC_TO_VK_EX, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN, VK_BACK, VK_BROWSER_BACK,
|
||||||
VK_BACK, VK_BROWSER_BACK, VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME,
|
VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, VK_BROWSER_REFRESH,
|
||||||
VK_BROWSER_REFRESH, VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR,
|
VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR, VK_CONTROL, VK_CONVERT,
|
||||||
VK_CONTROL, VK_CONVERT, VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF,
|
VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF, VK_ESCAPE, VK_EXECUTE,
|
||||||
VK_ESCAPE, VK_EXECUTE, VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F2, VK_F3, VK_F4, VK_F5,
|
VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8,
|
||||||
VK_F6, VK_F7, VK_F8, VK_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI,
|
VK_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI, VK_LAUNCH_APP1,
|
||||||
VK_LAUNCH_APP1, VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT,
|
VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT, VK_LMENU,
|
||||||
VK_LMENU, VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK,
|
VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK,
|
||||||
VK_MEDIA_STOP, VK_MENU, VK_MODECHANGE, VK_MULTIPLY, VK_NEXT, VK_NONCONVERT, VK_NUMLOCK,
|
VK_MEDIA_STOP, VK_MENU, VK_MODECHANGE, VK_MULTIPLY, VK_NEXT, VK_NONCONVERT, VK_NUMLOCK,
|
||||||
VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
||||||
VK_NUMPAD8, VK_NUMPAD9, VK_OEM_ATTN, VK_OEM_CLEAR, VK_PAUSE, VK_PLAY, VK_PRINT, VK_PRIOR,
|
VK_NUMPAD8, VK_NUMPAD9, VK_OEM_ATTN, VK_OEM_CLEAR, VK_PAUSE, VK_PLAY, VK_PRINT, VK_PRIOR,
|
||||||
|
@ -344,110 +343,6 @@ fn vk_to_key(vk: VkCode) -> Option<Key> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a key to a virtual key code.
|
|
||||||
///
|
|
||||||
/// The virtual key code is needed in various winapi interfaces, including
|
|
||||||
/// accelerators. This provides the virtual key code in the current keyboard
|
|
||||||
/// map.
|
|
||||||
///
|
|
||||||
/// The virtual key code can have modifiers in the higher order byte when the
|
|
||||||
/// argument is a `Character` variant. See:
|
|
||||||
/// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw
|
|
||||||
pub(crate) fn key_to_vk(key: &Key) -> Option<i32> {
|
|
||||||
Some(match key {
|
|
||||||
Key::Character(s) => {
|
|
||||||
if let Some(code_point) = s.chars().next() {
|
|
||||||
if let Ok(wchar) = (code_point as u32).try_into() {
|
|
||||||
unsafe { VkKeyScanW(wchar) as i32 }
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Cancel => VK_CANCEL,
|
|
||||||
Key::Backspace => VK_BACK,
|
|
||||||
Key::Tab => VK_TAB,
|
|
||||||
Key::Clear => VK_CLEAR,
|
|
||||||
Key::Enter => VK_RETURN,
|
|
||||||
Key::Shift => VK_SHIFT,
|
|
||||||
Key::Control => VK_CONTROL,
|
|
||||||
Key::Alt => VK_MENU,
|
|
||||||
Key::Pause => VK_PAUSE,
|
|
||||||
Key::CapsLock => VK_CAPITAL,
|
|
||||||
// TODO: disambiguate kana and hangul? same vk
|
|
||||||
Key::KanaMode => VK_KANA,
|
|
||||||
Key::JunjaMode => VK_JUNJA,
|
|
||||||
Key::FinalMode => VK_FINAL,
|
|
||||||
Key::KanjiMode => VK_KANJI,
|
|
||||||
Key::Escape => VK_ESCAPE,
|
|
||||||
Key::NonConvert => VK_NONCONVERT,
|
|
||||||
Key::Accept => VK_ACCEPT,
|
|
||||||
Key::PageUp => VK_PRIOR,
|
|
||||||
Key::PageDown => VK_NEXT,
|
|
||||||
Key::End => VK_END,
|
|
||||||
Key::Home => VK_HOME,
|
|
||||||
Key::ArrowLeft => VK_LEFT,
|
|
||||||
Key::ArrowUp => VK_UP,
|
|
||||||
Key::ArrowRight => VK_RIGHT,
|
|
||||||
Key::ArrowDown => VK_DOWN,
|
|
||||||
Key::Select => VK_SELECT,
|
|
||||||
Key::Print => VK_PRINT,
|
|
||||||
Key::Execute => VK_EXECUTE,
|
|
||||||
Key::PrintScreen => VK_SNAPSHOT,
|
|
||||||
Key::Insert => VK_INSERT,
|
|
||||||
Key::Delete => VK_DELETE,
|
|
||||||
Key::Help => VK_HELP,
|
|
||||||
Key::Meta => VK_LWIN,
|
|
||||||
Key::ContextMenu => VK_APPS,
|
|
||||||
Key::Standby => VK_SLEEP,
|
|
||||||
Key::F1 => VK_F1,
|
|
||||||
Key::F2 => VK_F2,
|
|
||||||
Key::F3 => VK_F3,
|
|
||||||
Key::F4 => VK_F4,
|
|
||||||
Key::F5 => VK_F5,
|
|
||||||
Key::F6 => VK_F6,
|
|
||||||
Key::F7 => VK_F7,
|
|
||||||
Key::F8 => VK_F8,
|
|
||||||
Key::F9 => VK_F9,
|
|
||||||
Key::F10 => VK_F10,
|
|
||||||
Key::F11 => VK_F11,
|
|
||||||
Key::F12 => VK_F12,
|
|
||||||
Key::NumLock => VK_NUMLOCK,
|
|
||||||
Key::ScrollLock => VK_SCROLL,
|
|
||||||
Key::BrowserBack => VK_BROWSER_BACK,
|
|
||||||
Key::BrowserForward => VK_BROWSER_FORWARD,
|
|
||||||
Key::BrowserRefresh => VK_BROWSER_REFRESH,
|
|
||||||
Key::BrowserStop => VK_BROWSER_STOP,
|
|
||||||
Key::BrowserSearch => VK_BROWSER_SEARCH,
|
|
||||||
Key::BrowserFavorites => VK_BROWSER_FAVORITES,
|
|
||||||
Key::BrowserHome => VK_BROWSER_HOME,
|
|
||||||
Key::AudioVolumeMute => VK_VOLUME_MUTE,
|
|
||||||
Key::AudioVolumeDown => VK_VOLUME_DOWN,
|
|
||||||
Key::AudioVolumeUp => VK_VOLUME_UP,
|
|
||||||
Key::MediaTrackNext => VK_MEDIA_NEXT_TRACK,
|
|
||||||
Key::MediaTrackPrevious => VK_MEDIA_PREV_TRACK,
|
|
||||||
Key::MediaStop => VK_MEDIA_STOP,
|
|
||||||
Key::MediaPlayPause => VK_MEDIA_PLAY_PAUSE,
|
|
||||||
Key::LaunchMail => VK_LAUNCH_MAIL,
|
|
||||||
Key::LaunchMediaPlayer => VK_LAUNCH_MEDIA_SELECT,
|
|
||||||
Key::LaunchApplication1 => VK_LAUNCH_APP1,
|
|
||||||
Key::LaunchApplication2 => VK_LAUNCH_APP2,
|
|
||||||
Key::Alphanumeric => VK_OEM_ATTN,
|
|
||||||
Key::Convert => VK_CONVERT,
|
|
||||||
Key::ModeChange => VK_MODECHANGE,
|
|
||||||
Key::Process => VK_PROCESSKEY,
|
|
||||||
Key::Attn => VK_ATTN,
|
|
||||||
Key::CrSel => VK_CRSEL,
|
|
||||||
Key::ExSel => VK_EXSEL,
|
|
||||||
Key::EraseEof => VK_EREOF,
|
|
||||||
Key::Play => VK_PLAY,
|
|
||||||
Key::ZoomToggle => VK_ZOOM,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn code_unit_to_key(code_unit: u32) -> Key {
|
fn code_unit_to_key(code_unit: u32) -> Key {
|
||||||
match code_unit {
|
match code_unit {
|
||||||
0x8 | 0x7F => Key::Backspace,
|
0x8 | 0x7F => Key::Backspace,
|
||||||
|
@ -692,6 +587,7 @@ impl KeyboardState {
|
||||||
key_state[VK_LCONTROL as usize] = if has_altgr { 0x80 } else { 0 };
|
key_state[VK_LCONTROL as usize] = if has_altgr { 0x80 } else { 0 };
|
||||||
key_state[VK_MENU as usize] = if has_altgr { 0x80 } else { 0 };
|
key_state[VK_MENU as usize] = if has_altgr { 0x80 } else { 0 };
|
||||||
key_state[VK_RMENU as usize] = if has_altgr { 0x80 } else { 0 };
|
key_state[VK_RMENU as usize] = if has_altgr { 0x80 } else { 0 };
|
||||||
|
#[allow(clippy::iter_overeager_cloned)]
|
||||||
for vk in PRINTABLE_VKS.iter().cloned().flatten() {
|
for vk in PRINTABLE_VKS.iter().cloned().flatten() {
|
||||||
let ret = ToUnicodeEx(
|
let ret = ToUnicodeEx(
|
||||||
vk as UINT,
|
vk as UINT,
|
||||||
|
|
|
@ -16,14 +16,14 @@ use winapi::um::winuser::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::{c_void, OsStr};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use raw_window_handle::{windows::WindowsHandle, HasRawWindowHandle, RawWindowHandle};
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle};
|
||||||
|
|
||||||
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
||||||
|
|
||||||
|
@ -34,6 +34,9 @@ use crate::{
|
||||||
|
|
||||||
use super::keyboard::KeyboardState;
|
use super::keyboard::KeyboardState;
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
use crate::{gl::GlContext, window::RawWindowHandleWrapper};
|
||||||
|
|
||||||
unsafe fn generate_guid() -> String {
|
unsafe fn generate_guid() -> String {
|
||||||
let mut guid: GUID = std::mem::zeroed();
|
let mut guid: GUID = std::mem::zeroed();
|
||||||
CoCreateGuid(&mut guid);
|
CoCreateGuid(&mut guid);
|
||||||
|
@ -80,12 +83,12 @@ impl WindowHandle {
|
||||||
unsafe impl HasRawWindowHandle for WindowHandle {
|
unsafe impl HasRawWindowHandle for WindowHandle {
|
||||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||||
if let Some(hwnd) = self.hwnd {
|
if let Some(hwnd) = self.hwnd {
|
||||||
RawWindowHandle::Windows(WindowsHandle {
|
let mut handle = Win32Handle::empty();
|
||||||
hwnd: hwnd as *mut std::ffi::c_void,
|
handle.hwnd = hwnd as *mut c_void;
|
||||||
..WindowsHandle::empty()
|
|
||||||
})
|
RawWindowHandle::Win32(handle)
|
||||||
} else {
|
} else {
|
||||||
RawWindowHandle::Windows(WindowsHandle { ..WindowsHandle::empty() })
|
RawWindowHandle::Win32(Win32Handle::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,18 +127,17 @@ unsafe extern "system" fn wnd_proc(
|
||||||
|
|
||||||
let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut RefCell<WindowState>;
|
let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut RefCell<WindowState>;
|
||||||
if !window_state_ptr.is_null() {
|
if !window_state_ptr.is_null() {
|
||||||
let mut window = Window { hwnd };
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
WM_MOUSEMOVE => {
|
WM_MOUSEMOVE => {
|
||||||
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let x = (lparam & 0xFFFF) as i16 as i32;
|
let x = (lparam & 0xFFFF) as i16 as i32;
|
||||||
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
||||||
|
|
||||||
let physical_pos = PhyPoint { x, y };
|
let physical_pos = PhyPoint { x, y };
|
||||||
|
|
||||||
let mut window_state = (&*window_state_ptr).borrow_mut();
|
|
||||||
|
|
||||||
let logical_pos = physical_pos.to_logical(&window_state.window_info);
|
let logical_pos = physical_pos.to_logical(&window_state.window_info);
|
||||||
|
|
||||||
window_state.handler.on_event(
|
window_state.handler.on_event(
|
||||||
|
@ -145,12 +147,14 @@ unsafe extern "system" fn wnd_proc(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
WM_MOUSEWHEEL => {
|
WM_MOUSEWHEEL => {
|
||||||
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let value = (wparam >> 16) as i16;
|
let value = (wparam >> 16) as i16;
|
||||||
let value = value as i32;
|
let value = value as i32;
|
||||||
let value = value as f32 / WHEEL_DELTA as f32;
|
let value = value as f32 / WHEEL_DELTA as f32;
|
||||||
|
|
||||||
let mut window_state = (&*window_state_ptr).borrow_mut();
|
|
||||||
|
|
||||||
window_state.handler.on_event(
|
window_state.handler.on_event(
|
||||||
&mut window,
|
&mut window,
|
||||||
Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines {
|
Event::Mouse(MouseEvent::WheelScrolled(ScrollDelta::Lines {
|
||||||
|
@ -162,7 +166,11 @@ unsafe extern "system" fn wnd_proc(
|
||||||
}
|
}
|
||||||
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
||||||
| WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => {
|
| WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => {
|
||||||
let mut mouse_button_counter = (&*window_state_ptr).borrow().mouse_button_counter;
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
let mut mouse_button_counter = window_state.mouse_button_counter;
|
||||||
|
|
||||||
let button = match msg {
|
let button = match msg {
|
||||||
WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left),
|
WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left),
|
||||||
|
@ -198,44 +206,48 @@ unsafe extern "system" fn wnd_proc(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(&*window_state_ptr).borrow_mut().mouse_button_counter = mouse_button_counter;
|
window_state.mouse_button_counter = mouse_button_counter;
|
||||||
|
|
||||||
(&*window_state_ptr)
|
window_state.handler.on_event(&mut window, Event::Mouse(event));
|
||||||
.borrow_mut()
|
|
||||||
.handler
|
|
||||||
.on_event(&mut window, Event::Mouse(event));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WM_TIMER => {
|
WM_TIMER => {
|
||||||
match wparam {
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
WIN_FRAME_TIMER => {
|
let mut window = window_state.create_window(hwnd);
|
||||||
(&*window_state_ptr).borrow_mut().handler.on_frame(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
}
|
|
||||||
_ => (),
|
if wparam == WIN_FRAME_TIMER {
|
||||||
|
window_state.handler.on_frame(&mut window);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
WM_CLOSE => {
|
WM_CLOSE => {
|
||||||
(&*window_state_ptr)
|
// Make sure to release the borrow before the DefWindowProc call
|
||||||
.borrow_mut()
|
{
|
||||||
.handler
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
window_state
|
||||||
|
.handler
|
||||||
|
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
|
||||||
|
}
|
||||||
|
|
||||||
// DestroyWindow(hwnd);
|
// DestroyWindow(hwnd);
|
||||||
// return 0;
|
// return 0;
|
||||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
|
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
|
||||||
| WM_INPUTLANGCHANGE => {
|
| WM_INPUTLANGCHANGE => {
|
||||||
let opt_event = (&*window_state_ptr)
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
.borrow_mut()
|
let mut window = window_state.create_window(hwnd);
|
||||||
.keyboard_state
|
let mut window = crate::Window::new(&mut window);
|
||||||
.process_message(hwnd, msg, wparam, lparam);
|
|
||||||
|
let opt_event =
|
||||||
|
window_state.keyboard_state.process_message(hwnd, msg, wparam, lparam);
|
||||||
|
|
||||||
if let Some(event) = opt_event {
|
if let Some(event) = opt_event {
|
||||||
(&*window_state_ptr)
|
window_state.handler.on_event(&mut window, Event::Keyboard(event));
|
||||||
.borrow_mut()
|
|
||||||
.handler
|
|
||||||
.on_event(&mut window, Event::Keyboard(event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg != WM_SYSKEYDOWN {
|
if msg != WM_SYSKEYDOWN {
|
||||||
|
@ -243,11 +255,13 @@ unsafe extern "system" fn wnd_proc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WM_SIZE => {
|
WM_SIZE => {
|
||||||
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
|
let mut window = window_state.create_window(hwnd);
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let width = (lparam & 0xFFFF) as u16 as u32;
|
let width = (lparam & 0xFFFF) as u16 as u32;
|
||||||
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
|
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
|
||||||
|
|
||||||
let mut window_state = (&*window_state_ptr).borrow_mut();
|
|
||||||
|
|
||||||
window_state.window_info = WindowInfo::from_physical_size(
|
window_state.window_info = WindowInfo::from_physical_size(
|
||||||
PhySize { width, height },
|
PhySize { width, height },
|
||||||
window_state.window_info.scale(),
|
window_state.window_info.scale(),
|
||||||
|
@ -260,10 +274,10 @@ unsafe extern "system" fn wnd_proc(
|
||||||
.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info)));
|
.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info)));
|
||||||
}
|
}
|
||||||
WM_DPICHANGED => {
|
WM_DPICHANGED => {
|
||||||
|
let mut window_state = (*window_state_ptr).borrow_mut();
|
||||||
|
|
||||||
// To avoid weirdness with the realtime borrow checker.
|
// To avoid weirdness with the realtime borrow checker.
|
||||||
let new_rect = {
|
let new_rect = {
|
||||||
let mut window_state = (&*window_state_ptr).borrow_mut();
|
|
||||||
|
|
||||||
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
|
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
|
||||||
let dpi = (wparam & 0xFFFF) as u16 as u32;
|
let dpi = (wparam & 0xFFFF) as u16 as u32;
|
||||||
let scale_factor = dpi as f64 / 96.0;
|
let scale_factor = dpi as f64 / 96.0;
|
||||||
|
@ -319,7 +333,7 @@ unsafe extern "system" fn wnd_proc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
DefWindowProcW(hwnd, msg, wparam, lparam)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn register_wnd_class() -> ATOM {
|
unsafe fn register_wnd_class() -> ATOM {
|
||||||
|
@ -357,10 +371,28 @@ struct WindowState {
|
||||||
handler: Box<dyn WindowHandler>,
|
handler: Box<dyn WindowHandler>,
|
||||||
scale_policy: WindowScalePolicy,
|
scale_policy: WindowScalePolicy,
|
||||||
dw_style: u32,
|
dw_style: u32,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context: Arc<Option<GlContext>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowState {
|
||||||
|
#[cfg(not(feature = "opengl"))]
|
||||||
|
fn create_window(&self, hwnd: HWND) -> Window {
|
||||||
|
Window { hwnd }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
fn create_window(&self, hwnd: HWND) -> Window {
|
||||||
|
Window { hwnd, gl_context: self.gl_context.clone() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
hwnd: HWND,
|
hwnd: HWND,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context: Arc<Option<GlContext>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -372,7 +404,7 @@ impl Window {
|
||||||
B: Send + 'static,
|
B: Send + 'static,
|
||||||
{
|
{
|
||||||
let parent = match parent.raw_window_handle() {
|
let parent = match parent.raw_window_handle() {
|
||||||
RawWindowHandle::Windows(h) => h.hwnd as HWND,
|
RawWindowHandle::Win32(h) => h.hwnd as HWND,
|
||||||
h => panic!("unsupported parent handle {:?}", h),
|
h => panic!("unsupported parent handle {:?}", h),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -410,8 +442,8 @@ impl Window {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TranslateMessage(&mut msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessageW(&mut msg);
|
DispatchMessageW(&msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -478,7 +510,22 @@ impl Window {
|
||||||
);
|
);
|
||||||
// todo: manage error ^
|
// todo: manage error ^
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
let gl_context: Arc<Option<GlContext>> = Arc::new(options.gl_config.map(|gl_config| {
|
||||||
|
let mut handle = Win32Handle::empty();
|
||||||
|
handle.hwnd = hwnd as *mut c_void;
|
||||||
|
let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Win32(handle) };
|
||||||
|
|
||||||
|
GlContext::create(&handle, gl_config).expect("Could not create OpenGL context")
|
||||||
|
}));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "opengl"))]
|
||||||
let handler = Box::new(build(&mut crate::Window::new(&mut Window { hwnd })));
|
let handler = Box::new(build(&mut crate::Window::new(&mut Window { hwnd })));
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
let handler = Box::new(build(&mut crate::Window::new(&mut Window {
|
||||||
|
hwnd,
|
||||||
|
gl_context: gl_context.clone(),
|
||||||
|
})));
|
||||||
|
|
||||||
let (parent_handle, window_handle) = ParentHandle::new(hwnd);
|
let (parent_handle, window_handle) = ParentHandle::new(hwnd);
|
||||||
let parent_handle = if parented { Some(parent_handle) } else { None };
|
let parent_handle = if parented { Some(parent_handle) } else { None };
|
||||||
|
@ -492,6 +539,9 @@ impl Window {
|
||||||
handler,
|
handler,
|
||||||
scale_policy: options.scale,
|
scale_policy: options.scale,
|
||||||
dw_style: flags,
|
dw_style: flags,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Only works on Windows 10 unfortunately.
|
// Only works on Windows 10 unfortunately.
|
||||||
|
@ -556,13 +606,18 @@ impl Window {
|
||||||
PostMessageW(self.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0);
|
PostMessageW(self.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
pub fn gl_context(&self) -> Option<&GlContext> {
|
||||||
|
self.gl_context.as_ref().as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl HasRawWindowHandle for Window {
|
unsafe impl HasRawWindowHandle for Window {
|
||||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||||
RawWindowHandle::Windows(WindowsHandle {
|
let mut handle = Win32Handle::empty();
|
||||||
hwnd: self.hwnd as *mut std::ffi::c_void,
|
handle.hwnd = self.hwnd as *mut c_void;
|
||||||
..WindowsHandle::empty()
|
|
||||||
})
|
RawWindowHandle::Win32(handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,12 @@ pub struct WindowHandle {
|
||||||
phantom: PhantomData<*mut ()>,
|
phantom: PhantomData<*mut ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Quick wrapper to satisfy [HasRawWindowHandle], because of course a raw window handle wouldn't
|
||||||
|
/// have a raw window handle, that would be silly.
|
||||||
|
pub(crate) struct RawWindowHandleWrapper {
|
||||||
|
pub handle: RawWindowHandle,
|
||||||
|
}
|
||||||
|
|
||||||
impl WindowHandle {
|
impl WindowHandle {
|
||||||
fn new(window_handle: platform::WindowHandle) -> Self {
|
fn new(window_handle: platform::WindowHandle) -> Self {
|
||||||
Self { window_handle, phantom: PhantomData::default() }
|
Self { window_handle, phantom: PhantomData::default() }
|
||||||
|
@ -91,6 +97,13 @@ impl<'a> Window<'a> {
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
self.window.close();
|
self.window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If provided, then an OpenGL context will be created for this window. You'll be able to
|
||||||
|
/// access this context through [crate::Window::gl_context].
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
pub fn gl_context(&self) -> Option<&crate::gl::GlContext> {
|
||||||
|
self.window.gl_context()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a> HasRawWindowHandle for Window<'a> {
|
unsafe impl<'a> HasRawWindowHandle for Window<'a> {
|
||||||
|
@ -98,3 +111,9 @@ unsafe impl<'a> HasRawWindowHandle for Window<'a> {
|
||||||
self.window.raw_window_handle()
|
self.window.raw_window_handle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl HasRawWindowHandle for RawWindowHandleWrapper {
|
||||||
|
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||||
|
self.handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,4 +21,9 @@ pub struct WindowOpenOptions {
|
||||||
|
|
||||||
/// The dpi scaling policy
|
/// The dpi scaling policy
|
||||||
pub scale: WindowScalePolicy,
|
pub scale: WindowScalePolicy,
|
||||||
|
|
||||||
|
/// If provided, then an OpenGL context will be created for this window. You'll be able to
|
||||||
|
/// access this context through [crate::Window::gl_context].
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
pub gl_config: Option<crate::gl::GlConfig>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,5 +101,5 @@ pub(super) fn get_xcursor(display: *mut x11::xlib::Display, cursor: MouseCursor)
|
||||||
MouseCursor::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
|
MouseCursor::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
|
||||||
};
|
};
|
||||||
|
|
||||||
cursor.or(load(b"left_ptr\0")).unwrap_or(0)
|
cursor.or_else(|| load(b"left_ptr\0")).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@ use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::*;
|
use std::time::*;
|
||||||
|
|
||||||
use raw_window_handle::{unix::XlibHandle, HasRawWindowHandle, RawWindowHandle};
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, XlibHandle};
|
||||||
|
use xcb::ffi::xcb_screen_t;
|
||||||
|
use xcb::StructPtr;
|
||||||
|
|
||||||
use super::XcbConnection;
|
use super::XcbConnection;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -16,6 +18,12 @@ use crate::{
|
||||||
|
|
||||||
use super::keyboard::{convert_key_press_event, convert_key_release_event};
|
use super::keyboard::{convert_key_press_event, convert_key_release_event};
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
use crate::{
|
||||||
|
gl::{platform, GlContext},
|
||||||
|
window::RawWindowHandleWrapper,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct WindowHandle {
|
pub struct WindowHandle {
|
||||||
raw_window_handle: Option<RawWindowHandle>,
|
raw_window_handle: Option<RawWindowHandle>,
|
||||||
close_requested: Arc<AtomicBool>,
|
close_requested: Arc<AtomicBool>,
|
||||||
|
@ -27,7 +35,7 @@ pub struct WindowHandle {
|
||||||
|
|
||||||
impl WindowHandle {
|
impl WindowHandle {
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
if let Some(_) = self.raw_window_handle.take() {
|
if self.raw_window_handle.take().is_some() {
|
||||||
// FIXME: This will need to be changed from just setting an atomic to somehow
|
// FIXME: This will need to be changed from just setting an atomic to somehow
|
||||||
// synchronizing with the window being closed (using a synchronous channel, or
|
// synchronizing with the window being closed (using a synchronous channel, or
|
||||||
// by joining on the event loop thread).
|
// by joining on the event loop thread).
|
||||||
|
@ -49,7 +57,7 @@ unsafe impl HasRawWindowHandle for WindowHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RawWindowHandle::Xlib(XlibHandle { ..raw_window_handle::unix::XlibHandle::empty() })
|
RawWindowHandle::Xlib(XlibHandle::empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +96,7 @@ pub struct Window {
|
||||||
xcb_connection: XcbConnection,
|
xcb_connection: XcbConnection,
|
||||||
window_id: u32,
|
window_id: u32,
|
||||||
window_info: WindowInfo,
|
window_info: WindowInfo,
|
||||||
|
// FIXME: There's all this mouse cursor logic but it's never actually used, is this correct?
|
||||||
mouse_cursor: MouseCursor,
|
mouse_cursor: MouseCursor,
|
||||||
|
|
||||||
frame_interval: Duration,
|
frame_interval: Duration,
|
||||||
|
@ -96,6 +105,9 @@ pub struct Window {
|
||||||
|
|
||||||
new_physical_size: Option<PhySize>,
|
new_physical_size: Option<PhySize>,
|
||||||
parent_handle: Option<ParentHandle>,
|
parent_handle: Option<ParentHandle>,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context: Option<GlContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack to allow sending a RawWindowHandle between threads. Do not make public
|
// Hack to allow sending a RawWindowHandle between threads. Do not make public
|
||||||
|
@ -124,7 +136,7 @@ impl Window {
|
||||||
|
|
||||||
let (parent_handle, mut window_handle) = ParentHandle::new();
|
let (parent_handle, mut window_handle) = ParentHandle::new();
|
||||||
|
|
||||||
let thread = thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
Self::window_thread(Some(parent_id), options, build, tx.clone(), Some(parent_handle));
|
Self::window_thread(Some(parent_id), options, build, tx.clone(), Some(parent_handle));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -144,7 +156,7 @@ impl Window {
|
||||||
|
|
||||||
let (parent_handle, mut window_handle) = ParentHandle::new();
|
let (parent_handle, mut window_handle) = ParentHandle::new();
|
||||||
|
|
||||||
let thread = thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
Self::window_thread(None, options, build, tx.clone(), Some(parent_handle));
|
Self::window_thread(None, options, build, tx.clone(), Some(parent_handle));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -163,12 +175,14 @@ impl Window {
|
||||||
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
||||||
|
|
||||||
let thread = thread::spawn(move || {
|
let thread = thread::spawn(move || {
|
||||||
Self::window_thread(None, options, build, tx.clone(), None);
|
Self::window_thread(None, options, build, tx, None);
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = rx.recv().unwrap().unwrap();
|
let _ = rx.recv().unwrap().unwrap();
|
||||||
|
|
||||||
thread.join();
|
thread.join().unwrap_or_else(|err| {
|
||||||
|
eprintln!("Window thread panicked: {:#?}", err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_thread<H, B>(
|
fn window_thread<H, B>(
|
||||||
|
@ -205,10 +219,49 @@ impl Window {
|
||||||
|
|
||||||
let window_info = WindowInfo::from_logical_size(options.size, scaling);
|
let window_info = WindowInfo::from_logical_size(options.size, scaling);
|
||||||
|
|
||||||
let window_id = xcb_connection.conn.generate_id();
|
// Now it starts becoming fun. If we're creating an OpenGL context, then we need to create
|
||||||
xcb::create_window(
|
// the window with a visual that matches the framebuffer used for the OpenGL context. So the
|
||||||
|
// idea is that we first retrieve a framebuffer config that matches our wanted OpenGL
|
||||||
|
// configuration, find the visual that matches that framebuffer config, create the window
|
||||||
|
// with that visual, and then finally create an OpenGL context for the window. If we don't
|
||||||
|
// use OpenGL, then we'll just take a random visual with a 32-bit depth.
|
||||||
|
let create_default_config = || {
|
||||||
|
Self::find_visual_for_depth(&screen, 32)
|
||||||
|
.map(|visual| (32, visual))
|
||||||
|
.unwrap_or((xcb::COPY_FROM_PARENT as u8, xcb::COPY_FROM_PARENT as u32))
|
||||||
|
};
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
let (fb_config, (depth, visual)) = match options.gl_config {
|
||||||
|
Some(gl_config) => unsafe {
|
||||||
|
platform::GlContext::get_fb_config_and_visual(
|
||||||
|
xcb_connection.conn.get_raw_dpy(),
|
||||||
|
gl_config,
|
||||||
|
)
|
||||||
|
.map(|(fb_config, window_config)| {
|
||||||
|
(Some(fb_config), (window_config.depth, window_config.visual))
|
||||||
|
})
|
||||||
|
.expect("Could not fetch framebuffer config")
|
||||||
|
},
|
||||||
|
None => (None, create_default_config()),
|
||||||
|
};
|
||||||
|
#[cfg(not(feature = "opengl"))]
|
||||||
|
let (depth, visual) = create_default_config();
|
||||||
|
|
||||||
|
// For this 32-bith depth to work, you also need to define a color map and set a border
|
||||||
|
// pixel: https://cgit.freedesktop.org/xorg/xserver/tree/dix/window.c#n818
|
||||||
|
let colormap = xcb_connection.conn.generate_id();
|
||||||
|
xcb::create_colormap(
|
||||||
&xcb_connection.conn,
|
&xcb_connection.conn,
|
||||||
xcb::COPY_FROM_PARENT as u8,
|
xcb::COLORMAP_ALLOC_NONE as u8,
|
||||||
|
colormap,
|
||||||
|
screen.root(),
|
||||||
|
visual,
|
||||||
|
);
|
||||||
|
|
||||||
|
let window_id = xcb_connection.conn.generate_id();
|
||||||
|
xcb::create_window_checked(
|
||||||
|
&xcb_connection.conn,
|
||||||
|
depth,
|
||||||
window_id,
|
window_id,
|
||||||
parent_id,
|
parent_id,
|
||||||
0, // x coordinate of the new window
|
0, // x coordinate of the new window
|
||||||
|
@ -217,18 +270,26 @@ impl Window {
|
||||||
window_info.physical_size().height as u16, // window height
|
window_info.physical_size().height as u16, // window height
|
||||||
0, // window border
|
0, // window border
|
||||||
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
|
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
|
||||||
screen.root_visual(),
|
visual,
|
||||||
&[(
|
&[
|
||||||
xcb::CW_EVENT_MASK,
|
(
|
||||||
xcb::EVENT_MASK_EXPOSURE
|
xcb::CW_EVENT_MASK,
|
||||||
| xcb::EVENT_MASK_POINTER_MOTION
|
xcb::EVENT_MASK_EXPOSURE
|
||||||
| xcb::EVENT_MASK_BUTTON_PRESS
|
| xcb::EVENT_MASK_POINTER_MOTION
|
||||||
| xcb::EVENT_MASK_BUTTON_RELEASE
|
| xcb::EVENT_MASK_BUTTON_PRESS
|
||||||
| xcb::EVENT_MASK_KEY_PRESS
|
| xcb::EVENT_MASK_BUTTON_RELEASE
|
||||||
| xcb::EVENT_MASK_KEY_RELEASE
|
| xcb::EVENT_MASK_KEY_PRESS
|
||||||
| xcb::EVENT_MASK_STRUCTURE_NOTIFY,
|
| xcb::EVENT_MASK_KEY_RELEASE
|
||||||
)],
|
| xcb::EVENT_MASK_STRUCTURE_NOTIFY,
|
||||||
);
|
),
|
||||||
|
// As mentioend above, these two values are needed to be able to create a window
|
||||||
|
// with a dpeth of 32-bits when the parent window has a different depth
|
||||||
|
(xcb::CW_COLORMAP, colormap),
|
||||||
|
(xcb::CW_BORDER_PIXEL, 0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.request_check()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
xcb::map_window(&xcb_connection.conn, window_id);
|
xcb::map_window(&xcb_connection.conn, window_id);
|
||||||
|
|
||||||
|
@ -244,19 +305,35 @@ impl Window {
|
||||||
title.as_bytes(),
|
title.as_bytes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
xcb_connection.atoms.wm_protocols.zip(xcb_connection.atoms.wm_delete_window).map(
|
if let Some((wm_protocols, wm_delete_window)) =
|
||||||
|(wm_protocols, wm_delete_window)| {
|
xcb_connection.atoms.wm_protocols.zip(xcb_connection.atoms.wm_delete_window)
|
||||||
xcb_util::icccm::set_wm_protocols(
|
{
|
||||||
&xcb_connection.conn,
|
xcb_util::icccm::set_wm_protocols(
|
||||||
window_id,
|
&xcb_connection.conn,
|
||||||
wm_protocols,
|
window_id,
|
||||||
&[wm_delete_window],
|
wm_protocols,
|
||||||
);
|
&[wm_delete_window],
|
||||||
},
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
xcb_connection.conn.flush();
|
xcb_connection.conn.flush();
|
||||||
|
|
||||||
|
// TODO: These APIs could use a couple tweaks now that everything is internal and there is
|
||||||
|
// no error handling anymore at this point. Everything is more or less unchanged
|
||||||
|
// compared to when raw-gl-context was a separate crate.
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
let gl_context = fb_config.map(|fb_config| {
|
||||||
|
let mut handle = XlibHandle::empty();
|
||||||
|
handle.window = window_id as c_ulong;
|
||||||
|
handle.display = xcb_connection.conn.get_raw_dpy() as *mut c_void;
|
||||||
|
let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Xlib(handle) };
|
||||||
|
|
||||||
|
// Because of the visual negotation we had to take some extra steps to create this context
|
||||||
|
let context = unsafe { platform::GlContext::create(&handle, fb_config) }
|
||||||
|
.expect("Could not create OpenGL context");
|
||||||
|
GlContext::new(context)
|
||||||
|
});
|
||||||
|
|
||||||
let mut window = Self {
|
let mut window = Self {
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
window_id,
|
window_id,
|
||||||
|
@ -269,6 +346,9 @@ impl Window {
|
||||||
|
|
||||||
new_physical_size: None,
|
new_physical_size: None,
|
||||||
parent_handle,
|
parent_handle,
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
gl_context,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut handler = build(&mut crate::Window::new(&mut window));
|
let mut handler = build(&mut crate::Window::new(&mut window));
|
||||||
|
@ -309,6 +389,27 @@ impl Window {
|
||||||
self.close_requested = true;
|
self.close_requested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
pub fn gl_context(&self) -> Option<&crate::gl::GlContext> {
|
||||||
|
self.gl_context.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_visual_for_depth(screen: &StructPtr<xcb_screen_t>, depth: u8) -> Option<u32> {
|
||||||
|
for candidate_depth in screen.allowed_depths() {
|
||||||
|
if candidate_depth.depth() != depth {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for candidate_visual in candidate_depth.visuals() {
|
||||||
|
if candidate_visual.class() == xcb::VISUAL_CLASS_TRUE_COLOR as u8 {
|
||||||
|
return Some(candidate_visual.visual_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) {
|
fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) {
|
||||||
// the X server has a tendency to send spurious/extraneous configure notify events when a
|
// the X server has a tendency to send spurious/extraneous configure notify events when a
|
||||||
|
@ -538,7 +639,7 @@ impl Window {
|
||||||
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(self),
|
&mut crate::Window::new(self),
|
||||||
Event::Keyboard(convert_key_press_event(&event)),
|
Event::Keyboard(convert_key_press_event(event)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,7 +648,7 @@ impl Window {
|
||||||
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(self),
|
&mut crate::Window::new(self),
|
||||||
Event::Keyboard(convert_key_release_event(&event)),
|
Event::Keyboard(convert_key_release_event(event)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,11 +659,11 @@ impl Window {
|
||||||
|
|
||||||
unsafe impl HasRawWindowHandle for Window {
|
unsafe impl HasRawWindowHandle for Window {
|
||||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||||
RawWindowHandle::Xlib(XlibHandle {
|
let mut handle = XlibHandle::empty();
|
||||||
window: self.window_id as c_ulong,
|
handle.window = self.window_id as c_ulong;
|
||||||
display: self.xcb_connection.conn.get_raw_dpy() as *mut c_void,
|
handle.display = self.xcb_connection.conn.get_raw_dpy() as *mut c_void;
|
||||||
..raw_window_handle::unix::XlibHandle::empty()
|
|
||||||
})
|
RawWindowHandle::Xlib(handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ use super::cursor;
|
||||||
pub(crate) struct Atoms {
|
pub(crate) struct Atoms {
|
||||||
pub wm_protocols: Option<u32>,
|
pub wm_protocols: Option<u32>,
|
||||||
pub wm_delete_window: Option<u32>,
|
pub wm_delete_window: Option<u32>,
|
||||||
pub wm_normal_hints: Option<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct XcbConnection {
|
pub struct XcbConnection {
|
||||||
|
@ -20,6 +19,7 @@ pub struct XcbConnection {
|
||||||
|
|
||||||
pub(crate) atoms: Atoms,
|
pub(crate) atoms: Atoms,
|
||||||
|
|
||||||
|
// FIXME: Same here, there's a ton of unused cursor machinery in here
|
||||||
pub(super) cursor_cache: HashMap<MouseCursor, u32>,
|
pub(super) cursor_cache: HashMap<MouseCursor, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,14 +46,13 @@ impl XcbConnection {
|
||||||
|
|
||||||
conn.set_event_queue_owner(xcb::base::EventQueueOwner::Xcb);
|
conn.set_event_queue_owner(xcb::base::EventQueueOwner::Xcb);
|
||||||
|
|
||||||
let (wm_protocols, wm_delete_window, wm_normal_hints) =
|
let (wm_protocols, wm_delete_window) = intern_atoms!(&conn, WM_PROTOCOLS, WM_DELETE_WINDOW);
|
||||||
intern_atoms!(&conn, WM_PROTOCOLS, WM_DELETE_WINDOW, WM_NORMAL_HINTS);
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
conn,
|
conn,
|
||||||
xlib_display,
|
xlib_display,
|
||||||
|
|
||||||
atoms: Atoms { wm_protocols, wm_delete_window, wm_normal_hints },
|
atoms: Atoms { wm_protocols, wm_delete_window },
|
||||||
|
|
||||||
cursor_cache: HashMap::new(),
|
cursor_cache: HashMap::new(),
|
||||||
})
|
})
|
||||||
|
@ -136,7 +135,7 @@ impl XcbConnection {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_scaling(&self) -> Option<f64> {
|
pub fn get_scaling(&self) -> Option<f64> {
|
||||||
self.get_scaling_xft().or(self.get_scaling_screen_dimensions())
|
self.get_scaling_xft().or_else(|| self.get_scaling_screen_dimensions())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
Loading…
Reference in a new issue