1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-10 15:04:32 +03:00

window: A basic opengl capability

This commit is contained in:
Wez Furlong 2019-10-07 07:17:57 -07:00
parent 85311373a5
commit 89a0046f5a
5 changed files with 253 additions and 2 deletions

View File

@ -17,9 +17,11 @@ line_drawing = "0.8"
palette = "0.4"
promise = { path = "../promise" }
resize = "0.3"
glium = { version = "0.26.0-alpha3", optional=true, default-features = false}
[features]
async_await = []
opengl = ["cgl", "glium"]
[target."cfg(windows)".dependencies]
winapi = { version = "0.3", features = [
@ -46,3 +48,4 @@ cocoa = "0.19"
objc = "0.2"
core-foundation = "0.6"
core-graphics = "0.17"
cgl = { version = "0.3", optional = true }

View File

@ -0,0 +1,73 @@
use ::window::*;
use failure::Fallible;
use std::any::Any;
struct MyWindow {
allow_close: bool,
cursor_pos: (u16, u16),
}
impl WindowCallbacks for MyWindow {
fn destroy(&mut self) {
Connection::get().unwrap().terminate_message_loop();
}
fn paint(&mut self, context: &mut dyn PaintContext) {
// Window contents are black in software mode
context.clear(Color::rgb(0x0, 0x0, 0x0));
}
#[cfg(feature = "opengl")]
fn paint_opengl(&mut self, frame: &mut glium::Frame) {
// Window contents are gray in opengl mode
use glium::Surface;
frame.clear_color(0.15, 0.15, 0.15, 1.0);
}
fn as_any(&mut self) -> &mut dyn Any {
self
}
}
fn spawn_window() -> Fallible<()> {
let win = Window::new_window(
"myclass",
"the title",
800,
600,
Box::new(MyWindow {
allow_close: false,
cursor_pos: (100, 200),
}),
)?;
#[cfg(feature = "opengl")]
match win.enable_opengl() {
Ok(_ctx) => {
eprintln!("opengl enabled!");
}
Err(err) => eprintln!("opengl fail: {}", err),
};
#[cfg(not(feature = "opengl"))]
eprintln!(
"opengl not enabled at compile time: cargo run --feature opengl --example basic_opengl"
);
win.show();
win.apply(|myself, _win| {
if let Some(myself) = myself.downcast_ref::<MyWindow>() {
eprintln!(
"got myself; allow_close={}, cursor_pos:{:?}",
myself.allow_close, myself.cursor_pos
);
}
});
Ok(())
}
fn main() -> Fallible<()> {
let conn = Connection::init()?;
spawn_window()?;
conn.run_message_loop()
}

View File

@ -99,11 +99,22 @@ pub trait WindowCallbacks: Any {
/// Called when the window is resized, or when the dpi has changed
fn resize(&mut self, dimensions: Dimensions) {}
/// Called when the window contents need painting
/// Called when the window contents need painting.
/// This is used only when the software renderer is enabled (which
/// is the default). When the window is set to opengl mode, the
/// `paint_opengl` function is called instead.
fn paint(&mut self, context: &mut dyn PaintContext) {
context.clear(Color::rgb(0x20, 0x40, 0x60));
}
/// Called when the window has opengl mode enabled and the window
/// contents need painting.
#[cfg(feature = "opengl")]
fn paint_opengl(&mut self, frame: &mut glium::Frame) {
use glium::Surface;
frame.clear_color(0.25, 0.125, 0.375, 1.0);
}
/// Called to handle a key event.
/// If your window didn't handle the event, you must return false.
/// This is particularly important for eg: ALT keys on windows,
@ -155,6 +166,11 @@ pub trait WindowOps {
fn apply<F: Send + 'static + Fn(&mut dyn Any, &dyn WindowOps)>(&self, func: F)
where
Self: Sized;
#[cfg(feature = "opengl")]
fn enable_opengl(&self) -> failure::Fallible<std::rc::Rc<glium::backend::Context>> {
failure::bail!("opengl support is not implemented");
}
}
pub trait WindowOpsMut {

View File

@ -48,7 +48,7 @@ impl Connection {
.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed)
}
fn window_by_id(&self, window_id: usize) -> Option<Rc<RefCell<WindowInner>>> {
pub(crate) fn window_by_id(&self, window_id: usize) -> Option<Rc<RefCell<WindowInner>>> {
self.windows.borrow().get(&window_id).map(Rc::clone)
}

View File

@ -28,6 +28,119 @@ use std::cell::RefCell;
use std::ffi::c_void;
use std::rc::Rc;
#[cfg(feature = "opengl")]
mod opengl {
use super::*;
use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat};
use cocoa::foundation::NSAutoreleasePool;
use core_foundation::base::TCFType;
use core_foundation::bundle::{
CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName,
};
use core_foundation::string::CFString;
use std::str::FromStr;
pub struct GlState {
_pixel_format: StrongPtr,
gl_context: StrongPtr,
}
impl GlState {
pub fn create(view: id) -> Fallible<Self> {
let pixel_format = unsafe {
StrongPtr::new(NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&[
appkit::NSOpenGLPFAOpenGLProfile as u32,
appkit::NSOpenGLProfileVersionLegacy as u32,
appkit::NSOpenGLPFAClosestPolicy as u32,
appkit::NSOpenGLPFAColorSize as u32,
32,
appkit::NSOpenGLPFAAlphaSize as u32,
8,
appkit::NSOpenGLPFADepthSize as u32,
24,
appkit::NSOpenGLPFAStencilSize as u32,
8,
appkit::NSOpenGLPFAAllowOfflineRenderers as u32,
appkit::NSOpenGLPFAAccelerated as u32,
appkit::NSOpenGLPFADoubleBuffer as u32,
0,
]))
};
failure::ensure!(
!pixel_format.is_null(),
"failed to create NSOpenGLPixelFormat"
);
let gl_context = unsafe {
StrongPtr::new(
NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(*pixel_format, nil),
)
};
failure::ensure!(!gl_context.is_null(), "failed to create NSOpenGLContext");
unsafe {
gl_context.setView_(view);
}
Ok(Self {
_pixel_format: pixel_format,
gl_context,
})
}
}
unsafe impl glium::backend::Backend for GlState {
fn swap_buffers(&self) -> Result<(), glium::SwapBuffersError> {
unsafe {
let pool = NSAutoreleasePool::new(nil);
self.gl_context.flushBuffer();
let _: () = msg_send![pool, release];
}
Ok(())
}
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
let symbol_name: CFString = FromStr::from_str(symbol).unwrap();
let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
let framework = CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef());
let symbol =
CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef());
symbol as *const _
}
fn get_framebuffer_dimensions(&self) -> (u32, u32) {
unsafe {
let view = self.gl_context.view();
let frame = NSView::frame(view);
let backing_frame = NSView::convertRectToBacking(view, frame);
(
backing_frame.size.width as u32,
backing_frame.size.height as u32,
)
}
}
fn is_current(&self) -> bool {
unsafe {
let pool = NSAutoreleasePool::new(nil);
let current = NSOpenGLContext::currentContext(nil);
let res = if current != nil {
let is_equal: BOOL = msg_send![current, isEqual: *self.gl_context];
is_equal != NO
} else {
false
};
let _: () = msg_send![pool, release];
res
}
}
unsafe fn make_current(&self) {
let _: () = msg_send![*self.gl_context, update];
self.gl_context.makeCurrentContext();
}
}
}
pub(crate) struct WindowInner {
window_id: usize,
view: StrongPtr,
@ -63,6 +176,8 @@ impl Window {
callbacks,
view_id: None,
window_id,
#[cfg(feature = "opengl")]
gl_context: None,
}));
let window = StrongPtr::new(
@ -154,6 +269,34 @@ impl WindowOps for Window {
}
});
}
#[cfg(feature = "opengl")]
fn enable_opengl(&self) -> Fallible<Rc<glium::backend::Context>> {
if let Some(handle) = Connection::get().unwrap().window_by_id(self.0) {
let inner = handle.borrow_mut();
let gl_state = opengl::GlState::create(*inner.view)?;
let glium_context = unsafe {
glium::backend::Context::new(
Rc::new(gl_state),
true,
if cfg!(debug_assertions) {
glium::debug::DebugCallbackBehavior::DebugMessageOnError
} else {
glium::debug::DebugCallbackBehavior::Ignore
},
)?
};
if let Some(window_view) = WindowView::get_this(unsafe { &**inner.view }) {
window_view.inner.borrow_mut().gl_context = Some(Rc::clone(&glium_context));
}
Ok(glium_context)
} else {
failure::bail!("invalid window?");
}
}
}
impl WindowOpsMut for WindowInner {
@ -204,6 +347,8 @@ struct Inner {
callbacks: Box<dyn WindowCallbacks>,
view_id: Option<WeakPtr>,
window_id: usize,
#[cfg(feature = "opengl")]
gl_context: Option<Rc<glium::backend::Context>>,
}
const CLS_NAME: &str = "WezTermWindowView";
@ -499,6 +644,20 @@ impl WindowView {
let mut inner = this.inner.borrow_mut();
let mut buffer = this.buffer.borrow_mut();
#[cfg(feature = "opengl")]
{
if let Some(gl_context) = inner.gl_context.as_ref() {
let mut frame =
glium::Frame::new(Rc::clone(gl_context), (width as u32, height as u32));
inner.callbacks.paint_opengl(&mut frame);
frame
.finish()
.expect("frame.finish failed and we don't know how to recover");
return;
}
}
let (pixel_width, pixel_height) = buffer.image_dimensions();
if width as usize != pixel_width || height as usize != pixel_height {
*buffer = Image::new(width as usize, height as usize);