opengl monitor
This commit is contained in:
parent
7e2ef5558a
commit
145e09ef53
714
Cargo.lock
generated
714
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,11 @@ quadtree_rs = "0.1.2"
|
|||||||
proj-sys = "0.23.1"
|
proj-sys = "0.23.1"
|
||||||
glib-macros = "0.17.10"
|
glib-macros = "0.17.10"
|
||||||
svg = "0.13.1"
|
svg = "0.13.1"
|
||||||
|
libloading = "0.8.0"
|
||||||
|
glue = "0.8.7"
|
||||||
|
epoxy = "0.1.0"
|
||||||
|
femtovg = "0.7.1"
|
||||||
|
glow = "0.12.2"
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@ -2,6 +2,6 @@ fn main() {
|
|||||||
glib_build_tools::compile_resources(
|
glib_build_tools::compile_resources(
|
||||||
&["src/resources"],
|
&["src/resources"],
|
||||||
"src/resources/resources.gresource.xml",
|
"src/resources/resources.gresource.xml",
|
||||||
"window.gresource",
|
"monitor.gresource",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -193,20 +193,6 @@ where
|
|||||||
});
|
});
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dpi(&self, axis: Axis) {
|
|
||||||
match self.coord_type {
|
|
||||||
CoordType::LatLon => {
|
|
||||||
if axis == Axis::Asix0 {
|
|
||||||
let mut new_dim1 = Array::zeros(self.dim1.raw_dim());
|
|
||||||
let a = self.dim1.slice(s![0..3, ..]);
|
|
||||||
} else if axis == Axis::Asix1 {
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
60
src/main.rs
60
src/main.rs
@ -1,6 +1,4 @@
|
|||||||
use std::cell::RefCell;
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use glib::{timeout_add, timeout_add_local};
|
use glib::{timeout_add, timeout_add_local};
|
||||||
use glib_macros::clone;
|
use glib_macros::clone;
|
||||||
@ -8,30 +6,48 @@ use gtk::gdk::builders::RGBABuilder;
|
|||||||
use gtk::gdk::Display;
|
use gtk::gdk::Display;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gio, glib, style_context_add_provider_for_display, Application, ApplicationWindow, CssProvider,
|
gio, glib, style_context_add_provider_for_display, Application, ApplicationWindow, CssProvider,
|
||||||
GestureDrag,
|
FontButton, GestureDrag,
|
||||||
};
|
};
|
||||||
use gtk::{prelude::*, DrawingArea};
|
use gtk::{prelude::*, DrawingArea};
|
||||||
mod data;
|
mod data;
|
||||||
|
mod monitor;
|
||||||
mod painter;
|
mod painter;
|
||||||
mod render;
|
mod render;
|
||||||
mod tree;
|
mod tree;
|
||||||
mod window;
|
mod window;
|
||||||
use data::{Npz, Radar2d, RadarData2d};
|
use data::{Npz, Radar2d, RadarData2d};
|
||||||
|
use monitor::Monitor;
|
||||||
use ndarray::parallel::prelude::{IntoParallelIterator, ParallelIterator};
|
use ndarray::parallel::prelude::{IntoParallelIterator, ParallelIterator};
|
||||||
use num_traits::{FromPrimitive, Num};
|
|
||||||
use painter::wgs84::{LatLonCoord, Mercator, ProjectionS, Range};
|
use painter::wgs84::{LatLonCoord, Mercator, ProjectionS, Range};
|
||||||
use painter::{Coord, Painter};
|
use render::Render;
|
||||||
use svg::node::element::Rectangle;
|
use render::{BackgroundConfig, BackgroundWidget};
|
||||||
|
|
||||||
const APP_ID: &str = "org.gtk_rs.HelloWorld2";
|
const APP_ID: &str = "org.gtk_rs.HelloWorld2";
|
||||||
|
|
||||||
fn main() -> glib::ExitCode {
|
fn main() -> glib::ExitCode {
|
||||||
// gio::resources_register_include!("window.gresource").expect("Failed to register resources");
|
// Load GL pointers from epoxy (GL context management library used by GTK).
|
||||||
|
{
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let library = unsafe { libloading::os::unix::Library::new("libepoxy.0.dylib") }.unwrap();
|
||||||
|
#[cfg(all(unix, not(target_os = "macos")))]
|
||||||
|
let library = unsafe { libloading::os::unix::Library::new("libepoxy.so.0") }.unwrap();
|
||||||
|
#[cfg(windows)]
|
||||||
|
let library = libloading::os::windows::Library::open_already_loaded("libepoxy-0.dll")
|
||||||
|
.or_else(|_| libloading::os::windows::Library::open_already_loaded("epoxy-0.dll"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
epoxy::load_with(|name| {
|
||||||
|
unsafe { library.get::<_>(name.as_bytes()) }
|
||||||
|
.map(|symbol| *symbol)
|
||||||
|
.unwrap_or(ptr::null())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// gio::resources_register_include!("monitor.gresource").expect("Failed to register resources");
|
||||||
// Create a new application
|
// Create a new application
|
||||||
let app = Application::builder().application_id(APP_ID).build();
|
let app = Application::builder().application_id(APP_ID).build();
|
||||||
// Connect to "activate" signal of `app`
|
// Connect to "activate" signal of `app`
|
||||||
app.connect_activate(build_ui);
|
app.connect_activate(build_ui);
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
app.run()
|
app.run()
|
||||||
}
|
}
|
||||||
@ -43,19 +59,27 @@ fn build_ui(app: &Application) {
|
|||||||
.title("My GTK App")
|
.title("My GTK App")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let drawing_area = DrawingArea::new();
|
let mut background_config = BackgroundConfig::new();
|
||||||
let path = "/Users/ruomu/test2.npz";
|
|
||||||
|
|
||||||
let data = Radar2d::<i8>::load(path, Npz).unwrap();
|
let background_widget = BackgroundWidget::new(background_config);
|
||||||
|
|
||||||
let projection = Mercator::new();
|
let drawing_area = Render::new();
|
||||||
let aa = Range::from((*data.dim1.first().unwrap(), *data.dim1.last().unwrap()));
|
|
||||||
let ab = Range::from((*data.dim2.first().unwrap(), *data.dim2.last().unwrap()));
|
|
||||||
let coord = LatLonCoord::new(Some(aa), Some(ab), ((0, 1000), (0, 1000)), projection);
|
|
||||||
|
|
||||||
let result = data.mapped(&coord);
|
// let path = "/Users/ruomu/test2.npz";
|
||||||
|
|
||||||
window.set_child(Some(&drawing_area));
|
// let data = Radar2d::<i8>::load(path, Npz).unwrap();
|
||||||
|
// let projection = Mercator::new();
|
||||||
|
|
||||||
|
// let aa = Range::from((*data.dim1.first().unwrap(), *data.dim1.last().unwrap()));
|
||||||
|
// let ab = Range::from((*data.dim2.first().unwrap(), *data.dim2.last().unwrap()));
|
||||||
|
|
||||||
|
// let coord = LatLonCoord::new(Some(aa), Some(ab), ((0, 1000), (0, 1000)), projection);
|
||||||
|
|
||||||
|
// let result = data.mapped(&coord);
|
||||||
|
|
||||||
|
let monitor = Monitor::new();
|
||||||
|
|
||||||
|
window.set_child(Some(&monitor));
|
||||||
window.set_default_width(1000);
|
window.set_default_width(1000);
|
||||||
window.set_default_height(800);
|
window.set_default_height(800);
|
||||||
|
|
||||||
|
|||||||
34
src/monitor/imp.rs
Normal file
34
src/monitor/imp.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use crate::render::Render;
|
||||||
|
use glib::subclass::InitializingObject;
|
||||||
|
use gtk::subclass::prelude::*;
|
||||||
|
use gtk::CompositeTemplate;
|
||||||
|
use gtk::{glib, prelude::WidgetExtManual};
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
|
#[derive(Default, CompositeTemplate)]
|
||||||
|
#[template(file = "monitor.ui")]
|
||||||
|
pub struct Monitor {
|
||||||
|
#[template_child(id = "render")]
|
||||||
|
pub(super) label: TemplateChild<Render>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for Monitor {
|
||||||
|
const NAME: &'static str = "Monitor";
|
||||||
|
type Type = super::Monitor;
|
||||||
|
type ParentType = gtk::Box;
|
||||||
|
|
||||||
|
fn class_init(klass: &mut Self::Class) {
|
||||||
|
klass.bind_template();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instance_init(obj: &InitializingObject<Self>) {
|
||||||
|
obj.init_template();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for Monitor {}
|
||||||
|
|
||||||
|
impl WidgetImpl for Monitor {}
|
||||||
|
|
||||||
|
impl BoxImpl for Monitor {}
|
||||||
16
src/monitor/mod.rs
Normal file
16
src/monitor/mod.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
mod imp;
|
||||||
|
use crate::render::Render;
|
||||||
|
use glib::subclass::prelude::*;
|
||||||
|
use gtk::traits::WidgetExt;
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct Monitor(ObjectSubclass<imp::Monitor>)
|
||||||
|
@extends gtk::Box,gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Monitor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let this: Self = glib::Object::new();
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/monitor/monitor.ui
Normal file
10
src/monitor/monitor.ui
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<template class="Monitor" parent="GtkBox">
|
||||||
|
<child>
|
||||||
|
<object class="MyRender" id="render">
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</template>
|
||||||
|
</interface>
|
||||||
@ -9,6 +9,92 @@ type Lon = f64;
|
|||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Range(pub f64, pub f64);
|
pub struct Range(pub f64, pub f64);
|
||||||
|
|
||||||
|
impl Range {
|
||||||
|
pub fn key_points(&self, max_points: usize) -> Vec<f64> {
|
||||||
|
if max_points == 0 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = (self.0.min(self.1) as f64, self.1.max(self.0) as f64);
|
||||||
|
assert!(!(range.0.is_nan() || range.1.is_nan()));
|
||||||
|
if (range.0 - range.1).abs() < std::f64::EPSILON {
|
||||||
|
return vec![range.0 as f64];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
|
||||||
|
// The value granularity controls how we round the values.
|
||||||
|
// To avoid generating key points like 1.00000000001, we round to the nearest multiple of the
|
||||||
|
// value granularity.
|
||||||
|
// By default, we make the granularity as the 1/10 of the scale.
|
||||||
|
let mut value_granularity = scale / 10.0;
|
||||||
|
fn rem_euclid(a: f64, b: f64) -> f64 {
|
||||||
|
let ret = if b > 0.0 {
|
||||||
|
a - (a / b).floor() * b
|
||||||
|
} else {
|
||||||
|
a - (a / b).ceil() * b
|
||||||
|
};
|
||||||
|
if (ret - b).abs() < std::f64::EPSILON {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we need to make sure that the loop invariant:
|
||||||
|
// The scale must yield number of points than requested
|
||||||
|
if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
|
||||||
|
scale *= 10.0;
|
||||||
|
value_granularity *= 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
'outer: loop {
|
||||||
|
let old_scale = scale;
|
||||||
|
for nxt in [2.0, 5.0, 10.0].iter() {
|
||||||
|
let mut new_left = range.0 - rem_euclid(range.0, old_scale / nxt);
|
||||||
|
if new_left < range.0 {
|
||||||
|
new_left += old_scale / nxt;
|
||||||
|
}
|
||||||
|
let new_right = range.1 - rem_euclid(range.1, old_scale / nxt);
|
||||||
|
|
||||||
|
let npoints = 1.0 + ((new_right - new_left) / old_scale * nxt);
|
||||||
|
|
||||||
|
if npoints.round() as usize > max_points {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
scale = old_scale / nxt;
|
||||||
|
}
|
||||||
|
scale = old_scale / 10.0;
|
||||||
|
value_granularity /= 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = vec![];
|
||||||
|
// In some extreme cases, left might be too big, so that (left + scale) - left == 0 due to
|
||||||
|
// floating point error.
|
||||||
|
// In this case, we may loop forever. To avoid this, we need to use two variables to store
|
||||||
|
// the current left value. So we need keep a left_base and a left_relative.
|
||||||
|
let left = {
|
||||||
|
let mut value = range.0 - rem_euclid(range.0, scale);
|
||||||
|
if value < range.0 {
|
||||||
|
value += scale;
|
||||||
|
}
|
||||||
|
value
|
||||||
|
};
|
||||||
|
let left_base = (left / value_granularity).floor() * value_granularity;
|
||||||
|
let mut left_relative = left - left_base;
|
||||||
|
let right = range.1 - rem_euclid(range.1, scale);
|
||||||
|
while (right - left_relative - left_base) >= -std::f64::EPSILON {
|
||||||
|
let new_left_relative = (left_relative / value_granularity).round() * value_granularity;
|
||||||
|
if new_left_relative < 0.0 {
|
||||||
|
left_relative += value_granularity;
|
||||||
|
}
|
||||||
|
ret.push((left_relative + left_base) as f64);
|
||||||
|
left_relative += scale;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum CoordError {
|
pub enum CoordError {
|
||||||
#[error("")]
|
#[error("")]
|
||||||
|
|||||||
35
src/render/background/imp.rs
Normal file
35
src/render/background/imp.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use crate::render::{imp, WindowCoord};
|
||||||
|
use femtovg::Paint;
|
||||||
|
use geo_macros::Prj;
|
||||||
|
use gtk::glib;
|
||||||
|
use gtk::subclass::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Prj, Default)]
|
||||||
|
pub struct BackgroundConfig {
|
||||||
|
pub show_lat_lines: bool,
|
||||||
|
pub show_lon_lines: bool,
|
||||||
|
pub lat_lines: Vec<Vec<WindowCoord>>,
|
||||||
|
pub lon_lines: Vec<Vec<WindowCoord>>,
|
||||||
|
pub painter: Paint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackgroundConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BackgroundWidget {
|
||||||
|
pub(super) config: RefCell<BackgroundConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for BackgroundWidget {
|
||||||
|
const NAME: &'static str = "BackgroundWidget";
|
||||||
|
type Type = super::BackgroundWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for BackgroundWidget {}
|
||||||
46
src/render/background/mod.rs
Normal file
46
src/render/background/mod.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
mod imp;
|
||||||
|
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||||
|
use glib::subclass::types::ObjectSubclassIsExt;
|
||||||
|
use gtk::{ffi::gtk_widget_get_width, glib, graphene::Rect, prelude::SnapshotExtManual};
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
pub use self::imp::BackgroundConfig;
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct BackgroundWidget(ObjectSubclass<imp::BackgroundWidget>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BackgroundWidget {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(BackgroundConfig::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackgroundWidget {
|
||||||
|
pub fn new(config: BackgroundConfig) -> Self {
|
||||||
|
let this: Self = glib::Object::new();
|
||||||
|
let imp = this.imp();
|
||||||
|
imp.config.replace(config);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mesh_lines(&self, canvas: &mut Canvas<OpenGl>) {
|
||||||
|
let imp = self.imp();
|
||||||
|
let line_painter = &imp.config.borrow().painter;
|
||||||
|
if imp.config.borrow().show_lat_lines {
|
||||||
|
for lat_line in imp.config.borrow().lat_lines.iter() {
|
||||||
|
let mut path = Path::new();
|
||||||
|
lat_line.iter().for_each(|v| {
|
||||||
|
let (x, y) = *v;
|
||||||
|
path.move_to(x, y);
|
||||||
|
});
|
||||||
|
canvas.stroke_path(&mut path, line_painter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if imp.config.borrow().show_lon_lines {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&self, canvas: &mut Canvas<OpenGl>) {
|
||||||
|
let imp = self.imp();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,30 +1,111 @@
|
|||||||
|
use super::background::BackgroundWidget;
|
||||||
use glib::{ObjectExt, ParamSpec, Properties, Value};
|
use glib::{ObjectExt, ParamSpec, Properties, Value};
|
||||||
use gtk::glib;
|
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
|
use gtk::traits::{GLAreaExt, WidgetExt};
|
||||||
|
use gtk::{glib, prelude::WidgetExtManual};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::f32::consts::PI;
|
||||||
use crate::data::Radar2d;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Render {
|
pub struct Render {
|
||||||
data: RefCell<Option<Radar2d<i8>>>,
|
// pub(super) background: RefCell<BackgroundWidget>,
|
||||||
|
canvas: RefCell<Option<femtovg::Canvas<femtovg::renderer::OpenGl>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for Render {
|
impl ObjectSubclass for Render {
|
||||||
const NAME: &'static str = "MyRender";
|
const NAME: &'static str = "MyRender";
|
||||||
type Type = super::Render;
|
type Type = super::Render;
|
||||||
type ParentType = gtk::Widget;
|
type ParentType = gtk::GLArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trait shared by all GObjects
|
// Trait shared by all GObjects
|
||||||
impl ObjectImpl for Render {}
|
impl ObjectImpl for Render {
|
||||||
|
fn constructed(&self) {
|
||||||
|
self.parent_constructed();
|
||||||
|
let area = self.obj();
|
||||||
|
area.set_has_stencil_buffer(true);
|
||||||
|
area.add_tick_callback(|area, _| {
|
||||||
|
area.queue_render();
|
||||||
|
glib::Continue(true)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Trait shared by all widgets
|
// Trait shared by all widgets
|
||||||
impl WidgetImpl for Render {
|
impl WidgetImpl for Render {}
|
||||||
fn snapshot(&self, snapshot: >k::Snapshot) {
|
|
||||||
if let Some(data) = self.data.borrow_mut().take() {}
|
impl GLAreaImpl for Render {
|
||||||
|
fn resize(&self, width: i32, height: i32) {
|
||||||
|
self.ensure_canvas();
|
||||||
|
let mut canvas = self.canvas.borrow_mut();
|
||||||
|
let canvas = canvas.as_mut().unwrap();
|
||||||
|
canvas.set_size(
|
||||||
|
width as u32,
|
||||||
|
height as u32,
|
||||||
|
self.obj().scale_factor() as f32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&self, context: >k::gdk::GLContext) -> bool {
|
||||||
|
use femtovg::{Color, Paint, Path};
|
||||||
|
|
||||||
|
self.ensure_canvas();
|
||||||
|
let mut canvas = self.canvas.borrow_mut();
|
||||||
|
let canvas = canvas.as_mut().unwrap();
|
||||||
|
|
||||||
|
let dpi = self.obj().scale_factor();
|
||||||
|
let w = self.obj().width();
|
||||||
|
let h = self.obj().width();
|
||||||
|
|
||||||
|
canvas.clear_rect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
(w * dpi) as u32,
|
||||||
|
(h * dpi) as u32,
|
||||||
|
Color::rgba(0, 0, 0, 255),
|
||||||
|
);
|
||||||
|
canvas.fill_text(
|
||||||
|
0.,
|
||||||
|
0.,
|
||||||
|
"hello",
|
||||||
|
&Paint::color(Color::rgba(255, 255, 255, 0)),
|
||||||
|
);
|
||||||
|
|
||||||
|
canvas.flush();
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawingAreaImpl for Render {}
|
impl Render {
|
||||||
|
fn ensure_canvas(&self) {
|
||||||
|
use femtovg::{renderer, Canvas};
|
||||||
|
use glow::HasContext;
|
||||||
|
|
||||||
|
if self.canvas.borrow().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let widget = self.obj();
|
||||||
|
widget.attach_buffers();
|
||||||
|
|
||||||
|
static LOAD_FN: fn(&str) -> *const std::ffi::c_void =
|
||||||
|
|s| epoxy::get_proc_addr(s) as *const _;
|
||||||
|
// SAFETY: Need to get the framebuffer id that gtk expects us to draw into, so femtovg
|
||||||
|
// knows which framebuffer to bind. This is safe as long as we call attach_buffers
|
||||||
|
// beforehand. Also unbind it here just in case, since this can be called outside render.
|
||||||
|
let (mut renderer, fbo) = unsafe {
|
||||||
|
let renderer =
|
||||||
|
renderer::OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
|
||||||
|
let ctx = glow::Context::from_loader_function(LOAD_FN);
|
||||||
|
let id = NonZeroU32::new(ctx.get_parameter_i32(glow::DRAW_FRAMEBUFFER_BINDING) as u32)
|
||||||
|
.expect("No GTK provided framebuffer binding");
|
||||||
|
ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
|
||||||
|
(renderer, glow::NativeFramebuffer(id))
|
||||||
|
};
|
||||||
|
renderer.set_screen_target(Some(fbo));
|
||||||
|
let canvas = Canvas::new(renderer).expect("Cannot create canvas");
|
||||||
|
self.canvas.replace(Some(canvas));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,20 @@
|
|||||||
|
mod background;
|
||||||
mod imp;
|
mod imp;
|
||||||
use glib::Object;
|
pub use self::background::{BackgroundConfig, BackgroundWidget};
|
||||||
use gtk::glib;
|
use glib::subclass::prelude::*;
|
||||||
|
use gtk::{traits::GestureSingleExt, EventControllerScrollFlags};
|
||||||
|
|
||||||
|
pub(super) type WindowCoord = (f32, f32);
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct Render(ObjectSubclass<imp::Render>) @extends gtk::Widget;
|
pub struct Render(ObjectSubclass<imp::Render>)
|
||||||
|
@extends gtk::GLArea, gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Render {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render {
|
impl Render {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/org/cinrad_g/">
|
<gresource prefix="/org/cinrad_g/">
|
||||||
<file compressed="true" preprocess="xml-stripblanks">window.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">monitor.ui</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<interface>
|
|
||||||
<template class="MyGtkAppWindow" parent="GtkApplicationWindow">
|
|
||||||
<property name="title">My GTK App</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="button">
|
|
||||||
<property name="label">Press me!</property>
|
|
||||||
<property name="margin-top">12</property>
|
|
||||||
<property name="margin-bottom">12</property>
|
|
||||||
<property name="margin-start">12</property>
|
|
||||||
<property name="margin-end">12</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</template>
|
|
||||||
</interface>
|
|
||||||
Loading…
Reference in New Issue
Block a user