@@ -14,7 +14,10 @@ use cfg_if::cfg_if;
14
14
use config:: Config ;
15
15
use crossbeam:: channel:: Receiver ;
16
16
use egui:: ahash:: HashMap ;
17
- use std:: sync:: Arc ;
17
+ use std:: sync:: {
18
+ Arc ,
19
+ atomic:: { AtomicBool , Ordering } ,
20
+ } ;
18
21
use tetanes_core:: { time:: Instant , video:: Frame } ;
19
22
use thingbuf:: mpsc:: blocking;
20
23
use winit:: {
@@ -46,25 +49,31 @@ pub struct Nes {
46
49
pub ( crate ) state : State ,
47
50
}
48
51
49
- #[ derive( Debug , Default ) ]
52
+ #[ derive( Debug ) ]
50
53
#[ must_use]
51
54
pub ( crate ) enum State {
52
- #[ default]
53
- Suspended ,
55
+ Suspended {
56
+ should_terminate : Arc < AtomicBool > ,
57
+ } ,
54
58
Pending {
55
59
ctx : egui:: Context ,
56
60
window : Arc < Window > ,
57
61
painter_rx : Receiver < Painter > ,
62
+ should_terminate : Arc < AtomicBool > ,
58
63
} ,
59
64
Running ( Box < Running > ) ,
60
65
Exiting ,
61
66
}
62
67
63
- impl State {
64
- pub const fn is_suspended ( & self ) -> bool {
65
- matches ! ( self , Self :: Suspended )
68
+ impl Default for State {
69
+ fn default ( ) -> Self {
70
+ Self :: Suspended {
71
+ should_terminate : Default :: default ( ) ,
72
+ }
66
73
}
74
+ }
67
75
76
+ impl State {
68
77
pub const fn is_exiting ( & self ) -> bool {
69
78
matches ! ( self , Self :: Exiting )
70
79
}
@@ -97,8 +106,9 @@ impl RunState {
97
106
pub ( crate ) struct Running {
98
107
pub ( crate ) cfg : Config ,
99
108
// Only used by wasm currently
100
- #[ allow( unused) ]
109
+ #[ cfg_attr ( target_arch = "wasm32" , allow( unused) ) ]
101
110
pub ( crate ) tx : NesEventProxy ,
111
+ pub ( crate ) should_terminate : Arc < AtomicBool > ,
102
112
pub ( crate ) emulation : Emulation ,
103
113
pub ( crate ) renderer : Renderer ,
104
114
pub ( crate ) input_bindings : InputBindings ,
@@ -133,11 +143,33 @@ impl Nes {
133
143
Ok ( ( ) )
134
144
}
135
145
146
+ /// Return whether the application should terminate.
147
+ pub fn should_terminate ( & self ) -> bool {
148
+ match & self . state {
149
+ State :: Suspended { should_terminate }
150
+ | State :: Pending {
151
+ should_terminate, ..
152
+ } => should_terminate. load ( Ordering :: Relaxed ) ,
153
+ State :: Running ( running) => running. should_terminate . load ( Ordering :: Relaxed ) ,
154
+ State :: Exiting => true ,
155
+ }
156
+ }
157
+
136
158
/// Create the NES instance.
137
159
pub fn new ( cfg : Config , event_loop : & EventLoop < NesEvent > ) -> Self {
160
+ let should_terminate = Arc :: new ( AtomicBool :: new ( false ) ) ;
161
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
162
+ // Minor issue if this fails, but not enough to terminate the program
163
+ let _ = ctrlc:: set_handler ( {
164
+ let should_terminate = Arc :: clone ( & should_terminate) ;
165
+ move || {
166
+ should_terminate. store ( true , Ordering :: Relaxed ) ;
167
+ }
168
+ } ) ;
169
+
138
170
Self {
139
171
init_state : Some ( ( cfg, NesEventProxy :: new ( event_loop) ) ) ,
140
- state : State :: Suspended ,
172
+ state : State :: Suspended { should_terminate } ,
141
173
}
142
174
}
143
175
@@ -150,6 +182,7 @@ impl Nes {
150
182
pub ( crate ) fn request_renderer_resources (
151
183
& mut self ,
152
184
event_loop : & ActiveEventLoop ,
185
+ should_terminate : Arc < AtomicBool > ,
153
186
) -> anyhow:: Result < ( ) > {
154
187
let ( cfg, tx) = self
155
188
. init_state
@@ -162,6 +195,7 @@ impl Nes {
162
195
ctx,
163
196
window,
164
197
painter_rx,
198
+ should_terminate,
165
199
} ;
166
200
167
201
Ok ( ( ) )
@@ -180,6 +214,7 @@ impl Nes {
180
214
ctx,
181
215
window,
182
216
painter_rx,
217
+ should_terminate,
183
218
} => {
184
219
let resources = Resources {
185
220
ctx,
@@ -199,27 +234,10 @@ impl Nes {
199
234
let emulation = Emulation :: new ( tx. clone ( ) , frame_tx. clone ( ) , & cfg) ?;
200
235
let renderer = Renderer :: new ( event_loop, tx. clone ( ) , resources, frame_rx, & cfg) ?;
201
236
202
- // Minor issue if this fails, but not enough to terminate the program
203
- #[ cfg( not( target_arch = "wasm32" ) ) ]
204
- let _ = ctrlc:: set_handler ( {
205
- let tx = tx. clone ( ) ;
206
- move || {
207
- use std:: { process, thread, time:: Duration } ;
208
-
209
- tracing:: info!( "received ctrl-c. terminating..." ) ;
210
-
211
- // Give application time to clean up
212
- tx. event ( event:: UiEvent :: Terminate ) ;
213
- thread:: sleep ( Duration :: from_millis ( 200 ) ) ;
214
-
215
- tracing:: debug!( "forcing termination..." ) ;
216
- process:: exit ( 0 ) ;
217
- }
218
- } ) ;
219
-
220
237
let mut running = Running {
221
238
cfg,
222
239
tx,
240
+ should_terminate,
223
241
emulation,
224
242
renderer,
225
243
input_bindings,
@@ -239,7 +257,7 @@ impl Nes {
239
257
self . state = State :: Running ( running) ;
240
258
Ok ( ( ) )
241
259
}
242
- State :: Suspended | State :: Exiting => anyhow:: bail!( "not in pending state" ) ,
260
+ State :: Suspended { .. } | State :: Exiting => anyhow:: bail!( "not in pending state" ) ,
243
261
}
244
262
}
245
263
}
0 commit comments