-
Notifications
You must be signed in to change notification settings - Fork 298
/
Copy pathall_winit_glium_threaded.rs
213 lines (181 loc) · 9.39 KB
/
all_winit_glium_threaded.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//! This example behaves the same as the `all_winit_glium` example while demonstrating how to run
//! the `conrod` loop on a separate thread.
#[cfg(all(feature="winit", feature="glium"))] #[macro_use] extern crate conrod;
#[cfg(all(feature="winit", feature="glium"))] mod support;
fn main() {
feature::main();
}
#[cfg(all(feature="winit", feature="glium"))]
mod feature {
extern crate find_folder;
extern crate image;
use conrod;
use conrod::backend::glium::glium;
use conrod::backend::glium::glium::Surface;
use support;
use std;
// The initial width and height in "points".
const WIN_W: u32 = support::WIN_W;
const WIN_H: u32 = support::WIN_H;
pub fn main() {
// Build the window.
let mut events_loop = glium::glutin::EventsLoop::new();
let window = glium::glutin::WindowBuilder::new()
.with_title("Conrod with glium!")
.with_dimensions(WIN_W, WIN_H);
let context = glium::glutin::ContextBuilder::new()
.with_vsync(true)
.with_multisampling(4);
let display = glium::Display::new(window, context, &events_loop).unwrap();
// A type used for converting `conrod::render::Primitives` into `Command`s that can be used
// for drawing to the glium `Surface`.
//
// Internally, the `Renderer` maintains:
// - a `backend::glium::GlyphCache` for caching text onto a `glium::texture::Texture2d`.
// - a `glium::Program` to use as the shader program when drawing to the `glium::Surface`.
// - a `Vec` for collecting `backend::glium::Vertex`s generated when translating the
// `conrod::render::Primitive`s.
// - a `Vec` of commands that describe how to draw the vertices.
let mut renderer = conrod::backend::glium::Renderer::new(&display).unwrap();
// Load the Rust logo from our assets folder to use as an example image.
fn load_rust_logo(display: &glium::Display) -> glium::texture::Texture2d {
let assets = find_folder::Search::ParentsThenKids(3, 3).for_folder("assets").unwrap();
let path = assets.join("images/rust.png");
let rgba_image = image::open(&std::path::Path::new(&path)).unwrap().to_rgba();
let image_dimensions = rgba_image.dimensions();
let raw_image = glium::texture::RawImage2d::from_raw_rgba_reversed(&rgba_image.into_raw(), image_dimensions);
let texture = glium::texture::Texture2d::new(display, raw_image).unwrap();
texture
}
let mut image_map = conrod::image::Map::new();
let rust_logo = image_map.insert(load_rust_logo(&display));
// A channel to send events from the main `winit` thread to the conrod thread.
let (event_tx, event_rx) = std::sync::mpsc::channel();
// A channel to send `render::Primitive`s from the conrod thread to the `winit thread.
let (render_tx, render_rx) = std::sync::mpsc::channel();
// Clone the handle to the events loop so that we can interrupt it when ready to draw.
let events_loop_proxy = events_loop.create_proxy();
// A function that runs the conrod loop.
fn run_conrod(rust_logo: conrod::image::Id,
event_rx: std::sync::mpsc::Receiver<conrod::event::Input>,
render_tx: std::sync::mpsc::Sender<conrod::render::OwnedPrimitives>,
events_loop_proxy: glium::glutin::EventsLoopProxy)
{
// Construct our `Ui`.
let mut ui = conrod::UiBuilder::new([WIN_W as f64, WIN_H as f64]).theme(support::theme()).build();
// Add a `Font` to the `Ui`'s `font::Map` from file.
let assets = find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap();
let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
ui.fonts.insert_from_file(font_path).unwrap();
// A demonstration of some app state that we want to control with the conrod GUI.
let mut app = support::DemoApp::new(rust_logo);
// The `widget::Id` of each widget instantiated in `support::gui`.
let ids = support::Ids::new(ui.widget_id_generator());
// Many widgets require another frame to finish drawing after clicks or hovers, so we
// insert an update into the conrod loop using this `bool` after each event.
let mut needs_update = true;
'conrod: loop {
// Collect any pending events.
let mut events = Vec::new();
while let Ok(event) = event_rx.try_recv() {
events.push(event);
}
// If there are no events pending, wait for them.
if events.is_empty() || !needs_update {
match event_rx.recv() {
Ok(event) => events.push(event),
Err(_) => break 'conrod,
};
}
needs_update = false;
// Input each event into the `Ui`.
for event in events {
ui.handle_event(event);
needs_update = true;
}
// Instantiate a GUI demonstrating every widget type provided by conrod.
support::gui(&mut ui.set_widgets(), &ids, &mut app);
// Render the `Ui` to a list of primitives that we can send to the main thread for
// display. Wakeup `winit` for rendering.
if let Some(primitives) = ui.draw_if_changed() {
if render_tx.send(primitives.owned()).is_err()
|| events_loop_proxy.wakeup().is_err() {
break 'conrod;
}
}
}
}
// Draws the given `primitives` to the given `Display`.
fn draw(display: &glium::Display,
renderer: &mut conrod::backend::glium::Renderer,
image_map: &conrod::image::Map<glium::Texture2d>,
primitives: &conrod::render::OwnedPrimitives)
{
renderer.fill(display, primitives.walk(), &image_map);
let mut target = display.draw();
target.clear_color(0.0, 0.0, 0.0, 1.0);
renderer.draw(display, &mut target, &image_map).unwrap();
target.finish().unwrap();
}
// Spawn the conrod loop on its own thread.
std::thread::spawn(move || run_conrod(rust_logo, event_rx, render_tx, events_loop_proxy));
// Run the `winit` loop.
let mut last_update = std::time::Instant::now();
let mut closed = false;
while !closed {
// We don't want to loop any faster than 60 FPS, so wait until it has been at least
// 16ms since the last yield.
let sixteen_ms = std::time::Duration::from_millis(16);
let now = std::time::Instant::now();
let duration_since_last_update = now.duration_since(last_update);
if duration_since_last_update < sixteen_ms {
std::thread::sleep(sixteen_ms - duration_since_last_update);
}
events_loop.run_forever(|event| {
// Use the `winit` backend feature to convert the winit event to a conrod one.
if let Some(event) = conrod::backend::winit::convert_event(event.clone(), &display) {
event_tx.send(event).unwrap();
}
match event {
glium::glutin::Event::WindowEvent { event, .. } => match event {
// Break from the loop upon `Escape`.
glium::glutin::WindowEvent::Closed |
glium::glutin::WindowEvent::KeyboardInput {
input: glium::glutin::KeyboardInput {
virtual_keycode: Some(glium::glutin::VirtualKeyCode::Escape),
..
},
..
} => {
closed = true;
return glium::glutin::ControlFlow::Break;
},
// We must re-draw on `Resized`, as the event loops become blocked during
// resize on macOS.
glium::glutin::WindowEvent::Resized(..) => {
if let Some(primitives) = render_rx.iter().next() {
draw(&display, &mut renderer, &image_map, &primitives);
}
},
_ => {},
},
glium::glutin::Event::Awakened => return glium::glutin::ControlFlow::Break,
_ => (),
}
glium::glutin::ControlFlow::Continue
});
// Draw the most recently received `conrod::render::Primitives` sent from the `Ui`.
if let Some(primitives) = render_rx.try_iter().last() {
draw(&display, &mut renderer, &image_map, &primitives);
}
last_update = std::time::Instant::now();
}
}
}
#[cfg(not(all(feature="winit", feature="glium")))]
mod feature {
pub fn main() {
println!("This example requires the `winit` and `glium` features. \
Try running `cargo run --release --features=\"winit glium\" --example <example_name>`");
}
}