use crate::event_queue::EventQueue;
use crate::guest_call::SharedGuestCallStack;
use crate::menu_manager::{ProcessMenuTrackingState, SharedNativeMenuSelection};
#[derive(Debug, Default)]
pub(crate) struct ProcessContext {
event_queue: EventQueue,
menu_tracking: Option<ProcessMenuTrackingState>,
pending_native_menu_selection: SharedNativeMenuSelection,
guest_calls: SharedGuestCallStack,
}
impl ProcessContext {
pub(crate) fn event_queue(&self) -> &EventQueue {
&self.event_queue
}
pub(crate) fn event_queue_mut(&mut self) -> &mut EventQueue {
&mut self.event_queue
}
pub(crate) fn menu_tracking(&self) -> Option<&ProcessMenuTrackingState> {
self.menu_tracking.as_ref()
}
#[cfg(test)]
pub(crate) fn menu_tracking_mut(&mut self) -> Option<&mut ProcessMenuTrackingState> {
self.menu_tracking.as_mut()
}
#[cfg(test)]
pub(crate) fn take_menu_tracking(&mut self) -> Option<ProcessMenuTrackingState> {
self.menu_tracking.take()
}
#[cfg(test)]
pub(crate) fn set_menu_tracking(&mut self, state: Option<ProcessMenuTrackingState>) {
self.menu_tracking = state;
}
#[cfg(test)]
pub(crate) fn menu_tracking_slot_mut(&mut self) -> &mut Option<ProcessMenuTrackingState> {
&mut self.menu_tracking
}
pub(crate) fn adopt_menu_tracking(&mut self, adapter: &mut Option<ProcessMenuTrackingState>) {
assert!(
adapter.is_none() || self.menu_tracking.is_none(),
"cannot attach two active Menu Manager continuations"
);
if self.menu_tracking.is_none() {
self.menu_tracking = adapter.take();
}
}
pub(crate) fn event_queue_and_menu_tracking_mut(
&mut self,
) -> (&mut EventQueue, &mut Option<ProcessMenuTrackingState>) {
(&mut self.event_queue, &mut self.menu_tracking)
}
pub(crate) fn attach_native_menu_selection(&self, adapter: &mut SharedNativeMenuSelection) {
adapter.attach_to(&self.pending_native_menu_selection);
}
pub(crate) fn attach_guest_calls(&self, adapter: &mut SharedGuestCallStack) {
adapter.attach_to(&self.guest_calls);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event_queue::QueuedEvent;
use crate::guest_call::GuestCallTarget;
use crate::guest_procedure::GuestIsa;
#[test]
fn process_context_owns_canonical_event_queue() {
let mut context = ProcessContext::default();
assert!(context.event_queue().is_empty());
context.event_queue_mut().push_back(QueuedEvent {
what: 1,
message: 0x1234,
where_v: 10,
where_h: 20,
modifiers: 0,
});
assert_eq!(context.event_queue().len(), 1);
assert_eq!(context.event_queue().front().unwrap().message, 0x1234);
}
#[test]
fn process_context_owns_canonical_menu_tracking() {
let mut context = ProcessContext::default();
assert!(context.menu_tracking().is_none());
let tracking = crate::menu_manager::test_process_menu_tracking(0x0012_3456);
context.set_menu_tracking(Some(tracking));
assert_eq!(
context.menu_tracking().map(|t| t.menu_handle),
Some(0x0012_3456)
);
if let Some(t) = context.menu_tracking_mut() {
t.highlighted_item = 3;
}
assert_eq!(
context
.menu_tracking()
.map(|t| (t.menu_handle, t.highlighted_item)),
Some((0x0012_3456, 3))
);
let taken = context.take_menu_tracking();
assert_eq!(taken.map(|t| t.menu_handle), Some(0x0012_3456));
assert!(context.menu_tracking().is_none());
let (queue, menu) = context.event_queue_and_menu_tracking_mut();
queue.push_back(QueuedEvent {
what: 2,
message: 0x5678,
where_v: 0,
where_h: 0,
modifiers: 0,
});
*menu = Some(crate::menu_manager::test_process_menu_tracking(0x0065_4321));
assert_eq!(context.event_queue().len(), 1);
assert_eq!(
context.menu_tracking().map(|t| t.menu_handle),
Some(0x0065_4321)
);
}
#[test]
fn adapters_transfer_pending_state_and_share_one_process_owner() {
let context = ProcessContext::default();
let mut classic_selection = SharedNativeMenuSelection::default();
assert!(classic_selection.stage((128, 2)));
let mut native_selection = SharedNativeMenuSelection::default();
context.attach_native_menu_selection(&mut classic_selection);
context.attach_native_menu_selection(&mut native_selection);
assert_eq!(native_selection.take(), Some((128, 2)));
assert!(classic_selection.is_none());
let mut classic_calls = SharedGuestCallStack::default();
classic_calls.begin_m68k(
GuestCallTarget {
isa: GuestIsa::M68k,
entry: 0x1000,
rtoc: 0,
},
0x2000,
0x3000,
);
let mut native_calls = SharedGuestCallStack::default();
context.attach_guest_calls(&mut classic_calls);
context.attach_guest_calls(&mut native_calls);
assert_eq!(native_calls.len(), 1);
assert!(native_calls.complete_m68k(0x2002, 0x3000));
assert!(classic_calls.is_empty());
}
#[test]
#[should_panic(expected = "cannot attach two active Menu Manager continuations")]
fn adopting_two_active_menu_continuations_is_always_rejected() {
let mut context = ProcessContext::default();
context.set_menu_tracking(Some(crate::menu_manager::test_process_menu_tracking(
0x1000,
)));
let mut second = Some(crate::menu_manager::test_process_menu_tracking(0x2000));
context.adopt_menu_tracking(&mut second);
}
#[test]
#[should_panic(expected = "cannot attach two pending native menu selections")]
fn attaching_two_pending_native_selections_is_always_rejected() {
let context = ProcessContext::default();
let mut first = SharedNativeMenuSelection::default();
let mut second = SharedNativeMenuSelection::default();
first.stage((128, 1));
second.stage((129, 2));
context.attach_native_menu_selection(&mut first);
context.attach_native_menu_selection(&mut second);
}
#[test]
#[should_panic(expected = "cannot attach two active guest-procedure continuation stacks")]
fn attaching_two_active_guest_call_stacks_is_always_rejected() {
fn begin_call(calls: &SharedGuestCallStack, entry: u32) {
calls.begin_m68k(
GuestCallTarget {
isa: GuestIsa::M68k,
entry,
rtoc: 0,
},
entry + 2,
0x3000,
);
}
let context = ProcessContext::default();
let mut first = SharedGuestCallStack::default();
let mut second = SharedGuestCallStack::default();
begin_call(&first, 0x1000);
begin_call(&second, 0x2000);
context.attach_guest_calls(&mut first);
context.attach_guest_calls(&mut second);
}
}