Created
February 4, 2015 22:50
-
-
Save dylanede/12ef89053f641df218cb to your computer and use it in GitHub Desktop.
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, 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, Texture2d, Display, Texture}; | |
use glium::texture::{Texture2dDataSource, RawImage2d, ClientFormat}; | |
use cef::{CefRc, Browser, Void}; | |
struct SharedData { | |
display: Rc<Display>, | |
size: (u32, u32), | |
tex: Option<Texture2d>,//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> { | |
let shared = self.0.borrow(); | |
Some(cef::Rect { x: 0, y: 0, width: shared.size.0 as i32, height: shared.size.1 as i32 }) | |
} | |
// 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; | |
let display = shared.display.clone(); | |
struct MyDataSource<'a>(&'a [u8], u32, u32, ClientFormat); | |
impl<'a, 'b> Texture2dDataSource<'a> for &'b MyDataSource<'a> { | |
type Data = u8; | |
fn into_raw(self) -> RawImage2d<'a, u8> { | |
RawImage2d { | |
data: ::std::borrow::Cow::Borrowed(self.0), | |
width: self.1, | |
height: self.2, | |
format: self.3 | |
} | |
} | |
} | |
let source = MyDataSource(buffer, width as u32, height as u32, ClientFormat::UB8G8R8A8); | |
match &mut shared.tex { | |
&mut Some(ref mut tex) => { | |
let tex_width = tex.get_width(); | |
let tex_height = tex.get_height().unwrap(); | |
if tex_width == width as u32 && tex_height == height as u32 { | |
tex.write( | |
Rect { | |
left: 0, | |
bottom: 0, | |
width: tex_width, | |
height: tex_height | |
}, | |
&source); | |
} else { | |
*tex = Texture2d::new(&*display, &source); | |
} | |
}, | |
x => { | |
*x = Some(Texture2d::new(&*display, &source)); | |
} | |
} | |
} | |
} | |
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() | |
}; | |
// Create the window | |
let display = Rc::new(glutin::WindowBuilder::new() | |
.with_dimensions(1024, 768) | |
.with_vsync() | |
.build_glium() | |
.unwrap()); | |
let mut mouse_event: cef::MouseEvent = Default::default(); | |
let shared = Rc::new(RefCell::new(SharedData { | |
display: display.clone(), | |
size: (1024, 768), | |
tex: None, | |
changed: false | |
})); | |
let browser = cef::BrowserHost::create_browser_sync( | |
&window_info, MyBrowserClient(shared.clone()), "http://www.google.com", | |
&cef::BrowserSettings::new(), None); | |
'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 | |
let mut shared = shared.borrow_mut(); | |
if shared.changed { // Update the texture | |
// The texture is completely reallocated - this is actually the best for performance. | |
//browser_texture = glium::Texture2d::new(&*display, shared.buffer.take().unwrap()); | |
shared.changed = false; | |
} | |
shared.tex.as_mut().map(|x| { | |
x.as_surface().fill(&target, glium::uniforms::MagnifySamplerFilter::Linear) | |
}); | |
} | |
target.finish(); | |
display.synchronize(); | |
for event in display.poll_events() { | |
use glutin::MouseButton::*; | |
use cef::event_flags::*; | |
use cef::MouseButtonType as MouseButton; | |
match event { | |
glutin::Event::Closed => { | |
browser.get_host().close_browser(false); | |
cef::do_message_loop_work(); | |
break 'main | |
}, | |
glutin::Event::Focused(focused) => { | |
browser.get_host().set_focus(focused); | |
}, | |
glutin::Event::Resized(w, h) => { | |
{ | |
let mut shared = shared.borrow_mut(); | |
shared.size = (w, h); | |
} | |
browser.get_host().was_resized(); | |
}, | |
glutin::Event::MouseMoved((x, y)) => { | |
mouse_event.x = x; | |
mouse_event.y = {shared.borrow().size.1-(y as u32)} as i32; | |
browser.get_host().send_mouse_move_event(&mouse_event, false); | |
}, | |
glutin::Event::MouseInput(pressed, button) => { | |
let pressed = match pressed { | |
glutin::ElementState::Pressed => true, | |
glutin::ElementState::Released => false | |
}; | |
match button { | |
LeftMouseButton => Some((LEFT_MOUSE_BUTTON, MouseButton::Left)), | |
RightMouseButton => Some((RIGHT_MOUSE_BUTTON, MouseButton::Right)), | |
MiddleMouseButton => Some((MIDDLE_MOUSE_BUTTON, MouseButton::Middle)), | |
OtherMouseButton(..) => None | |
}.map(|(a, b)| { | |
browser.get_host().send_mouse_click_event(&mouse_event, b, !pressed, 1); | |
mouse_event.modifiers.insert(a); | |
println!("Sent {:?} Down at {}, {}", button, mouse_event.x, mouse_event.y); | |
}); | |
}, | |
_ => () | |
} | |
} | |
} | |
// 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