Skip to content

Instantly share code, notes, and snippets.

@dylanede
Created February 2, 2015 20:25
Show Gist options
  • Save dylanede/f2cb8b6c95f210b2fdbf to your computer and use it in GitHub Desktop.
Save dylanede/f2cb8b6c95f210b2fdbf to your computer and use it in GitHub Desktop.
CEF in Glium
#![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