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 extern crate euclid;
6 extern crate gleam;
7 extern crate glutin;
8 extern crate webrender;
9 extern crate winit;
10 
11 use gleam::gl;
12 use glutin::NotCurrent;
13 use std::fs::File;
14 use std::io::Read;
15 use webrender::api::*;
16 use webrender::api::units::*;
17 use webrender::DebugFlags;
18 use winit::dpi::LogicalSize;
19 
20 struct Notifier {
21     events_proxy: winit::EventsLoopProxy,
22 }
23 
24 impl Notifier {
new(events_proxy: winit::EventsLoopProxy) -> Notifier25     fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
26         Notifier { events_proxy }
27     }
28 }
29 
30 impl RenderNotifier for Notifier {
clone(&self) -> Box<dyn RenderNotifier>31     fn clone(&self) -> Box<dyn RenderNotifier> {
32         Box::new(Notifier {
33             events_proxy: self.events_proxy.clone(),
34         })
35     }
36 
wake_up(&self)37     fn wake_up(&self) {
38         #[cfg(not(target_os = "android"))]
39         let _ = self.events_proxy.wakeup();
40     }
41 
new_frame_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool, _render_time: Option<u64>)42     fn new_frame_ready(&self,
43                        _: DocumentId,
44                        _scrolled: bool,
45                        _composite_needed: bool,
46                        _render_time: Option<u64>) {
47         self.wake_up();
48     }
49 }
50 
51 struct Window {
52     events_loop: winit::EventsLoop, //TODO: share events loop?
53     context: Option<glutin::WindowedContext<NotCurrent>>,
54     renderer: webrender::Renderer,
55     name: &'static str,
56     pipeline_id: PipelineId,
57     document_id: DocumentId,
58     epoch: Epoch,
59     api: RenderApi,
60     font_instance_key: FontInstanceKey,
61 }
62 
63 impl Window {
new(name: &'static str, clear_color: ColorF) -> Self64     fn new(name: &'static str, clear_color: ColorF) -> Self {
65         let events_loop = winit::EventsLoop::new();
66         let window_builder = winit::WindowBuilder::new()
67             .with_title(name)
68             .with_multitouch()
69             .with_dimensions(LogicalSize::new(800., 600.));
70         let context = glutin::ContextBuilder::new()
71             .with_gl(glutin::GlRequest::GlThenGles {
72                 opengl_version: (3, 2),
73                 opengles_version: (3, 0),
74             })
75             .build_windowed(window_builder, &events_loop)
76             .unwrap();
77 
78         let context = unsafe { context.make_current().unwrap() };
79 
80         let gl = match context.get_api() {
81             glutin::Api::OpenGl => unsafe {
82                 gl::GlFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
83             },
84             glutin::Api::OpenGlEs => unsafe {
85                 gl::GlesFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
86             },
87             glutin::Api::WebGl => unimplemented!(),
88         };
89 
90         let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
91 
92         let opts = webrender::RendererOptions {
93             device_pixel_ratio,
94             clear_color: Some(clear_color),
95             ..webrender::RendererOptions::default()
96         };
97 
98         let device_size = {
99             let size = context
100                 .window()
101                 .get_inner_size()
102                 .unwrap()
103                 .to_physical(device_pixel_ratio as f64);
104             DeviceIntSize::new(size.width as i32, size.height as i32)
105         };
106         let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
107         let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts, None, device_size).unwrap();
108         let mut api = sender.create_api();
109         let document_id = api.add_document(device_size, 0);
110 
111         let epoch = Epoch(0);
112         let pipeline_id = PipelineId(0, 0);
113         let mut txn = Transaction::new();
114 
115         let font_key = api.generate_font_key();
116         let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
117         txn.add_raw_font(font_key, font_bytes, 0);
118 
119         let font_instance_key = api.generate_font_instance_key();
120         txn.add_font_instance(font_instance_key, font_key, 32.0, None, None, Vec::new());
121 
122         api.send_transaction(document_id, txn);
123 
124         Window {
125             events_loop,
126             context: Some(unsafe { context.make_not_current().unwrap() }),
127             renderer,
128             name,
129             epoch,
130             pipeline_id,
131             document_id,
132             api,
133             font_instance_key,
134         }
135     }
136 
tick(&mut self) -> bool137     fn tick(&mut self) -> bool {
138         let mut do_exit = false;
139         let my_name = &self.name;
140         let renderer = &mut self.renderer;
141         let api = &mut self.api;
142 
143         self.events_loop.poll_events(|global_event| match global_event {
144             winit::Event::WindowEvent { event, .. } => match event {
145                 winit::WindowEvent::CloseRequested |
146                 winit::WindowEvent::KeyboardInput {
147                     input: winit::KeyboardInput {
148                         virtual_keycode: Some(winit::VirtualKeyCode::Escape),
149                         ..
150                     },
151                     ..
152                 } => {
153                     do_exit = true
154                 }
155                 winit::WindowEvent::KeyboardInput {
156                     input: winit::KeyboardInput {
157                         state: winit::ElementState::Pressed,
158                         virtual_keycode: Some(winit::VirtualKeyCode::P),
159                         ..
160                     },
161                     ..
162                 } => {
163                     println!("set flags {}", my_name);
164                     api.send_debug_cmd(DebugCommand::SetFlags(DebugFlags::PROFILER_DBG))
165                 }
166                 _ => {}
167             }
168             _ => {}
169         });
170         if do_exit {
171             return true
172         }
173 
174         let context = unsafe { self.context.take().unwrap().make_current().unwrap() };
175         let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
176         let device_size = {
177             let size = context
178                 .window()
179                 .get_inner_size()
180                 .unwrap()
181                 .to_physical(device_pixel_ratio as f64);
182             DeviceIntSize::new(size.width as i32, size.height as i32)
183         };
184         let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
185         let mut txn = Transaction::new();
186         let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
187         let space_and_clip = SpaceAndClipInfo::root_scroll(self.pipeline_id);
188 
189         let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
190         builder.push_simple_stacking_context(
191             bounds.origin,
192             space_and_clip.spatial_id,
193             PrimitiveFlags::IS_BACKFACE_VISIBLE,
194         );
195 
196         builder.push_rect(
197             &CommonItemProperties::new(
198                 LayoutRect::new(
199                     LayoutPoint::new(100.0, 200.0),
200                     LayoutSize::new(100.0, 200.0),
201                 ),
202                 space_and_clip,
203             ),
204             LayoutRect::new(
205                 LayoutPoint::new(100.0, 200.0),
206                 LayoutSize::new(100.0, 200.0),
207             ),
208             ColorF::new(0.0, 1.0, 0.0, 1.0));
209 
210         let text_bounds = LayoutRect::new(
211             LayoutPoint::new(100.0, 50.0),
212             LayoutSize::new(700.0, 200.0)
213         );
214         let glyphs = vec![
215             GlyphInstance {
216                 index: 48,
217                 point: LayoutPoint::new(100.0, 100.0),
218             },
219             GlyphInstance {
220                 index: 68,
221                 point: LayoutPoint::new(150.0, 100.0),
222             },
223             GlyphInstance {
224                 index: 80,
225                 point: LayoutPoint::new(200.0, 100.0),
226             },
227             GlyphInstance {
228                 index: 82,
229                 point: LayoutPoint::new(250.0, 100.0),
230             },
231             GlyphInstance {
232                 index: 81,
233                 point: LayoutPoint::new(300.0, 100.0),
234             },
235             GlyphInstance {
236                 index: 3,
237                 point: LayoutPoint::new(350.0, 100.0),
238             },
239             GlyphInstance {
240                 index: 86,
241                 point: LayoutPoint::new(400.0, 100.0),
242             },
243             GlyphInstance {
244                 index: 79,
245                 point: LayoutPoint::new(450.0, 100.0),
246             },
247             GlyphInstance {
248                 index: 72,
249                 point: LayoutPoint::new(500.0, 100.0),
250             },
251             GlyphInstance {
252                 index: 83,
253                 point: LayoutPoint::new(550.0, 100.0),
254             },
255             GlyphInstance {
256                 index: 87,
257                 point: LayoutPoint::new(600.0, 100.0),
258             },
259             GlyphInstance {
260                 index: 17,
261                 point: LayoutPoint::new(650.0, 100.0),
262             },
263         ];
264 
265         builder.push_text(
266             &CommonItemProperties::new(
267                 text_bounds,
268                 space_and_clip,
269             ),
270             text_bounds,
271             &glyphs,
272             self.font_instance_key,
273             ColorF::new(1.0, 1.0, 0.0, 1.0),
274             None,
275         );
276 
277         builder.pop_stacking_context();
278 
279         txn.set_display_list(
280             self.epoch,
281             None,
282             layout_size,
283             builder.finalize(),
284             true,
285         );
286         txn.set_root_pipeline(self.pipeline_id);
287         txn.generate_frame();
288         api.send_transaction(self.document_id, txn);
289 
290         renderer.update();
291         renderer.render(device_size).unwrap();
292         context.swap_buffers().ok();
293 
294         self.context = Some(unsafe { context.make_not_current().unwrap() });
295 
296         false
297     }
298 
deinit(self)299     fn deinit(self) {
300         self.renderer.deinit();
301     }
302 }
303 
main()304 fn main() {
305     let mut win1 = Window::new("window1", ColorF::new(0.3, 0.0, 0.0, 1.0));
306     let mut win2 = Window::new("window2", ColorF::new(0.0, 0.3, 0.0, 1.0));
307 
308     loop {
309         if win1.tick() {
310             break;
311         }
312         if win2.tick() {
313             break;
314         }
315     }
316 
317     win1.deinit();
318     win2.deinit();
319 }
320 
load_file(name: &str) -> Vec<u8>321 fn load_file(name: &str) -> Vec<u8> {
322     let mut file = File::open(name).unwrap();
323     let mut buffer = vec![];
324     file.read_to_end(&mut buffer).unwrap();
325     buffer
326 }
327