Skip to content

Commit

Permalink
Implement an Event System
Browse files Browse the repository at this point in the history
This adds an event system. The events usually come from the hotkey
system, an auto splitter, the UI, or through a network connection. The
UI usually provides the implementation for the so called event sink,
forwarding all the events to the actual timer. It is able to intercept
the events and for example ask the user for confirmation before applying
them. Other handling is possible such as automatically saving the splits
or notifying a server about changes happening in the run.
  • Loading branch information
CryZe committed May 28, 2024
1 parent 99fbe17 commit 74e26c9
Show file tree
Hide file tree
Showing 19 changed files with 676 additions and 93 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ bytemuck = { version = "1.9.1", default-features = false }
bytemuck_derive = { version = "1.4.1", default_features = false }
cfg-if = "1.0.0"
itoa = { version = "1.0.3", default-features = false }
time = { version = "0.3.3", default-features = false }
time = { version = "0.3.36", default-features = false }
hashbrown = "0.14.0"
libm = "0.2.1"
livesplit-hotkey = { path = "crates/livesplit-hotkey", version = "0.7.0", default-features = false }
Expand Down
4 changes: 2 additions & 2 deletions capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ web-sys = { version = "0.3.28", optional = true }
default = ["image-shrinking"]
image-shrinking = ["livesplit-core/image-shrinking"]
software-rendering = ["livesplit-core/software-rendering"]
wasm-web = ["livesplit-core/wasm-web"]
wasm-web = ["livesplit-core/wasm-web", "wasm-bindgen", "web-sys"]
auto-splitting = ["livesplit-core/auto-splitting"]
assume-str-parameters-are-utf8 = []
web-rendering = ["wasm-web", "livesplit-core/web-rendering", "wasm-bindgen", "web-sys"]
web-rendering = ["wasm-web", "livesplit-core/web-rendering"]
2 changes: 1 addition & 1 deletion capi/src/auto_splitting_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::shared_timer::OwnedSharedTimer;
use std::{os::raw::c_char, path::PathBuf};

#[cfg(feature = "auto-splitting")]
use livesplit_core::auto_splitting::Runtime as AutoSplittingRuntime;
type AutoSplittingRuntime = livesplit_core::auto_splitting::Runtime<livesplit_core::SharedTimer>;

#[cfg(not(feature = "auto-splitting"))]
use livesplit_core::SharedTimer;
Expand Down
10 changes: 4 additions & 6 deletions capi/src/delta_component.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
//! The Delta Component is a component that shows the how far ahead or behind
//! the current attempt is compared to the chosen comparison.
//! The Delta Component is a component that shows how far ahead or behind the
//! current attempt is compared to the chosen comparison.

use super::{output_vec, Json};
use crate::component::OwnedComponent;
use crate::key_value_component_state::OwnedKeyValueComponentState;
use livesplit_core::component::delta::Component as DeltaComponent;
use livesplit_core::{GeneralLayoutSettings, Timer};
use crate::{component::OwnedComponent, key_value_component_state::OwnedKeyValueComponentState};
use livesplit_core::{component::delta::Component as DeltaComponent, GeneralLayoutSettings, Timer};

/// type
pub type OwnedDeltaComponent = Box<DeltaComponent>;
Expand Down
110 changes: 110 additions & 0 deletions capi/src/event_sink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! An event sink accepts events that are meant to be passed to the timer. The
//! events usually come from the hotkey system, an auto splitter, the UI, or
//! through a network connection. The UI usually provides the implementation for
//! this, forwarding all the events to the actual timer. It is able to intercept
//! the events and for example ask the user for confirmation before applying
//! them. Other handling is possible such as automatically saving the splits or
//! notifying a server about changes happening in the run.

use std::sync::Arc;

use crate::shared_timer::OwnedSharedTimer;

/// type
#[derive(Clone)]
pub struct EventSink(pub(crate) Arc<dyn EventSinkAndQuery>);

/// type
pub type OwnedEventSink = Box<EventSink>;

/// Creates a new Event Sink.
#[no_mangle]
pub extern "C" fn EventSink_from_timer(timer: OwnedSharedTimer) -> OwnedEventSink {
Box::new(EventSink(Arc::new(*timer)))
}

/// drop
#[no_mangle]
pub extern "C" fn EventSink_drop(this: OwnedEventSink) {
drop(this);
}

pub(crate) trait EventSinkAndQuery:
livesplit_core::event::Sink + livesplit_core::event::TimerQuery + Send + Sync + 'static
{
}

impl<T> EventSinkAndQuery for T where
T: livesplit_core::event::Sink + livesplit_core::event::TimerQuery + Send + Sync + 'static
{
}

impl livesplit_core::event::Sink for EventSink {
fn start(&self) {
self.0.start()
}

fn split(&self) {
self.0.split()
}

fn split_or_start(&self) {
self.0.split_or_start()
}

fn reset(&self, save_attempt: Option<bool>) {
self.0.reset(save_attempt)
}

fn undo_split(&self) {
self.0.undo_split()
}

fn skip_split(&self) {
self.0.skip_split()
}

fn toggle_pause_or_start(&self) {
self.0.toggle_pause_or_start()
}

fn pause(&self) {
self.0.pause()
}

fn resume(&self) {
self.0.resume()
}

fn undo_all_pauses(&self) {
self.0.undo_all_pauses()
}

fn switch_to_previous_comparison(&self) {
self.0.switch_to_previous_comparison()
}

fn switch_to_next_comparison(&self) {
self.0.switch_to_next_comparison()
}

fn toggle_timing_method(&self) {
self.0.toggle_timing_method()
}

fn set_game_time(&self, time: livesplit_core::TimeSpan) {
self.0.set_game_time(time)
}

fn pause_game_time(&self) {
self.0.pause_game_time()
}

fn resume_game_time(&self) {
self.0.resume_game_time()
}

fn set_custom_variable(&self, name: &str, value: &str) {
self.0.set_custom_variable(name, value)
}
}
14 changes: 8 additions & 6 deletions capi/src/hotkey_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

use std::{os::raw::c_char, str::FromStr};

use crate::{hotkey_config::OwnedHotkeyConfig, output_str, shared_timer::OwnedSharedTimer, str};
use livesplit_core::{hotkey::KeyCode, HotkeySystem};
use crate::{event_sink::EventSink, hotkey_config::OwnedHotkeyConfig, output_str, str};
use livesplit_core::hotkey::KeyCode;

type HotkeySystem = livesplit_core::HotkeySystem<EventSink>;

/// type
pub type OwnedHotkeySystem = Box<HotkeySystem>;
Expand All @@ -16,18 +18,18 @@ pub type NullableOwnedHotkeySystem = Option<OwnedHotkeySystem>;

/// Creates a new Hotkey System for a Timer with the default hotkeys.
#[no_mangle]
pub extern "C" fn HotkeySystem_new(shared_timer: OwnedSharedTimer) -> NullableOwnedHotkeySystem {
HotkeySystem::new(*shared_timer).ok().map(Box::new)
pub extern "C" fn HotkeySystem_new(event_sink: &EventSink) -> NullableOwnedHotkeySystem {
HotkeySystem::new(event_sink.clone()).ok().map(Box::new)
}

/// Creates a new Hotkey System for a Timer with a custom configuration for the
/// hotkeys.
#[no_mangle]
pub extern "C" fn HotkeySystem_with_config(
shared_timer: OwnedSharedTimer,
event_sink: &EventSink,
config: OwnedHotkeyConfig,
) -> NullableOwnedHotkeySystem {
HotkeySystem::with_config(*shared_timer, *config)
HotkeySystem::with_config(event_sink.clone(), *config)
.ok()
.map(Box::new)
}
Expand Down
3 changes: 3 additions & 0 deletions capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod current_pace_component;
pub mod delta_component;
pub mod detailed_timer_component;
pub mod detailed_timer_component_state;
pub mod event_sink;
pub mod fuzzy_list;
pub mod general_layout_settings;
pub mod graph_component;
Expand Down Expand Up @@ -83,6 +84,8 @@ pub mod timer_write_lock;
pub mod title_component;
pub mod title_component_state;
pub mod total_playtime_component;
#[cfg(all(target_family = "wasm", feature = "wasm-web"))]
pub mod web_event_sink;
#[cfg(all(target_family = "wasm", feature = "web-rendering"))]
pub mod web_rendering;

Expand Down
6 changes: 6 additions & 0 deletions capi/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ pub extern "C" fn Run_segment(this: &Run, index: usize) -> &Segment {
this.segment(index)
}

/// Returns the amount of segments in this Run.
#[no_mangle]
pub extern "C" fn Run_segments_len(this: &Run) -> usize {
this.segments().len()
}

/// Returns the amount attempt history elements are stored in this Run.
#[no_mangle]
pub extern "C" fn Run_attempt_history_len(this: &Run) -> usize {
Expand Down
2 changes: 1 addition & 1 deletion capi/src/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub type OwnedSegment = Box<Segment>;

/// Creates a new Segment with the name given.
#[no_mangle]
pub unsafe extern "C" fn Segment_new(name: &c_char) -> OwnedSegment {
pub unsafe extern "C" fn Segment_new(name: *const c_char) -> OwnedSegment {
Box::new(Segment::new(str(name)))
}

Expand Down
20 changes: 19 additions & 1 deletion capi/src/timer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! A Timer provides all the capabilities necessary for doing speedrun attempts.

use super::{output_str, output_time, output_time_span, output_vec};
use super::{output_str, output_time, output_time_span, output_vec, str};
use crate::{
run::{NullableOwnedRun, OwnedRun},
shared_timer::OwnedSharedTimer,
Expand Down Expand Up @@ -204,6 +204,12 @@ pub extern "C" fn Timer_set_current_timing_method(this: &mut Timer, method: Timi
this.set_current_timing_method(method);
}

/// Toggles between the Real Time and Game Time timing methods.
#[no_mangle]
pub extern "C" fn Timer_toggle_timing_method(this: &mut Timer) {
this.toggle_timing_method();
}

/// Returns the current comparison that is being compared against. This may
/// be a custom comparison or one of the Comparison Generators.
#[no_mangle]
Expand Down Expand Up @@ -287,6 +293,18 @@ pub extern "C" fn Timer_set_loading_times(this: &mut Timer, time: &TimeSpan) {
this.set_loading_times(*time);
}

/// Sets the value of a custom variable with the name specified. If the variable
/// does not exist, a temporary variable gets created that will not be stored in
/// the splits file.
#[no_mangle]
pub unsafe extern "C" fn Timer_set_custom_variable(
this: &mut Timer,
name: *const c_char,
value: *const c_char,
) {
this.set_custom_variable(str(name), str(value));
}

/// Returns the current Timer Phase.
#[no_mangle]
pub extern "C" fn Timer_current_phase(this: &Timer) -> TimerPhase {
Expand Down
Loading

0 comments on commit 74e26c9

Please sign in to comment.