Created
February 2, 2015 20:25
-
-
Save dylanede/f2cb8b6c95f210b2fdbf to your computer and use it in GitHub Desktop.
CEF in Glium
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(os, collections, core)] | |
extern crate cef; | |
extern crate glium; | |
extern crate glutin; | |
extern crate image; | |
use std::cell::RefCell; | |
use std::default::Default; | |
use std::rc::Rc; | |
use glium::{DisplayBuild, Surface, Rect}; | |
use cef::{CefRc, Browser, Void}; | |
struct SharedData { | |
buffer: Option<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>>, | |
changed: bool | |
} | |
struct MyRenderHandler(Rc<RefCell<SharedData>>); | |
impl cef::RenderHandler for MyRenderHandler { | |
// This callback notifies CEF of the intended view size | |
fn get_view_rect(&mut self, _: CefRc<Browser>) -> Option<cef::Rect> { | |
Some(cef::Rect { x: 0, y: 0, width: 1024, height: 768 }) | |
} | |
// On every invalidation of the browser frame | |
fn on_paint(&mut self, | |
_: CefRc<Browser>, | |
_: cef::PaintElementType, | |
_: &[cef::Rect], | |
buffer: &[u8], // The current image in BGRA | |
width: i32, // width | |
height: i32) { // and height in pixels | |
let mut shared = self.0.borrow_mut(); | |
shared.changed = true; | |
// If the last buffer made wasn't used we can use it again. | |
// Otherwise we must make a new one as the last one was consumed by the Texture2D. | |
let mut v = shared.buffer.take() | |
.map(|x| x.into_raw()) | |
.unwrap_or_else(|| Vec::new()); | |
let len = (width as usize) * (height as usize) * 4; // 4 channels | |
// The vector is resized without creating new values to save on initialisation. | |
v.reserve_exact(len); | |
unsafe{ v.set_len(len) }; | |
v[].clone_from_slice(buffer); // This is the actual copy from the buffer | |
// The explicit unwrap then rewrap is just to prevent any silent errors | |
shared.buffer = Some(image::ImageBuffer::from_raw(width as u32, height as u32, v).unwrap()); | |
} | |
} | |
struct MyBrowserClient(Rc<RefCell<SharedData>>); | |
impl cef::BrowserClient for MyBrowserClient { | |
// When associated types are fully implemented all of these Void definitions can be removed | |
type OutContextMenuHandler = Void; | |
type OutDialogHandler = Void; | |
type OutDisplayHandler = Void; | |
type OutDownloadHandler = Void; | |
type OutDragHandler = Void; | |
type OutFindHandler = Void; | |
type OutFocusHandler = Void; | |
type OutGeolocationHandler = Void; | |
type OutJSDialogHandler = Void; | |
type OutKeyboardHandler = Void; | |
type OutLifeSpanHandler = Void; | |
type OutLoadHandler = Void; | |
type OutRequestHandler = Void; | |
type OutRenderHandler = MyRenderHandler; | |
fn get_render_handler(&mut self) -> Option<MyRenderHandler> { | |
Some(MyRenderHandler(self.0.clone())) | |
} | |
} | |
fn main() { | |
// This is used by helper processes. For more details see the CEF docs. | |
let result_code = cef::execute_process::<()>(None); | |
if result_code >= 0 { | |
std::os::set_exit_status(result_code); | |
return; | |
} | |
let settings = cef::Settings { | |
log_file: Some("log.log"), | |
locale: Some("en_GB"), | |
windowless_rendering_enabled: true, | |
.. Default::default() | |
}; | |
if !cef::initialize::<()>(&settings, None) { | |
panic!("Initialising CEF failed. Please check the log file.") | |
} | |
let window_info = cef::WindowInfo { | |
windowless_rendering_enabled: true, | |
transparent_painting_enabled: true, | |
.. Default::default() | |
}; | |
let shared = Rc::new(RefCell::new(SharedData { buffer: None, changed: false })); | |
cef::BrowserHost::create_browser_sync( | |
&window_info, MyBrowserClient(shared.clone()), "http://www.google.com", | |
&cef::BrowserSettings::new(), None); | |
// Create the window | |
let display = glutin::WindowBuilder::new() | |
.with_dimensions(1024, 768) | |
.with_vsync() | |
.build_glium() | |
.unwrap(); | |
let mut browser_texture = glium::Texture2d::new(&display, image::DynamicImage::new_rgba8(1, 1)); | |
'main: loop { // The UI/render loop | |
let target = display.draw(); | |
// Update the browser | |
cef::do_message_loop_work(); // <--- shared is only locked by the render handler within this call | |
{ // This scope limits the RefCell borrow (not really necessary in this case) | |
let mut shared = shared.borrow_mut(); | |
if shared.changed { // Update the texture | |
// The texture is completely reallocated - this is actually the best for performance. | |
// Unfortunately it will regenerate mipmaps needlessly | |
// Also I wish I could just memcpy straight from the CEF buffer into an allocated, uninitialised texture. | |
browser_texture = glium::Texture2d::new(&display, shared.buffer.take().unwrap()); | |
shared.changed = false; | |
} | |
} | |
browser_texture.as_surface().fill(&target, glium::uniforms::MagnifySamplerFilter::Linear); | |
target.finish(); | |
// TODO: Feed events into CEF | |
for event in display.poll_events() { | |
match event { | |
glutin::Event::Closed => break 'main, | |
_ => () | |
} | |
} | |
} | |
// Important to do this to close helper processes and clean up. | |
cef::shutdown(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment