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