1
0
Fork 0

Merge branch 'master' into macos-scrollwheel

This commit is contained in:
Ingo Budde 2022-07-24 00:25:46 +02:00
commit 3dd255f238
19 changed files with 1311 additions and 233 deletions

View file

@ -21,7 +21,9 @@ jobs:
with:
toolchain: stable
override: true
- name: Build
- name: Build with default features
run: cargo build --examples --workspace --verbose
- name: Build again with all features
run: cargo build --examples --workspace --all-features --verbose
- name: Run tests
run: cargo test --examples --workspace --verbose
run: cargo test --examples --workspace --all-features --verbose

View file

@ -9,22 +9,28 @@ authors = [
"Billy Messenger <billydm@protonmail.com>",
"Anton Lazarev <https://antonok.com>",
"Joakim Frostegård <joakim.frostegard@gmail.com>",
"Robbert van der Helm <mail@robbertvanderhelm.nl>",
]
edition = "2018"
license = "MIT OR Apache-2.0"
[features]
default = []
opengl = ["uuid", "x11/glx"]
[dependencies]
keyboard-types = { version = "0.5.0", default-features = false }
raw-window-handle = "0.3.3"
keyboard-types = { version = "0.6.1", default-features = false }
raw-window-handle = "0.4.2"
[target.'cfg(target_os="linux")'.dependencies]
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
x11 = { version = "2.18", features = ["xlib", "xcursor"] }
xcb-util = { version = "0.3", features = ["icccm"] }
nix = "0.18"
nix = "0.22.0"
[target.'cfg(target_os="windows")'.dependencies]
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]
cocoa = "0.24.0"

View file

@ -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.
| Feature | Windows | Mac OS | Linux |
| ----------------------------------------------- | ------------------ | ------------------ | ------------------ |
| 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: |
| 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: |
| Parent window support | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Feature | Windows | Mac OS | Linux |
| ----------------------------------------------------- | ------------------ | ------------------ | ------------------ |
| 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: |
| 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: |
| 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

View file

@ -36,6 +36,10 @@ fn main() {
title: "baseview".into(),
size: baseview::Size::new(512.0, 512.0),
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);

146
src/gl/macos.rs Normal file
View 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
View 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
View 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
View 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
View 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()
}
}

View file

@ -17,6 +17,7 @@
//! Keyboard types.
#[cfg(any(target_os = "linux", target_os = "macos"))]
use keyboard_types::{Code, Location};
#[cfg(any(target_os = "linux", target_os = "macos"))]

View file

@ -12,6 +12,9 @@ mod window;
mod window_info;
mod window_open_options;
#[cfg(feature = "opengl")]
pub mod gl;
pub use event::*;
pub use mouse_cursor::MouseCursor;
pub use window::*;

View file

@ -1,5 +1,6 @@
use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@ -16,7 +17,7 @@ use keyboard_types::KeyboardEvent;
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::{
Event, EventStatus, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
@ -26,6 +27,12 @@ use crate::{
use super::keyboard::KeyboardState;
use super::view::{create_view, BASEVIEW_STATE_IVAR};
#[cfg(feature = "opengl")]
use crate::{
gl::{GlConfig, GlContext},
window::RawWindowHandleWrapper,
};
pub struct WindowHandle {
raw_window_handle: Option<RawWindowHandle>,
close_requested: Arc<AtomicBool>,
@ -37,7 +44,7 @@ pub struct WindowHandle {
impl WindowHandle {
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);
}
}
@ -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
ns_view: id,
close_requested: bool,
#[cfg(feature = "opengl")]
gl_context: Option<GlContext>,
}
impl Window {
@ -114,7 +124,7 @@ impl Window {
{
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
} else {
panic!("Not a macOS window");
@ -122,7 +132,17 @@ impl Window {
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);
@ -146,7 +166,17 @@ impl Window {
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);
@ -217,6 +247,11 @@ impl Window {
ns_window: Some(ns_window),
ns_view,
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);
@ -266,6 +301,21 @@ impl Window {
pub fn close(&mut self) {
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 {
@ -373,7 +423,7 @@ impl WindowState {
// Clear ivar before triggering WindowEvent::WillClose. Otherwise, if the
// handler of the event causes another call to release, this function could be
// 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));
@ -386,12 +436,12 @@ impl WindowState {
unsafe impl HasRawWindowHandle for Window {
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 {
ns_window,
ns_view: self.ns_view as *mut c_void,
..MacOSHandle::empty()
})
let mut handle = AppKitHandle::empty();
handle.ns_window = ns_window;
handle.ns_view = self.ns_view as *mut c_void;
RawWindowHandle::AppKit(handle)
}
}

View file

@ -19,7 +19,6 @@
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use std::mem;
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::windef::HWND;
use winapi::um::winuser::{
GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, VkKeyScanW,
MAPVK_VK_TO_CHAR, MAPVK_VSC_TO_VK_EX, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN,
VK_BACK, VK_BROWSER_BACK, VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME,
VK_BROWSER_REFRESH, VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR,
VK_CONTROL, VK_CONVERT, VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF,
VK_ESCAPE, VK_EXECUTE, 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_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI,
VK_LAUNCH_APP1, VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT,
VK_LMENU, VK_LSHIFT, VK_LWIN, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK,
GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, PeekMessageW, ToUnicodeEx, MAPVK_VK_TO_CHAR,
MAPVK_VSC_TO_VK_EX, PM_NOREMOVE, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN, VK_BACK, VK_BROWSER_BACK,
VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, VK_BROWSER_REFRESH,
VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR, VK_CONTROL, VK_CONVERT,
VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF, VK_ESCAPE, VK_EXECUTE,
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_F9, VK_FINAL, VK_HELP, VK_HOME, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI, VK_LAUNCH_APP1,
VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT, VK_LMENU,
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_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,
@ -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 {
match code_unit {
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_MENU 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() {
let ret = ToUnicodeEx(
vk as UINT,

View file

@ -16,14 +16,14 @@ use winapi::um::winuser::{
};
use std::cell::RefCell;
use std::ffi::OsStr;
use std::ffi::{c_void, OsStr};
use std::marker::PhantomData;
use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
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;
@ -34,6 +34,9 @@ use crate::{
use super::keyboard::KeyboardState;
#[cfg(feature = "opengl")]
use crate::{gl::GlContext, window::RawWindowHandleWrapper};
unsafe fn generate_guid() -> String {
let mut guid: GUID = std::mem::zeroed();
CoCreateGuid(&mut guid);
@ -80,12 +83,12 @@ impl WindowHandle {
unsafe impl HasRawWindowHandle for WindowHandle {
fn raw_window_handle(&self) -> RawWindowHandle {
if let Some(hwnd) = self.hwnd {
RawWindowHandle::Windows(WindowsHandle {
hwnd: hwnd as *mut std::ffi::c_void,
..WindowsHandle::empty()
})
let mut handle = Win32Handle::empty();
handle.hwnd = hwnd as *mut c_void;
RawWindowHandle::Win32(handle)
} 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>;
if !window_state_ptr.is_null() {
let mut window = Window { hwnd };
let mut window = crate::Window::new(&mut window);
match msg {
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 y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
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);
window_state.handler.on_event(
@ -145,12 +147,14 @@ unsafe extern "system" fn wnd_proc(
return 0;
}
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 = value as i32;
let value = value as f32 / WHEEL_DELTA as f32;
let mut window_state = (&*window_state_ptr).borrow_mut();
window_state.handler.on_event(
&mut window,
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_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 {
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)
.borrow_mut()
.handler
.on_event(&mut window, Event::Mouse(event));
window_state.handler.on_event(&mut window, Event::Mouse(event));
}
}
WM_TIMER => {
match wparam {
WIN_FRAME_TIMER => {
(&*window_state_ptr).borrow_mut().handler.on_frame(&mut window);
}
_ => (),
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);
if wparam == WIN_FRAME_TIMER {
window_state.handler.on_frame(&mut window);
}
return 0;
}
WM_CLOSE => {
(&*window_state_ptr)
.borrow_mut()
.handler
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
// Make sure to release the borrow before the DefWindowProc call
{
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);
window_state
.handler
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
}
// DestroyWindow(hwnd);
// return 0;
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
| WM_INPUTLANGCHANGE => {
let opt_event = (&*window_state_ptr)
.borrow_mut()
.keyboard_state
.process_message(hwnd, msg, wparam, lparam);
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 opt_event =
window_state.keyboard_state.process_message(hwnd, msg, wparam, lparam);
if let Some(event) = opt_event {
(&*window_state_ptr)
.borrow_mut()
.handler
.on_event(&mut window, Event::Keyboard(event));
window_state.handler.on_event(&mut window, Event::Keyboard(event));
}
if msg != WM_SYSKEYDOWN {
@ -243,11 +255,13 @@ unsafe extern "system" fn wnd_proc(
}
}
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 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(
PhySize { width, height },
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)));
}
WM_DPICHANGED => {
let mut window_state = (*window_state_ptr).borrow_mut();
// To avoid weirdness with the realtime borrow checker.
let new_rect = {
let mut window_state = (&*window_state_ptr).borrow_mut();
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
let dpi = (wparam & 0xFFFF) as u16 as u32;
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 {
@ -357,10 +371,28 @@ struct WindowState {
handler: Box<dyn WindowHandler>,
scale_policy: WindowScalePolicy,
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 {
hwnd: HWND,
#[cfg(feature = "opengl")]
gl_context: Arc<Option<GlContext>>,
}
impl Window {
@ -372,7 +404,7 @@ impl Window {
B: Send + 'static,
{
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),
};
@ -410,8 +442,8 @@ impl Window {
break;
}
TranslateMessage(&mut msg);
DispatchMessageW(&mut msg);
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
@ -478,7 +510,22 @@ impl Window {
);
// 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 })));
#[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 = if parented { Some(parent_handle) } else { None };
@ -492,6 +539,9 @@ impl Window {
handler,
scale_policy: options.scale,
dw_style: flags,
#[cfg(feature = "opengl")]
gl_context,
}));
// Only works on Windows 10 unfortunately.
@ -556,13 +606,18 @@ impl Window {
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 {
fn raw_window_handle(&self) -> RawWindowHandle {
RawWindowHandle::Windows(WindowsHandle {
hwnd: self.hwnd as *mut std::ffi::c_void,
..WindowsHandle::empty()
})
let mut handle = Win32Handle::empty();
handle.hwnd = self.hwnd as *mut c_void;
RawWindowHandle::Win32(handle)
}
}

View file

@ -18,6 +18,12 @@ pub struct WindowHandle {
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 {
fn new(window_handle: platform::WindowHandle) -> Self {
Self { window_handle, phantom: PhantomData::default() }
@ -91,6 +97,13 @@ impl<'a> Window<'a> {
pub fn close(&mut self) {
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> {
@ -98,3 +111,9 @@ unsafe impl<'a> HasRawWindowHandle for Window<'a> {
self.window.raw_window_handle()
}
}
unsafe impl HasRawWindowHandle for RawWindowHandleWrapper {
fn raw_window_handle(&self) -> RawWindowHandle {
self.handle
}
}

View file

@ -21,4 +21,9 @@ pub struct WindowOpenOptions {
/// The dpi scaling policy
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>,
}

View file

@ -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"]),
};
cursor.or(load(b"left_ptr\0")).unwrap_or(0)
cursor.or_else(|| load(b"left_ptr\0")).unwrap_or(0)
}

View file

@ -6,7 +6,9 @@ use std::sync::Arc;
use std::thread;
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 crate::{
@ -16,6 +18,12 @@ use crate::{
use super::keyboard::{convert_key_press_event, convert_key_release_event};
#[cfg(feature = "opengl")]
use crate::{
gl::{platform, GlContext},
window::RawWindowHandleWrapper,
};
pub struct WindowHandle {
raw_window_handle: Option<RawWindowHandle>,
close_requested: Arc<AtomicBool>,
@ -27,7 +35,7 @@ pub struct WindowHandle {
impl WindowHandle {
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
// synchronizing with the window being closed (using a synchronous channel, or
// 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,
window_id: u32,
window_info: WindowInfo,
// FIXME: There's all this mouse cursor logic but it's never actually used, is this correct?
mouse_cursor: MouseCursor,
frame_interval: Duration,
@ -96,6 +105,9 @@ pub struct Window {
new_physical_size: Option<PhySize>,
parent_handle: Option<ParentHandle>,
#[cfg(feature = "opengl")]
gl_context: Option<GlContext>,
}
// 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 thread = thread::spawn(move || {
thread::spawn(move || {
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 thread = thread::spawn(move || {
thread::spawn(move || {
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 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();
thread.join();
thread.join().unwrap_or_else(|err| {
eprintln!("Window thread panicked: {:#?}", err);
});
}
fn window_thread<H, B>(
@ -205,10 +219,49 @@ impl Window {
let window_info = WindowInfo::from_logical_size(options.size, scaling);
let window_id = xcb_connection.conn.generate_id();
xcb::create_window(
// Now it starts becoming fun. If we're creating an OpenGL context, then we need to create
// 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::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,
parent_id,
0, // x coordinate of the new window
@ -217,18 +270,26 @@ impl Window {
window_info.physical_size().height as u16, // window height
0, // window border
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(),
&[(
xcb::CW_EVENT_MASK,
xcb::EVENT_MASK_EXPOSURE
| xcb::EVENT_MASK_POINTER_MOTION
| xcb::EVENT_MASK_BUTTON_PRESS
| xcb::EVENT_MASK_BUTTON_RELEASE
| xcb::EVENT_MASK_KEY_PRESS
| xcb::EVENT_MASK_KEY_RELEASE
| xcb::EVENT_MASK_STRUCTURE_NOTIFY,
)],
);
visual,
&[
(
xcb::CW_EVENT_MASK,
xcb::EVENT_MASK_EXPOSURE
| xcb::EVENT_MASK_POINTER_MOTION
| xcb::EVENT_MASK_BUTTON_PRESS
| xcb::EVENT_MASK_BUTTON_RELEASE
| xcb::EVENT_MASK_KEY_PRESS
| 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);
@ -244,19 +305,35 @@ impl Window {
title.as_bytes(),
);
xcb_connection.atoms.wm_protocols.zip(xcb_connection.atoms.wm_delete_window).map(
|(wm_protocols, wm_delete_window)| {
xcb_util::icccm::set_wm_protocols(
&xcb_connection.conn,
window_id,
wm_protocols,
&[wm_delete_window],
);
},
);
if let Some((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,
window_id,
wm_protocols,
&[wm_delete_window],
);
}
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 {
xcb_connection,
window_id,
@ -269,6 +346,9 @@ impl Window {
new_physical_size: None,
parent_handle,
#[cfg(feature = "opengl")]
gl_context,
};
let mut handler = build(&mut crate::Window::new(&mut window));
@ -309,6 +389,27 @@ impl Window {
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]
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
@ -538,7 +639,7 @@ impl Window {
handler.on_event(
&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(
&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 {
fn raw_window_handle(&self) -> RawWindowHandle {
RawWindowHandle::Xlib(XlibHandle {
window: self.window_id as c_ulong,
display: self.xcb_connection.conn.get_raw_dpy() as *mut c_void,
..raw_window_handle::unix::XlibHandle::empty()
})
let mut handle = XlibHandle::empty();
handle.window = self.window_id as c_ulong;
handle.display = self.xcb_connection.conn.get_raw_dpy() as *mut c_void;
RawWindowHandle::Xlib(handle)
}
}

View file

@ -11,7 +11,6 @@ use super::cursor;
pub(crate) struct Atoms {
pub wm_protocols: Option<u32>,
pub wm_delete_window: Option<u32>,
pub wm_normal_hints: Option<u32>,
}
pub struct XcbConnection {
@ -20,6 +19,7 @@ pub struct XcbConnection {
pub(crate) atoms: Atoms,
// FIXME: Same here, there's a ton of unused cursor machinery in here
pub(super) cursor_cache: HashMap<MouseCursor, u32>,
}
@ -46,14 +46,13 @@ impl XcbConnection {
conn.set_event_queue_owner(xcb::base::EventQueueOwner::Xcb);
let (wm_protocols, wm_delete_window, wm_normal_hints) =
intern_atoms!(&conn, WM_PROTOCOLS, WM_DELETE_WINDOW, WM_NORMAL_HINTS);
let (wm_protocols, wm_delete_window) = intern_atoms!(&conn, WM_PROTOCOLS, WM_DELETE_WINDOW);
Ok(Self {
conn,
xlib_display,
atoms: Atoms { wm_protocols, wm_delete_window, wm_normal_hints },
atoms: Atoms { wm_protocols, wm_delete_window },
cursor_cache: HashMap::new(),
})
@ -136,7 +135,7 @@ impl XcbConnection {
#[inline]
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]