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:
parent
85311373a5
commit
89a0046f5a
@ -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 }
|
||||
|
73
window/examples/basic_opengl.rs
Normal file
73
window/examples/basic_opengl.rs
Normal 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()
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user