Skip to content

Commit eaf001b

Browse files
authored
Merge pull request #53 from rust-native-ui/fix-unsound-callbacks
Finish up callback unsoundness fixes
2 parents d5dd45b + e2a79fd commit eaf001b

26 files changed

+768
-365
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
2323
* `LayoutGrid::insert_at` no longer takes `left` and `height` arguments
2424
* Many APIs which took `u64` or `i64` arguments now take `i32` for wider compatibility
2525
* The semi-unstable `iui::draw` subsystem is again exported to downstream consumers of the `iui` crate.
26+
* `UI::queue_main` and `UI::on_should_quit` now require passed closures to be `'static`, for soundness
27+
* All callback registration functions require that their callbacks live at least as long as the `UI` token, for soundness
2628

2729
### Deprecated
2830

CONTRIBUTING.md renamed to CONTRIBUTING/CONTRIBUTING.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ from slowing down your PR.
3434
* Mark unsafe functions as `unsafe`. This includes _anything_ with potential
3535
undefined behavior, not just memory unsafety.
3636
* Document your code! This is the number one holdup for pull requests, especially
37-
large ones. Don't forget to insert appropriate entries into the changelog.
37+
large ones. This includes adding new concepts to the CONTRIBUTING directory (developer
38+
documentation).
39+
* Insert appropriate entries into the changelog.
3840
* If implementing a new feature from `ui`, please mention the stability of that
3941
feature in your pull request. We are fine with implementing unstable APIs from
4042
`ui`, but it's important to mark such APIs as unstable.

CONTRIBUTING/callbacks.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Callbacks with libui
2+
3+
libui provides a mechanism through which each callback can be passed arbitrary data. (An
4+
untyped buffer.)
5+
IUI uses this mechanism to provide a C "wrapper" function which converts any raw libui
6+
types into IUI Rust types, and does sanity checking.
7+
8+
We do this with the functions `callback_helpers::to_heap_ptr`, which `Box`es up a Rust
9+
type and returns a pointer to the allocated memory, and `callback_helpers::from_void_ptr`,
10+
which reconstitutes the Rust type (specified using generics). These functions are `unsafe`
11+
for obvious reasons, and can also leak memory if used improperly.
12+
13+
Their intended use is for `to_heap_ptr` to turn a Rust function into a bag of bits and for
14+
`from_void_ptr` to reconstitute that function into the _exact same type_, which we ensure
15+
by "generic locking" the user and wrapper functions, like so:
16+
17+
```rust
18+
fn on_whatever<'ctx, F: FnMut(&Whatever) + 'static>(&mut self, _ctx: &'ctx UI, callback: F) {
19+
20+
fn c_callback<G: FnMut(&Whatever)> { /* ... do stuff ... */ }
21+
22+
ui_sys::uiWhateverOnWhatever(/* ... */, c_callback::<F>);
23+
}
24+
```
25+
26+
This is somewhat verbose but ensures that the types do not deviate, which would be unsafe.
27+
28+
Callbacks should be named `on_event` where `event` is, for instance, `clicked` or
29+
`closing`. The functions taken by callbacks must always have the `'static` bound.
30+

iui/examples/basic.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
extern crate iui;
2+
use iui::controls::{Button, Group, Label, VerticalBox};
23
use iui::prelude::*;
3-
use iui::controls::{Label, Button, VerticalBox, Group};
44

55
fn main() {
66
// Initialize the UI library
77
let ui = UI::init().expect("Couldn't initialize UI library");
88
// Create a window into which controls can be placed
99
let mut win = Window::new(&ui, "Test App", 200, 200, WindowType::NoMenubar);
10-
10+
1111
// Create a vertical layout to hold the controls
1212
let mut vbox = VerticalBox::new(&ui);
1313
vbox.set_padded(&ui, true);

iui/examples/canvas.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
extern crate iui;
22
extern crate ui_sys;
33

4-
use iui::controls::{
5-
Area, AreaDrawParams, AreaHandler, HorizontalBox, LayoutStrategy,
6-
};
7-
use iui::draw::{Brush, Path, SolidBrush, FillMode};
4+
use iui::controls::{Area, AreaDrawParams, AreaHandler, HorizontalBox, LayoutStrategy};
5+
use iui::draw::{Brush, FillMode, Path, SolidBrush};
86
use iui::prelude::*;
97
use std::f64::consts::PI;
108

iui/examples/files.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
//! and the Window::modal_err() call to display modal dialog boxes.
33
44
extern crate iui;
5+
use iui::controls::{Button, MultilineEntry, VerticalBox};
56
use iui::prelude::*;
6-
use iui::controls::{VerticalBox, MultilineEntry, Button};
7-
use std::io::prelude::*;
87
use std::error::Error;
98
use std::fs::File;
9+
use std::io::prelude::*;
1010

1111
fn main() {
1212
// Initialize the UI
@@ -32,17 +32,38 @@ fn main() {
3232
move |_| {
3333
if let Some(path) = window.save_file(&ui) {
3434
let mut file = match File::create(&path) {
35-
Err(why) => { window.modal_err(&ui, "I/O Error", &format!("Could not open file {}: {}", path.display(), why.description())); return; }
36-
Ok(f) => f
35+
Err(why) => {
36+
window.modal_err(
37+
&ui,
38+
"I/O Error",
39+
&format!(
40+
"Could not open file {}: {}",
41+
path.display(),
42+
why.description()
43+
),
44+
);
45+
return;
46+
}
47+
Ok(f) => f,
3748
};
3849
match file.write_all(entry.value(&ui).as_bytes()) {
39-
Err(why) => { window.modal_err(&ui, "I/O Error", &format!("Could not write to file {}: {}", path.display(), why.description())); return; }
40-
Ok(_) => ()
50+
Err(why) => {
51+
window.modal_err(
52+
&ui,
53+
"I/O Error",
54+
&format!(
55+
"Could not write to file {}: {}",
56+
path.display(),
57+
why.description()
58+
),
59+
);
60+
return;
61+
}
62+
Ok(_) => (),
4163
};
42-
}
64+
}
4365
}
4466
});
4567

4668
ui.main();
47-
4869
}

0 commit comments

Comments
 (0)