-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathlib.rs
188 lines (168 loc) · 4.81 KB
/
lib.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
use std::ffi::c_char;
use std::sync::Arc;
use graphics::compositor;
use iced::{window, Element, Renderer, Settings, Subscription, Task, Theme};
use iced_widget::graphics;
use iced_winit::Program;
mod advanced;
mod alignment;
mod color;
mod core;
mod ffi;
mod future;
mod keyboard;
mod length;
mod line_height;
mod padding;
mod settings;
mod subscription;
mod task;
mod theme;
mod time;
mod widget;
use core::ElementPtr;
use ffi::{from_raw, into_raw, read_c_string};
use subscription::SubscriptionFn;
use task::UpdateResult;
use theme::{theme_from_raw, ThemeFn};
type Model = *const u8;
type Message = *const u8;
type Title = extern "C" fn(model: Model) -> *mut c_char;
type Update = extern "C" fn(model: Model, message: Message) -> *mut UpdateResult;
type View = extern "C" fn(model: Model) -> ElementPtr;
extern "C" {
// Part of HsFFI.h
#[link_name = "hs_free_fun_ptr"]
fn free_haskell_fun_ptr(ptr: usize);
// Part of HsFFI.h
#[link_name = "hs_free_stable_ptr"]
fn free_stable_ptr(ptr: *const u8);
}
#[derive(Debug, Clone)]
struct HaskellMessage {
ptr: *const u8,
}
unsafe impl Send for HaskellMessage {}
unsafe impl Sync for HaskellMessage {}
impl Drop for HaskellMessage {
fn drop(&mut self) {
unsafe { free_stable_ptr(self.ptr) }
}
}
#[derive(Clone, Debug)]
enum IcedMessage {
Ptr(Arc<HaskellMessage>),
None,
}
impl IcedMessage {
// StablePtr to Haskell message
fn ptr(ptr: *const u8) -> Self {
let message = HaskellMessage { ptr };
IcedMessage::Ptr(Arc::new(message))
}
}
struct App {
title_hs: Title,
model: Model,
update_hs: Update,
view_hs: View,
subscription_hs: Option<SubscriptionFn>,
theme_hs: Option<ThemeFn>,
}
impl Program for App {
type Executor = iced::executor::Default;
type Message = IcedMessage;
type Theme = Theme;
type Renderer = Renderer;
type Flags = Self;
fn new(app: Self) -> (App, Task<IcedMessage>) {
(app, Task::none())
}
fn title(&self, _window: window::Id) -> String {
let pointer = (self.title_hs)(self.model);
read_c_string(pointer)
}
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
match message {
IcedMessage::Ptr(message) => self.process_update(message),
IcedMessage::None => Task::none(),
}
}
fn view(&self, _window: window::Id) -> Element<Self::Message, Self::Theme, Self::Renderer> {
unsafe { *Box::from_raw((self.view_hs)(self.model)) }
}
fn theme(&self, _window: window::Id) -> Theme {
match self.theme_hs {
Some(theme_fn) => {
let value = theme_fn(self.model);
theme_from_raw(value)
}
None => Self::Theme::default(),
}
}
fn subscription(&self) -> Subscription<IcedMessage> {
match self.subscription_hs {
Some(subscription) => {
let pointer = subscription(self.model);
unsafe { *Box::from_raw(pointer) }
}
None => Subscription::none(),
}
}
}
impl App {
fn process_update(&mut self, message: Arc<HaskellMessage>) -> Task<IcedMessage> {
let result_ptr = (self.update_hs)(self.model, message.ptr);
let result = from_raw(result_ptr);
// when pointer changes, free the old one
// in fact, it almost always changes
if self.model != result.model {
let old_ptr = self.model;
self.model = result.model;
unsafe { free_stable_ptr(old_ptr) }
}
result.task.perform()
}
}
#[no_mangle]
extern "C" fn app_new(title_hs: Title, model: Model, update_hs: Update, view_hs: View) -> *mut App {
let app = App {
title_hs,
model,
update_hs,
view_hs,
subscription_hs: None,
theme_hs: None,
};
into_raw(app)
}
#[no_mangle]
extern "C" fn app_set_subscription(app: &mut App, subscription: SubscriptionFn) {
app.subscription_hs = Some(subscription);
}
#[no_mangle]
extern "C" fn app_set_theme(app: &mut App, theme: ThemeFn) {
app.theme_hs = Some(theme);
}
#[no_mangle]
extern "C" fn app_run(app_ptr: *mut App, settings_ptr: *mut Settings) {
let app = from_raw(app_ptr);
let settings = from_raw(settings_ptr);
let renderer_settings = graphics::Settings {
default_font: settings.default_font,
default_text_size: settings.default_text_size,
antialiasing: if settings.antialiasing {
Some(graphics::Antialiasing::MSAAx4)
} else {
None
},
};
let window_settings = Some(window::Settings::default());
iced_winit::program::run::<App, <Renderer as compositor::Default>::Compositor>(
settings.into(),
renderer_settings,
window_settings,
app,
)
.expect("Should run the app")
}