Skip to content

Commit b79e37b

Browse files
authored
docs: add gtk opengl triangle example (#1682)
1 parent 1540788 commit b79e37b

3 files changed

Lines changed: 274 additions & 11 deletions

File tree

Cargo.lock

Lines changed: 31 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ winit = "0.30"
211211
getrandom = "0.3"
212212
http-range = "0.1"
213213
percent-encoding = "2.3"
214+
glow = "0.16.0"
215+
libloading = "0.8.9"
214216
# For unit testing `inject_initialization_scripts` since we can't do it easily on Android
215217
sha2 = "0.10"
216218
base64 = "0.22"

examples/gtk_opengl.rs

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use std::cell::RefCell;
6+
use std::rc::Rc;
7+
use tao::{
8+
event::{Event, WindowEvent},
9+
event_loop::{ControlFlow, EventLoop},
10+
window::WindowBuilder,
11+
};
12+
use wry::{
13+
dpi::{LogicalPosition, LogicalSize},
14+
Rect, WebViewBuilder,
15+
};
16+
17+
fn main() -> wry::Result<()> {
18+
let event_loop = EventLoop::new();
19+
let window = WindowBuilder::new()
20+
.with_title("GTK OpenGL with Webview")
21+
.with_inner_size(LogicalSize::new(800, 600))
22+
.build(&event_loop)
23+
.unwrap();
24+
25+
#[cfg(not(any(
26+
target_os = "windows",
27+
target_os = "macos",
28+
target_os = "ios",
29+
target_os = "android"
30+
)))]
31+
let (fixed, _) = {
32+
use gtk::prelude::*;
33+
use tao::platform::unix::WindowExtUnix;
34+
35+
let overlay = gtk::Overlay::new();
36+
let vbox = window.default_vbox().unwrap();
37+
vbox.pack_start(&overlay, true, true, 0);
38+
39+
let gl_area = gtk::GLArea::new();
40+
gl_area.set_has_alpha(true);
41+
gl_area.set_auto_render(true);
42+
43+
struct AppState {
44+
gl: glow::Context,
45+
program: glow::Program,
46+
vertex_array: glow::VertexArray,
47+
}
48+
49+
let state: Rc<RefCell<Option<AppState>>> = Rc::new(RefCell::new(None));
50+
let state_realize = state.clone();
51+
52+
gl_area.connect_realize(move |gl_area| {
53+
gl_area.make_current();
54+
if gl_area.error().is_some() {
55+
println!("Error creating GLArea context");
56+
return;
57+
}
58+
59+
let gl = unsafe {
60+
glow::Context::from_loader_function(|s| {
61+
let mut ptr = std::ptr::null();
62+
let name = std::ffi::CString::new(s).unwrap();
63+
64+
if let Ok(lib) = libloading::Library::new("libGL.so.1") {
65+
if let Ok(sym) = lib.get::<unsafe extern "C" fn(*const i8) -> *const std::ffi::c_void>(
66+
b"glXGetProcAddress\0",
67+
) {
68+
ptr = sym(name.as_ptr());
69+
}
70+
}
71+
if ptr.is_null() {
72+
if let Ok(lib) = libloading::Library::new("libEGL.so.1") {
73+
if let Ok(sym) = lib
74+
.get::<unsafe extern "C" fn(*const i8) -> *const std::ffi::c_void>(
75+
b"eglGetProcAddress\0",
76+
)
77+
{
78+
ptr = sym(name.as_ptr());
79+
}
80+
}
81+
}
82+
ptr
83+
})
84+
};
85+
86+
unsafe {
87+
use glow::HasContext as _;
88+
89+
let vertex_array = gl.create_vertex_array().unwrap();
90+
gl.bind_vertex_array(Some(vertex_array));
91+
92+
let program = gl.create_program().expect("Cannot create program");
93+
94+
let vertex_shader_source = r#"
95+
#version 330 core
96+
void main() {
97+
if (gl_VertexID == 0) gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);
98+
else if (gl_VertexID == 1) gl_Position = vec4(0.5, -0.5, 0.0, 1.0);
99+
else gl_Position = vec4(0.0, 0.5, 0.0, 1.0);
100+
}
101+
"#;
102+
103+
let fragment_shader_source = r#"
104+
#version 330 core
105+
out vec4 FragColor;
106+
void main() {
107+
FragColor = vec4(1.0, 0.5, 0.2, 1.0);
108+
}
109+
"#;
110+
111+
let vs = gl.create_shader(glow::VERTEX_SHADER).unwrap();
112+
gl.shader_source(vs, vertex_shader_source);
113+
gl.compile_shader(vs);
114+
if !gl.get_shader_compile_status(vs) {
115+
panic!("{}", gl.get_shader_info_log(vs));
116+
}
117+
118+
let fs = gl.create_shader(glow::FRAGMENT_SHADER).unwrap();
119+
gl.shader_source(fs, fragment_shader_source);
120+
gl.compile_shader(fs);
121+
if !gl.get_shader_compile_status(fs) {
122+
panic!("{}", gl.get_shader_info_log(fs));
123+
}
124+
125+
gl.attach_shader(program, vs);
126+
gl.attach_shader(program, fs);
127+
gl.link_program(program);
128+
if !gl.get_program_link_status(program) {
129+
panic!("{}", gl.get_program_info_log(program));
130+
}
131+
132+
gl.detach_shader(program, vs);
133+
gl.delete_shader(vs);
134+
gl.detach_shader(program, fs);
135+
gl.delete_shader(fs);
136+
137+
*state_realize.borrow_mut() = Some(AppState {
138+
gl,
139+
program,
140+
vertex_array,
141+
});
142+
}
143+
});
144+
145+
let state_render = state.clone();
146+
gl_area.connect_render(move |_gl_area, _gl_context| {
147+
if let Some(state) = state_render.borrow().as_ref() {
148+
unsafe {
149+
use glow::HasContext as _;
150+
state.gl.clear_color(0.1, 0.2, 0.3, 1.0);
151+
state.gl.clear(glow::COLOR_BUFFER_BIT);
152+
153+
state.gl.use_program(Some(state.program));
154+
state.gl.bind_vertex_array(Some(state.vertex_array));
155+
state.gl.draw_arrays(glow::TRIANGLES, 0, 3);
156+
}
157+
}
158+
gtk::glib::Propagation::Proceed
159+
});
160+
161+
gl_area.connect_unrealize(move |gl_area| {
162+
gl_area.make_current();
163+
if let Some(state) = state.borrow_mut().take() {
164+
unsafe {
165+
use glow::HasContext as _;
166+
state.gl.delete_program(state.program);
167+
state.gl.delete_vertex_array(state.vertex_array);
168+
}
169+
}
170+
});
171+
172+
overlay.add(&gl_area);
173+
174+
let fixed = gtk::Fixed::new();
175+
overlay.add_overlay(&fixed);
176+
177+
overlay.show_all();
178+
(fixed, gl_area)
179+
};
180+
181+
let builder = WebViewBuilder::new()
182+
.with_bounds(Rect {
183+
position: LogicalPosition::new(100, 100).into(),
184+
size: LogicalSize::new(400, 300).into(),
185+
})
186+
.with_transparent(true)
187+
.with_html(
188+
r#"<html>
189+
<body>
190+
<h1 style="color: white;">Hello World!</h1>
191+
</body>
192+
</html>"#,
193+
);
194+
195+
#[cfg(any(
196+
target_os = "windows",
197+
target_os = "macos",
198+
target_os = "ios",
199+
target_os = "android"
200+
))]
201+
let webview = builder.build(&window)?;
202+
203+
#[cfg(not(any(
204+
target_os = "windows",
205+
target_os = "macos",
206+
target_os = "ios",
207+
target_os = "android"
208+
)))]
209+
let webview = {
210+
use wry::WebViewBuilderExtUnix;
211+
builder.build_gtk(&fixed)?
212+
};
213+
214+
event_loop.run(move |event, _, control_flow| {
215+
*control_flow = ControlFlow::Wait;
216+
217+
match event {
218+
Event::WindowEvent {
219+
event: WindowEvent::Resized(size),
220+
..
221+
} => {
222+
let size = size.to_logical::<u32>(window.scale_factor());
223+
webview
224+
.set_bounds(Rect {
225+
position: LogicalPosition::new(100, 100).into(),
226+
size: LogicalSize::new(
227+
size.width.saturating_sub(200).max(100),
228+
size.height.saturating_sub(200).max(100),
229+
)
230+
.into(),
231+
})
232+
.unwrap();
233+
}
234+
Event::WindowEvent {
235+
event: WindowEvent::CloseRequested,
236+
..
237+
} => *control_flow = ControlFlow::Exit,
238+
_ => {}
239+
}
240+
});
241+
}

0 commit comments

Comments
 (0)