tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

boilerplate.rs (10739B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use gleam::gl;
      6 use glutin;
      7 use std::env;
      8 use std::path::PathBuf;
      9 use webrender;
     10 use winit;
     11 use winit::platform::run_return::EventLoopExtRunReturn;
     12 use webrender::{DebugFlags, ShaderPrecacheFlags};
     13 use webrender::api::*;
     14 use webrender::render_api::*;
     15 use webrender::api::units::*;
     16 
     17 struct Notifier {
     18    events_proxy: winit::event_loop::EventLoopProxy<()>,
     19 }
     20 
     21 impl Notifier {
     22    fn new(events_proxy: winit::event_loop::EventLoopProxy<()>) -> Notifier {
     23        Notifier { events_proxy }
     24    }
     25 }
     26 
     27 impl RenderNotifier for Notifier {
     28    fn clone(&self) -> Box<dyn RenderNotifier> {
     29        Box::new(Notifier {
     30            events_proxy: self.events_proxy.clone(),
     31        })
     32    }
     33 
     34    fn wake_up(&self, _composite_needed: bool) {
     35        #[cfg(not(target_os = "android"))]
     36        let _ = self.events_proxy.send_event(());
     37    }
     38 
     39    fn new_frame_ready(&self,
     40                       _: DocumentId,
     41                       _: FramePublishId,
     42                       params: &FrameReadyParams) {
     43        self.wake_up(params.render);
     44    }
     45 }
     46 
     47 #[allow(dead_code)]
     48 pub trait HandyDandyRectBuilder {
     49    fn to(&self, x2: i32, y2: i32) -> LayoutRect;
     50    fn by(&self, w: i32, h: i32) -> LayoutRect;
     51 }
     52 // Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32
     53 // values to build a f32 LayoutRect
     54 impl HandyDandyRectBuilder for (i32, i32) {
     55    fn to(&self, x2: i32, y2: i32) -> LayoutRect {
     56        LayoutRect::from_origin_and_size(
     57            LayoutPoint::new(self.0 as f32, self.1 as f32),
     58            LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32),
     59        )
     60    }
     61 
     62    fn by(&self, w: i32, h: i32) -> LayoutRect {
     63        LayoutRect::from_origin_and_size(
     64            LayoutPoint::new(self.0 as f32, self.1 as f32),
     65            LayoutSize::new(w as f32, h as f32),
     66        )
     67    }
     68 }
     69 
     70 pub trait Example {
     71    const TITLE: &'static str = "WebRender Sample App";
     72    const PRECACHE_SHADER_FLAGS: ShaderPrecacheFlags = ShaderPrecacheFlags::EMPTY;
     73    const WIDTH: u32 = 1920;
     74    const HEIGHT: u32 = 1080;
     75 
     76    fn render(
     77        &mut self,
     78        api: &mut RenderApi,
     79        builder: &mut DisplayListBuilder,
     80        txn: &mut Transaction,
     81        device_size: DeviceIntSize,
     82        pipeline_id: PipelineId,
     83        document_id: DocumentId,
     84    );
     85    fn on_event(
     86        &mut self,
     87        _: winit::event::WindowEvent,
     88        _: &winit::window::Window,
     89        _: &mut RenderApi,
     90        _: DocumentId,
     91    ) -> bool {
     92        false
     93    }
     94    fn get_image_handler(
     95        &mut self,
     96        _gl: &dyn gl::Gl,
     97    ) -> Option<Box<dyn ExternalImageHandler>> {
     98        None
     99    }
    100    fn draw_custom(&mut self, _gl: &dyn gl::Gl) {
    101    }
    102 }
    103 
    104 pub fn main_wrapper<E: Example>(
    105    example: &mut E,
    106    options: Option<webrender::WebRenderOptions>,
    107 ) {
    108    env_logger::init();
    109 
    110    #[cfg(target_os = "macos")]
    111    {
    112        use core_foundation::{self as cf, base::TCFType};
    113        let i = cf::bundle::CFBundle::main_bundle().info_dictionary();
    114        let mut i = unsafe { i.to_mutable() };
    115        i.set(
    116            cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"),
    117            cf::boolean::CFBoolean::true_value().into_CFType(),
    118        );
    119    }
    120 
    121    let args: Vec<String> = env::args().collect();
    122    let res_path = if args.len() > 1 {
    123        Some(PathBuf::from(&args[1]))
    124    } else {
    125        None
    126    };
    127 
    128    let mut events_loop = winit::event_loop::EventLoop::new();
    129    let window_builder = winit::window::WindowBuilder::new()
    130        .with_title(E::TITLE)
    131        .with_inner_size(winit::dpi::LogicalSize::new(E::WIDTH as f64, E::HEIGHT as f64));
    132    let windowed_context = glutin::ContextBuilder::new()
    133        .with_gl(glutin::GlRequest::GlThenGles {
    134            opengl_version: (3, 2),
    135            opengles_version: (3, 0),
    136        })
    137        .build_windowed(window_builder, &events_loop)
    138        .unwrap();
    139 
    140    let windowed_context = unsafe { windowed_context.make_current().unwrap() };
    141 
    142    let gl = match windowed_context.get_api() {
    143        glutin::Api::OpenGl => unsafe {
    144            gl::GlFns::load_with(
    145                |symbol| windowed_context.get_proc_address(symbol) as *const _
    146            )
    147        },
    148        glutin::Api::OpenGlEs => unsafe {
    149            gl::GlesFns::load_with(
    150                |symbol| windowed_context.get_proc_address(symbol) as *const _
    151            )
    152        },
    153        glutin::Api::WebGl => unimplemented!(),
    154    };
    155 
    156    println!("OpenGL version {}", gl.get_string(gl::VERSION));
    157    println!("Shader resource path: {:?}", res_path);
    158    let device_pixel_ratio = windowed_context.window().scale_factor() as f32;
    159    println!("Device pixel ratio: {}", device_pixel_ratio);
    160 
    161    println!("Loading shaders...");
    162    let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES | DebugFlags::TEXTURE_CACHE_DBG;
    163    let opts = webrender::WebRenderOptions {
    164        resource_override_path: res_path,
    165        precache_flags: E::PRECACHE_SHADER_FLAGS,
    166        clear_color: ColorF::new(0.3, 0.0, 0.0, 1.0),
    167        debug_flags,
    168        //allow_texture_swizzling: false,
    169        ..options.unwrap_or(webrender::WebRenderOptions::default())
    170    };
    171 
    172    let device_size = {
    173        let size = windowed_context
    174            .window()
    175            .inner_size();
    176        DeviceIntSize::new(size.width as i32, size.height as i32)
    177    };
    178    let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
    179    let (mut renderer, sender) = webrender::create_webrender_instance(
    180        gl.clone(),
    181        notifier,
    182        opts,
    183        None,
    184    ).unwrap();
    185    let mut api = sender.create_api();
    186    let document_id = api.add_document(device_size);
    187 
    188    let external = example.get_image_handler(&*gl);
    189 
    190    if let Some(external_image_handler) = external {
    191        renderer.set_external_image_handler(external_image_handler);
    192    }
    193 
    194    let epoch = Epoch(0);
    195    let pipeline_id = PipelineId(0, 0);
    196    let mut builder = DisplayListBuilder::new(pipeline_id);
    197    let mut txn = Transaction::new();
    198    builder.begin();
    199 
    200    example.render(
    201        &mut api,
    202        &mut builder,
    203        &mut txn,
    204        device_size,
    205        pipeline_id,
    206        document_id,
    207    );
    208    txn.set_display_list(
    209        epoch,
    210        builder.end(),
    211    );
    212    txn.set_root_pipeline(pipeline_id);
    213    txn.generate_frame(0, true, false, RenderReasons::empty());
    214    api.send_transaction(document_id, txn);
    215 
    216    println!("Entering event loop");
    217    events_loop.run_return(|global_event, _elwt, control_flow| {
    218        let mut txn = Transaction::new();
    219        let mut custom_event = true;
    220 
    221        let old_flags = debug_flags;
    222        let win_event = match global_event {
    223            winit::event::Event::WindowEvent { event, .. } => event,
    224            _ => return,
    225        };
    226        match win_event {
    227            winit::event::WindowEvent::CloseRequested => {
    228                *control_flow = winit::event_loop::ControlFlow::Exit;
    229                return;
    230            }
    231            winit::event::WindowEvent::AxisMotion { .. } |
    232            winit::event::WindowEvent::CursorMoved { .. } => {
    233                custom_event = example.on_event(
    234                    win_event,
    235                    windowed_context.window(),
    236                    &mut api,
    237                    document_id,
    238                );
    239                // skip high-frequency events from triggering a frame draw.
    240                if !custom_event {
    241                    return;
    242                }
    243            },
    244            winit::event::WindowEvent::KeyboardInput {
    245                input: winit::event::KeyboardInput {
    246                    state: winit::event::ElementState::Pressed,
    247                    virtual_keycode: Some(key),
    248                    ..
    249                },
    250                ..
    251            } => match key {
    252                winit::event::VirtualKeyCode::Escape => {
    253                    *control_flow = winit::event_loop::ControlFlow::Exit;
    254                    return;
    255                }
    256                winit::event::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG),
    257                winit::event::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
    258                winit::event::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
    259                winit::event::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
    260                winit::event::VirtualKeyCode::Q => debug_flags.toggle(
    261                    DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
    262                ),
    263                winit::event::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
    264                winit::event::VirtualKeyCode::M => api.notify_memory_pressure(),
    265                winit::event::VirtualKeyCode::C => {
    266                    let path: PathBuf = "../captures/example".into();
    267                    //TODO: switch between SCENE/FRAME capture types
    268                    // based on "shift" modifier, when `glutin` is updated.
    269                    let bits = CaptureBits::all();
    270                    api.save_capture(path, bits);
    271                },
    272                _ => {
    273                    custom_event = example.on_event(
    274                        win_event,
    275                        windowed_context.window(),
    276                        &mut api,
    277                        document_id,
    278                    )
    279                },
    280            },
    281            other => custom_event = example.on_event(
    282                other,
    283                windowed_context.window(),
    284                &mut api,
    285                document_id,
    286            ),
    287        };
    288 
    289        if debug_flags != old_flags {
    290            api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
    291        }
    292 
    293        if custom_event {
    294            let mut builder = DisplayListBuilder::new(pipeline_id);
    295            builder.begin();
    296 
    297            example.render(
    298                &mut api,
    299                &mut builder,
    300                &mut txn,
    301                device_size,
    302                pipeline_id,
    303                document_id,
    304            );
    305            txn.set_display_list(
    306                epoch,
    307                builder.end(),
    308            );
    309            txn.generate_frame(0, true, false, RenderReasons::empty());
    310        }
    311        api.send_transaction(document_id, txn);
    312 
    313        renderer.update();
    314        renderer.render(device_size, 0).unwrap();
    315        let _ = renderer.flush_pipeline_info();
    316        example.draw_custom(&*gl);
    317        windowed_context.swap_buffers().ok();
    318 
    319        *control_flow = winit::event_loop::ControlFlow::Wait;
    320    });
    321 
    322    renderer.deinit();
    323 }