From aa58f4149a65783b07c0dc401b5854ed80c4915e Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 30 Apr 2015 13:23:37 +0200 Subject: Add a GlContext trait --- src/api/caca/mod.rs | 72 +++++++++++++++++++---------------- src/api/cocoa/headless.rs | 18 +++++++-- src/api/cocoa/mod.rs | 85 ++++++++++++++++++++++-------------------- src/api/egl/mod.rs | 23 +++++++----- src/api/glx/mod.rs | 22 +++++++---- src/api/osmesa/mod.rs | 26 +++++++++---- src/api/win32/mod.rs | 79 +++++++++++++++++++-------------------- src/api/x11/mod.rs | 91 +++++++++++++++++++++++---------------------- src/headless.rs | 28 ++++++++++++++ src/lib.rs | 28 ++++++++++++++ src/platform/windows/mod.rs | 22 +++++++++-- src/window.rs | 28 +++++++++++++- 12 files changed, 332 insertions(+), 190 deletions(-) diff --git a/src/api/caca/mod.rs b/src/api/caca/mod.rs index d8199dd..da7810a 100644 --- a/src/api/caca/mod.rs +++ b/src/api/caca/mod.rs @@ -3,9 +3,11 @@ use libc; use api::osmesa::OsMesaContext; +use Api; use BuilderAttribs; use CreationError; use Event; +use GlContext; use PixelFormat; use CursorState; use MouseCursor; @@ -169,34 +171,6 @@ impl Window { } } - pub unsafe fn make_current(&self) { - self.opengl.make_current() - } - - pub fn is_current(&self) -> bool { - self.opengl.is_current() - } - - pub fn get_proc_address(&self, addr: &str) -> *const () { - self.opengl.get_proc_address(addr) as *const _ - } - - pub fn swap_buffers(&self) { - unsafe { - let canvas = (self.libcaca.caca_get_canvas)(self.display); - let width = (self.libcaca.caca_get_canvas_width)(canvas); - let height = (self.libcaca.caca_get_canvas_height)(canvas); - - let buffer = self.opengl.get_framebuffer().chunks(self.opengl.get_dimensions().0 as usize) - .flat_map(|i| i.iter().cloned()).rev().collect::>(); - - (self.libcaca.caca_dither_bitmap)(canvas, 0, 0, width as libc::c_int, - height as libc::c_int, self.dither, - buffer.as_ptr() as *const _); - (self.libcaca.caca_refresh_display)(self.display); - }; - } - pub fn platform_display(&self) -> *mut libc::c_void { unimplemented!() } @@ -205,10 +179,6 @@ impl Window { unimplemented!() } - pub fn get_api(&self) -> ::Api { - self.opengl.get_api() - } - pub fn get_pixel_format(&self) -> PixelFormat { unimplemented!(); } @@ -232,6 +202,44 @@ impl Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + self.opengl.make_current() + } + + fn is_current(&self) -> bool { + self.opengl.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.opengl.get_proc_address(addr) + } + + fn swap_buffers(&self) { + unsafe { + let canvas = (self.libcaca.caca_get_canvas)(self.display); + let width = (self.libcaca.caca_get_canvas_width)(canvas); + let height = (self.libcaca.caca_get_canvas_height)(canvas); + + let buffer = self.opengl.get_framebuffer().chunks(self.opengl.get_dimensions().0 as usize) + .flat_map(|i| i.iter().cloned()).rev().collect::>(); + + (self.libcaca.caca_dither_bitmap)(canvas, 0, 0, width as libc::c_int, + height as libc::c_int, self.dither, + buffer.as_ptr() as *const _); + (self.libcaca.caca_refresh_display)(self.display); + }; + } + + fn get_api(&self) -> Api { + self.opengl.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.opengl.get_pixel_format() + } +} + impl Drop for Window { fn drop(&mut self) { unsafe { diff --git a/src/api/cocoa/headless.rs b/src/api/cocoa/headless.rs index 298027f..cc2a526 100644 --- a/src/api/cocoa/headless.rs +++ b/src/api/cocoa/headless.rs @@ -1,6 +1,7 @@ use CreationError; use CreationError::OsError; use BuilderAttribs; +use GlContext; use libc; use std::ptr; @@ -56,8 +57,10 @@ impl HeadlessContext { Ok(headless) } +} - pub unsafe fn make_current(&self) { +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { self.context.makeCurrentContext(); gl::GenFramebuffersEXT(1, &mut framebuffer); @@ -76,11 +79,11 @@ impl HeadlessContext { } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unimplemented!() } - pub fn get_proc_address(&self, _addr: &str) -> *const () { + fn get_proc_address(&self, _addr: &str) -> *const () { let symbol_name: CFString = _addr.parse().unwrap(); let framework_name: CFString = "com.apple.opengl".parse().unwrap(); let framework = unsafe { @@ -92,9 +95,16 @@ impl HeadlessContext { symbol as *const () } - pub fn get_api(&self) -> ::Api { + fn swap_buffers(&self) { + } + + fn get_api(&self) -> ::Api { ::Api::OpenGl } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); + } } unsafe impl Send for HeadlessContext {} diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index 28d5ecc..101cfc0 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -9,6 +9,7 @@ use libc; use Api; use BuilderAttribs; +use GlContext; use GlRequest; use PixelFormat; use native_monitor::NativeMonitorId; @@ -672,39 +673,6 @@ impl Window { return None; } - pub unsafe fn make_current(&self) { - let _: () = msg_send![*self.context, update]; - self.context.makeCurrentContext(); - } - - pub fn is_current(&self) -> bool { - unsafe { - let current = NSOpenGLContext::currentContext(nil); - if current != nil { - let is_equal: BOOL = msg_send![current, isEqual:*self.context]; - is_equal != NO - } else { - false - } - } - } - - pub fn get_proc_address(&self, _addr: &str) -> *const () { - let symbol_name: CFString = FromStr::from_str(_addr).unwrap(); - let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); - let framework = unsafe { - CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) - }; - let symbol = unsafe { - CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) - }; - symbol as *const () - } - - pub fn swap_buffers(&self) { - unsafe { self.context.flushBuffer(); } - } - pub fn platform_display(&self) -> *mut libc::c_void { unimplemented!() } @@ -713,14 +681,6 @@ impl Window { unimplemented!() } - pub fn get_api(&self) -> ::Api { - ::Api::OpenGl - } - - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } - pub fn set_window_resize_callback(&mut self, callback: Option) { self.delegate.state.resize_handler = callback; } @@ -791,6 +751,49 @@ impl Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + let _: () = msg_send![*self.context, update]; + self.context.makeCurrentContext(); + } + + fn is_current(&self) -> bool { + unsafe { + let current = NSOpenGLContext::currentContext(nil); + if current != nil { + let is_equal: BOOL = msg_send![current, isEqual:*self.context]; + is_equal != NO + } else { + false + } + } + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + let symbol_name: CFString = FromStr::from_str(_addr).unwrap(); + let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); + let framework = unsafe { + CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) + }; + let symbol = unsafe { + CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) + }; + symbol as *const _ + } + + fn swap_buffers(&self) { + unsafe { self.context.flushBuffer(); } + } + + fn get_api(&self) -> ::Api { + ::Api::OpenGl + } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() + } +} + struct IdRef(id); impl IdRef { diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs index 9c8db62..a6b6bcc 100644 --- a/src/api/egl/mod.rs +++ b/src/api/egl/mod.rs @@ -2,6 +2,7 @@ use BuilderAttribs; use CreationError; +use GlContext; use GlRequest; use PixelFormat; use Api; @@ -156,8 +157,10 @@ impl Context { pixel_format: pixel_format, }) } +} - pub fn make_current(&self) { +impl GlContext for Context { + unsafe fn make_current(&self) { let ret = unsafe { self.egl.MakeCurrent(self.display, self.surface, self.surface, self.context) }; @@ -167,23 +170,19 @@ impl Context { } } - pub fn get_pixel_format(&self) -> &PixelFormat { - &self.pixel_format - } - - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unsafe { self.egl.GetCurrentContext() == self.context } } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); unsafe { - self.egl.GetProcAddress(addr) as *const () + self.egl.GetProcAddress(addr) as *const _ } } - pub fn swap_buffers(&self) { + fn swap_buffers(&self) { let ret = unsafe { self.egl.SwapBuffers(self.display, self.surface) }; @@ -193,9 +192,13 @@ impl Context { } } - pub fn get_api(&self) -> Api { + fn get_api(&self) -> Api { self.api } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() + } } unsafe impl Send for Context {} diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs index fb86dfd..7aa77e7 100644 --- a/src/api/glx/mod.rs +++ b/src/api/glx/mod.rs @@ -2,8 +2,10 @@ use BuilderAttribs; use CreationError; +use GlContext; use GlRequest; use Api; +use PixelFormat; use libc; use std::ffi::CString; @@ -140,35 +142,41 @@ impl Context { context: context, }) } +} - pub fn make_current(&self) { - let res = unsafe { ffi::glx::MakeCurrent(self.display as *mut _, self.window, self.context) }; +impl GlContext for Context { + unsafe fn make_current(&self) { + let res = ffi::glx::MakeCurrent(self.display as *mut _, self.window, self.context); if res == 0 { panic!("glx::MakeCurrent failed"); } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unsafe { ffi::glx::GetCurrentContext() == self.context } } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); unsafe { - ffi::glx::GetProcAddress(addr as *const _) as *const () + ffi::glx::GetProcAddress(addr as *const _) as *const _ } } - pub fn swap_buffers(&self) { + fn swap_buffers(&self) { unsafe { ffi::glx::SwapBuffers(self.display as *mut _, self.window) } } - pub fn get_api(&self) -> ::Api { + fn get_api(&self) -> ::Api { ::Api::OpenGl } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); + } } unsafe impl Send for Context {} diff --git a/src/api/osmesa/mod.rs b/src/api/osmesa/mod.rs index ea90583..61eebc1 100644 --- a/src/api/osmesa/mod.rs +++ b/src/api/osmesa/mod.rs @@ -2,9 +2,12 @@ extern crate osmesa_sys; +use Api; use BuilderAttribs; use CreationError; use CreationError::OsError; +use GlContext; +use PixelFormat; use libc; use std::{mem, ptr}; use std::ffi::CString; @@ -43,7 +46,13 @@ impl OsMesaContext { (self.width, self.height) } - pub unsafe fn make_current(&self) { + // TODO: can we remove this without causing havoc? + pub fn set_window_resize_callback(&mut self, _: Option) { + } +} + +impl GlContext for OsMesaContext { + unsafe fn make_current(&self) { let ret = osmesa_sys::OSMesaMakeCurrent(self.context, self.buffer.as_ptr() as *mut libc::c_void, 0x1401, self.width as libc::c_int, self.height as libc::c_int); @@ -53,23 +62,26 @@ impl OsMesaContext { } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unsafe { osmesa_sys::OSMesaGetCurrentContext() == self.context } } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { unsafe { let c_str = CString::new(addr.as_bytes().to_vec()).unwrap(); mem::transmute(osmesa_sys::OSMesaGetProcAddress(mem::transmute(c_str.as_ptr()))) } } - /// See the docs in the crate root file. - pub fn get_api(&self) -> ::Api { - ::Api::OpenGl + fn swap_buffers(&self) { } - pub fn set_window_resize_callback(&mut self, _: Option) { + fn get_api(&self) -> Api { + Api::OpenGl + } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); } } diff --git a/src/api/win32/mod.rs b/src/api/win32/mod.rs index dd3e685..e6f5cb8 100644 --- a/src/api/win32/mod.rs +++ b/src/api/win32/mod.rs @@ -14,7 +14,9 @@ use std::sync::mpsc::Receiver; use libc; use {CreationError, Event, MouseCursor}; use CursorState; +use GlContext; +use Api; use PixelFormat; use BuilderAttribs; @@ -218,37 +220,6 @@ impl Window { } } - /// See the docs in the crate root file. - pub unsafe fn make_current(&self) { - // TODO: check return value - gl::wgl::MakeCurrent(self.window.1 as *const libc::c_void, - self.context.0 as *const libc::c_void); - } - - /// See the docs in the crate root file. - pub fn is_current(&self) -> bool { - unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const libc::c_void } - } - - /// See the docs in the crate root file. - pub fn get_proc_address(&self, addr: &str) -> *const () { - let addr = CString::new(addr.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - - unsafe { - let p = gl::wgl::GetProcAddress(addr) as *const (); - if !p.is_null() { return p; } - kernel32::GetProcAddress(self.gl_library, addr) as *const () - } - } - - /// See the docs in the crate root file. - pub fn swap_buffers(&self) { - unsafe { - gdi32::SwapBuffers(self.window.1); - } - } - pub fn platform_display(&self) -> *mut libc::c_void { unimplemented!() } @@ -257,15 +228,6 @@ impl Window { self.window.0 as *mut libc::c_void } - /// See the docs in the crate root file. - pub fn get_api(&self) -> ::Api { - ::Api::OpenGl - } - - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } - pub fn set_window_resize_callback(&mut self, _: Option) { } @@ -362,6 +324,43 @@ impl Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + // TODO: check return value + gl::wgl::MakeCurrent(self.window.1 as *const libc::c_void, + self.context.0 as *const libc::c_void); + } + + fn is_current(&self) -> bool { + unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const libc::c_void } + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + let addr = CString::new(addr.as_bytes()).unwrap(); + let addr = addr.as_ptr(); + + unsafe { + let p = gl::wgl::GetProcAddress(addr) as *const _; + if !p.is_null() { return p; } + kernel32::GetProcAddress(self.gl_library, addr) as *const _ + } + } + + fn swap_buffers(&self) { + unsafe { + gdi32::SwapBuffers(self.window.1); + } + } + + fn get_api(&self) -> Api { + Api::OpenGl + } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() + } +} + pub struct PollEventsIterator<'a> { window: &'a Window, } diff --git a/src/api/x11/mod.rs b/src/api/x11/mod.rs index 6b528ab..9946243 100644 --- a/src/api/x11/mod.rs +++ b/src/api/x11/mod.rs @@ -13,6 +13,7 @@ use std::sync::{Arc, Mutex, Once, ONCE_INIT}; use Api; use CursorState; +use GlContext; use GlRequest; use PixelFormat; @@ -674,38 +675,6 @@ impl Window { } } - pub unsafe fn make_current(&self) { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.make_current(), - Context::Egl(ref ctxt) => ctxt.make_current(), - Context::None => {} - } - } - - pub fn is_current(&self) -> bool { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.is_current(), - Context::Egl(ref ctxt) => ctxt.is_current(), - Context::None => panic!() - } - } - - pub fn get_proc_address(&self, addr: &str) -> *const () { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.get_proc_address(addr), - Context::Egl(ref ctxt) => ctxt.get_proc_address(addr), - Context::None => ptr::null() - } - } - - pub fn swap_buffers(&self) { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.swap_buffers(), - Context::Egl(ref ctxt) => ctxt.swap_buffers(), - Context::None => {} - } - } - pub fn platform_display(&self) -> *mut libc::c_void { self.x.display as *mut libc::c_void } @@ -714,18 +683,6 @@ impl Window { unimplemented!() } - /// See the docs in the crate root file. - pub fn get_api(&self) -> ::Api { - match self.x.context { - Context::Glx(ref ctxt) => ctxt.get_api(), - Context::Egl(ref ctxt) => ctxt.get_api(), - Context::None => panic!() - } - } - - pub fn get_pixel_format(&self) -> PixelFormat { - self.pixel_format.clone() - } pub fn set_window_resize_callback(&mut self, _: Option) { } @@ -828,3 +785,49 @@ impl Window { Ok(()) } } + +impl GlContext for Window { + unsafe fn make_current(&self) { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.make_current(), + Context::Egl(ref ctxt) => ctxt.make_current(), + Context::None => {} + } + } + + fn is_current(&self) -> bool { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.is_current(), + Context::Egl(ref ctxt) => ctxt.is_current(), + Context::None => panic!() + } + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.get_proc_address(addr), + Context::Egl(ref ctxt) => ctxt.get_proc_address(addr), + Context::None => ptr::null() + } + } + + fn swap_buffers(&self) { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.swap_buffers(), + Context::Egl(ref ctxt) => ctxt.swap_buffers(), + Context::None => {} + } + } + + fn get_api(&self) -> Api { + match self.x.context { + Context::Glx(ref ctxt) => ctxt.get_api(), + Context::Egl(ref ctxt) => ctxt.get_api(), + Context::None => panic!() + } + } + + fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format.clone() + } +} diff --git a/src/headless.rs b/src/headless.rs index dc42108..566cbee 100644 --- a/src/headless.rs +++ b/src/headless.rs @@ -2,6 +2,8 @@ use Api; use BuilderAttribs; use CreationError; use GlRequest; +use GlContext; +use PixelFormat; use gl_common; use libc; @@ -101,3 +103,29 @@ impl gl_common::GlFunctionsSource for HeadlessContext { self.get_proc_address(addr) } } + +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { + self.make_current() + } + + fn is_current(&self) -> bool { + self.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.get_proc_address(addr) + } + + fn swap_buffers(&self) { + self.swap_buffers() + } + + fn get_api(&self) -> Api { + self.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.get_pixel_format() + } +} diff --git a/src/lib.rs b/src/lib.rs index 50f998b..be66099 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,34 @@ mod headless; #[cfg(feature = "window")] mod window; +/// Trait that describes objects that have access to an OpenGL context. +pub trait GlContext { + /// Sets the context as the current context. + unsafe fn make_current(&self); + + /// Returns true if this context is the current one in this thread. + fn is_current(&self) -> bool; + + /// Returns the address of an OpenGL function. + fn get_proc_address(&self, addr: &str) -> *const libc::c_void; + + /// Swaps the buffers in case of double or triple buffering. + /// + /// You should call this function every time you have finished rendering, or the image + /// may not be displayed on the screen. + /// + /// **Warning**: if you enabled vsync, this function will block until the next time the screen + /// is refreshed. However drivers can choose to override your vsync settings, which means that + /// you can't know in advance whether `swap_buffers` will block or not. + fn swap_buffers(&self); + + /// Returns the OpenGL API being used. + fn get_api(&self) -> Api; + + /// Returns the pixel format of the main framebuffer of the context. + fn get_pixel_format(&self) -> PixelFormat; +} + /// Error that can happen while creating a window or a headless renderer. #[derive(Clone, Debug, PartialEq, Eq)] pub enum CreationError { diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 78cbefc..260ab94 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -2,9 +2,13 @@ pub use api::win32::*; +use libc; + use Api; use BuilderAttribs; use CreationError; +use PixelFormat; +use GlContext; /// pub struct HeadlessContext(Window); @@ -14,20 +18,30 @@ impl HeadlessContext { builder.visible = false; Window::new(builder).map(|w| HeadlessContext(w)) } +} - pub unsafe fn make_current(&self) { +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { self.0.make_current() } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { self.0.is_current() } - pub fn get_proc_address(&self, addr: &str) -> *const () { + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { self.0.get_proc_address(addr) } - pub fn get_api(&self) -> Api { + fn swap_buffers(&self) { + self.0.swap_buffers() + } + + fn get_api(&self) -> Api { self.0.get_api() } + + fn get_pixel_format(&self) -> PixelFormat { + self.0.get_pixel_format() + } } diff --git a/src/window.rs b/src/window.rs index 0dd9905..7508f32 100644 --- a/src/window.rs +++ b/src/window.rs @@ -6,6 +6,7 @@ use BuilderAttribs; use CreationError; use CursorState; use Event; +use GlContext; use GlRequest; use MouseCursor; use PixelFormat; @@ -428,6 +429,32 @@ impl gl_common::GlFunctionsSource for Window { } } +impl GlContext for Window { + unsafe fn make_current(&self) { + self.make_current() + } + + fn is_current(&self) -> bool { + self.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + self.get_proc_address(addr) + } + + fn swap_buffers(&self) { + self.swap_buffers() + } + + fn get_api(&self) -> Api { + self.get_api() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.get_pixel_format() + } +} + /// Represents a thread safe subset of operations that can be called /// on a window. This structure can be safely cloned and sent between /// threads. @@ -437,7 +464,6 @@ pub struct WindowProxy { } impl WindowProxy { - /// Triggers a blocked event loop to wake up. This is /// typically called when another thread wants to wake /// up the blocked rendering thread to cause a refresh. -- cgit v1.2.3