diff --git a/.travis.yml b/.travis.yml index 42c8e681..b1c0957a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -94,11 +94,11 @@ before_cache: # Travis can't cache files that are not readable by "others" - chmod -R a+r $HOME/.cargo -branches: - only: - # release tags - - /^v\d+\.\d+\.\d+.*$/ - - master +# branches: +# only: +# # release tags +# - /^v\d+\.\d+\.\d+.*$/ +# - master notifications: email: diff --git a/Cargo.toml b/Cargo.toml index 3be76928..f4f1ba49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "livesplit-core" -version = "0.1.4" +version = "0.2.0" authors = ["Christopher Serr "] documentation = "https://docs.rs/livesplit-core/" repository = "https://github.com/CryZe/livesplit-core" @@ -27,3 +27,4 @@ image = "0.12.3" derive_more = "0.6.0" derive-new = "0.4.0" pdqsort = "0.1.2" +hotkey = { path = "hotkey" } diff --git a/appveyor.yml b/appveyor.yml index b638b9ca..dac864ba 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -73,11 +73,11 @@ cache: - C:\Users\appveyor\.cargo\registry - target -branches: - only: - # Release tags - - /^v\d+\.\d+\.\d+.*$/ - - master +# branches: +# only: +# # Release tags +# - /^v\d+\.\d+\.\d+.*$/ +# - master notifications: - provider: Email diff --git a/capi/Cargo.toml b/capi/Cargo.toml index 9f5448f0..ff23c4d8 100644 --- a/capi/Cargo.toml +++ b/capi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "livesplit-core-capi" -version = "0.1.4" +version = "0.2.0" authors = ["Christopher Serr "] [dependencies] diff --git a/capi/bind_gen/src/csharp.rs b/capi/bind_gen/src/csharp.rs index 3898bfd1..78588a91 100644 --- a/capi/bind_gen/src/csharp.rs +++ b/capi/bind_gen/src/csharp.rs @@ -4,7 +4,7 @@ use heck::{CamelCase, MixedCase}; fn get_type(ty: &Type, output: bool) -> &str { match (ty.kind, ty.name.as_str()) { - (TypeKind::Ref, "c_char") => if output { "LSCCoreString" } else { "string" }, + (TypeKind::Ref, "c_char") => if output { "LSCoreString" } else { "string" }, (TypeKind::Ref, _) | (TypeKind::RefMut, _) => "IntPtr", (_, t) if !ty.is_custom => { @@ -36,15 +36,12 @@ pub fn write(mut writer: W, functions: &[Function]) -> Result<()> { write!(writer, "{}", r#"using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; using System.Text; +using System.IO; namespace LiveSplitCore -{ - public class LiveSplitCoreNative - {"#)?; +{"#)?; for function in functions { let name = function.name.to_string(); @@ -53,23 +50,43 @@ namespace LiveSplitCore let postfix = splits.next().unwrap(); if new_prefix != prefix { if !prefix.is_empty() { + if prefix == "Run" { + write!(writer, + "{}", + r#" + public static IntPtr Parse(FileStream file) + { + var data = new byte[file.Length]; + file.Read(data, 0, data.Length); + IntPtr pnt = Marshal.AllocHGlobal(data.Length); + try + { + Marshal.Copy(data, 0, pnt, data.Length); + return Parse(pnt, (UIntPtr)data.Length); + } + finally + { + Marshal.FreeHGlobal(pnt); + } + }"#)?; + } writeln!(writer, "{}", r#" - }"#)?; + }"#)?; } write!(writer, r#" - public static class {} - {{"#, + public static class {} + {{"#, new_prefix)?; } prefix = new_prefix.to_string(); write!(writer, r#" - [DllImport("livesplit_core", EntryPoint="{}")] - public static extern {} {}("#, + [DllImport("livesplit_core", EntryPoint="{}")] + public static extern {} {}("#, &function.name, get_type(&function.output, true), postfix.to_camel_case())?; @@ -94,10 +111,9 @@ namespace LiveSplitCore writeln!(writer, "{}", r#" - } } - internal class LSCoreString : SafeHandle + public class LSCoreString : SafeHandle { public LSCoreString() : base(IntPtr.Zero, false) { } diff --git a/capi/bind_gen/src/node.rs b/capi/bind_gen/src/node.rs index a5678d04..34adc5dd 100644 --- a/capi/bind_gen/src/node.rs +++ b/capi/bind_gen/src/node.rs @@ -36,6 +36,7 @@ pub fn write(mut writer: W, functions: &[Function]) -> Result<()> { write!(writer, "{}", r#"var ffi = require('ffi'); +let fs = require('fs'); var ls = ffi.Library('livesplit_core', {"#)?; @@ -124,5 +125,15 @@ exports.{}_{} = function("#, };"#)?; } - Ok(()) + writeln!(writer, + "{}", + r#" +exports.Run_parseFile = function(file) { + var data = fs.readFileSync(file); + var run = ls.Run_parse(data, data.byteLength); + if (run.isNull()) { + return null; + } + return run; +}"#) } diff --git a/capi/src/hotkey_timer.rs b/capi/src/hotkey_timer.rs new file mode 100644 index 00000000..a9a516ea --- /dev/null +++ b/capi/src/hotkey_timer.rs @@ -0,0 +1,32 @@ +use livesplit_core::HotkeyTimer; +use timer::OwnedTimer; +use timer_read_lock::OwnedTimerReadLock; +use timer_write_lock::OwnedTimerWriteLock; +use super::{alloc, own, acc, drop}; +use std::ptr; + +pub type OwnedHotkeyTimer = *mut HotkeyTimer; + +#[no_mangle] +pub unsafe extern "C" fn HotkeyTimer_new(timer: OwnedTimer) -> OwnedHotkeyTimer { + if let Ok(timer) = HotkeyTimer::new(own(timer)) { + alloc(timer) + } else { + ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn HotkeyTimer_drop(this: OwnedHotkeyTimer) { + drop(this); +} + +#[no_mangle] +pub unsafe extern "C" fn HotkeyTimer_read(this: *const HotkeyTimer) -> OwnedTimerReadLock { + alloc(acc(this).read()) +} + +#[no_mangle] +pub unsafe extern "C" fn HotkeyTimer_write(this: *const HotkeyTimer) -> OwnedTimerWriteLock { + alloc(acc(this).write()) +} diff --git a/capi/src/lib.rs b/capi/src/lib.rs index 1185b011..0f198d4e 100644 --- a/capi/src/lib.rs +++ b/capi/src/lib.rs @@ -9,6 +9,7 @@ use std::cell::{Cell, RefCell}; use std::mem; pub mod timer; +pub mod hotkey_timer; pub mod run; pub mod segment_list; pub mod segment; @@ -32,6 +33,8 @@ pub mod sum_of_best_component_state; pub mod possible_time_save_component; pub mod possible_time_save_component_state; pub mod run_editor; +pub mod timer_read_lock; +pub mod timer_write_lock; use segment_history_element::SegmentHistoryElement; use livesplit_core::{Time, TimeSpan}; diff --git a/capi/src/run.rs b/capi/src/run.rs index 34571e33..e3fa7be8 100644 --- a/capi/src/run.rs +++ b/capi/src/run.rs @@ -1,5 +1,5 @@ -use livesplit_core::{Run, RunMetadata, TimeSpan, Segment, Attempt, parser}; -use super::{alloc, drop, acc, own, output_str, output_time_span, acc_mut, str}; +use livesplit_core::{Run, RunMetadata, TimeSpan, Segment, Attempt, parser, saver}; +use super::{alloc, drop, acc, own, output_str, output_time_span, output_vec, acc_mut, str}; use segment_list::OwnedSegmentList; use std::io::Cursor; use std::{slice, ptr}; @@ -111,3 +111,8 @@ pub unsafe extern "C" fn Run_attempt_history_index(this: *const Run, -> *const Attempt { &acc(this).attempt_history()[index] } + +#[no_mangle] +pub unsafe extern "C" fn Run_save_as_lss(this: *const Run) -> *const c_char { + output_vec(|o| { saver::livesplit::save(acc(this), o).unwrap(); }) +} diff --git a/capi/src/timer.rs b/capi/src/timer.rs index 6102e28e..cec37457 100644 --- a/capi/src/timer.rs +++ b/capi/src/timer.rs @@ -1,5 +1,5 @@ -use livesplit_core::{Timer, TimeSpan, TimingMethod, TimerPhase, Run, saver}; -use super::{alloc, own, acc_mut, drop, acc, output_str, output_vec, output_time_span}; +use livesplit_core::{Timer, TimeSpan, TimingMethod, TimerPhase, Run}; +use super::{alloc, own, acc_mut, drop, acc, output_str, output_time_span}; use run::OwnedRun; use libc::c_char; @@ -15,11 +15,6 @@ pub unsafe extern "C" fn Timer_drop(this: OwnedTimer) { drop(this); } -#[no_mangle] -pub unsafe extern "C" fn Timer_start(this: *mut Timer) { - acc_mut(this).start(); -} - #[no_mangle] pub unsafe extern "C" fn Timer_split(this: *mut Timer) { acc_mut(this).split(); @@ -134,8 +129,3 @@ pub unsafe extern "C" fn Timer_clone_run(this: *const Timer) -> OwnedRun { pub unsafe extern "C" fn Timer_print_debug(this: *const Timer) { println!("{:#?}", acc(this)); } - -#[no_mangle] -pub unsafe extern "C" fn Timer_save_run_as_lss(this: *const Timer) -> *const c_char { - output_vec(|o| { saver::livesplit::save(acc(this).run(), o).unwrap(); }) -} diff --git a/capi/src/timer_read_lock.rs b/capi/src/timer_read_lock.rs new file mode 100644 index 00000000..dba05e2f --- /dev/null +++ b/capi/src/timer_read_lock.rs @@ -0,0 +1,17 @@ +use livesplit_core::Timer; +use super::{drop, acc}; +use std::sync::RwLockReadGuard; +use std::ops::Deref; + +pub type TimerReadLock = RwLockReadGuard<'static, Timer>; +pub type OwnedTimerReadLock = *mut TimerReadLock; + +#[no_mangle] +pub unsafe extern "C" fn TimerReadLock_drop(this: OwnedTimerReadLock) { + drop(this); +} + +#[no_mangle] +pub unsafe extern "C" fn TimerReadLock_timer(this: *const TimerReadLock) -> *const Timer { + acc(this).deref() +} diff --git a/capi/src/timer_write_lock.rs b/capi/src/timer_write_lock.rs new file mode 100644 index 00000000..bed7b19c --- /dev/null +++ b/capi/src/timer_write_lock.rs @@ -0,0 +1,17 @@ +use livesplit_core::Timer; +use super::{drop, acc_mut}; +use std::sync::RwLockWriteGuard; +use std::ops::DerefMut; + +pub type TimerWriteLock = RwLockWriteGuard<'static, Timer>; +pub type OwnedTimerWriteLock = *mut TimerWriteLock; + +#[no_mangle] +pub unsafe extern "C" fn TimerWriteLock_drop(this: OwnedTimerWriteLock) { + drop(this); +} + +#[no_mangle] +pub unsafe extern "C" fn TimerWriteLock_timer(this: *mut TimerWriteLock) -> *mut Timer { + acc_mut(this).deref_mut() +} diff --git a/hotkey/.gitignore b/hotkey/.gitignore new file mode 100644 index 00000000..4308d822 --- /dev/null +++ b/hotkey/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/hotkey/Cargo.toml b/hotkey/Cargo.toml new file mode 100644 index 00000000..22b827c3 --- /dev/null +++ b/hotkey/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "hotkey" +version = "0.1.0" +authors = ["Christopher Serr "] + +[target.'cfg(windows)'.dependencies] +winapi = "0.2.8" +user32-sys = "0.2.0" +kernel32-sys = "0.2.2" + +[dependencies] diff --git a/hotkey/src/lib.rs b/hotkey/src/lib.rs new file mode 100644 index 00000000..7cbc09e6 --- /dev/null +++ b/hotkey/src/lib.rs @@ -0,0 +1,15 @@ +#[cfg(windows)] +pub mod windows; +#[cfg(windows)] +pub use windows::*; + +#[cfg(not(any(windows)))] +pub mod other; +#[cfg(not(any(windows)))] +pub use other::*; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum KeyEvent { + KeyUp(KeyCode), + KeyDown(KeyCode), +} diff --git a/hotkey/src/other/mod.rs b/hotkey/src/other/mod.rs new file mode 100644 index 00000000..d1bf004f --- /dev/null +++ b/hotkey/src/other/mod.rs @@ -0,0 +1,13 @@ +use KeyEvent; + +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] +pub struct KeyCode; + +pub struct Hook; + +pub fn register_hook(callback: F) -> Result + where F: FnMut(KeyEvent) + Send + 'static +{ + drop(callback); + Ok(Hook) +} diff --git a/hotkey/src/windows/mod.rs b/hotkey/src/windows/mod.rs new file mode 100644 index 00000000..284f0508 --- /dev/null +++ b/hotkey/src/windows/mod.rs @@ -0,0 +1,294 @@ +extern crate winapi; +extern crate kernel32; +extern crate user32; + +use std::cell::RefCell; +use std::{ptr, mem, thread}; +use std::sync::mpsc::{channel, Sender}; +use self::winapi::{c_int, WPARAM, LPARAM, LRESULT, WH_KEYBOARD_LL, HHOOK, WM_KEYDOWN, WM_KEYUP, + KBDLLHOOKSTRUCT, UINT, DWORD}; +use self::user32::{CallNextHookEx, UnhookWindowsHookEx, SetWindowsHookExW, GetMessageW, + PostThreadMessageW}; +use self::kernel32::{GetModuleHandleW, GetCurrentThreadId}; +use KeyEvent; + +const MSG_EXIT: UINT = 0x400; + +#[repr(u8)] +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] +pub enum KeyCode { + LButton = 0x01, + RButton = 0x02, + Cancel = 0x03, + MButton = 0x04, + XButton1 = 0x05, + XButton2 = 0x06, + Back = 0x08, + Tab = 0x09, + Clear = 0x0C, + Return = 0x0D, + Shift = 0x10, + Control = 0x11, + Menu = 0x12, + Pause = 0x13, + Capital = 0x14, + Kana = 0x15, + Junja = 0x17, + Final = 0x18, + Kanji = 0x19, + Escape = 0x1B, + Convert = 0x1C, + NonConvert = 0x1D, + Accept = 0x1E, + ModeChange = 0x1F, + Space = 0x20, + Prior = 0x21, + Next = 0x22, + End = 0x23, + Home = 0x24, + Left = 0x25, + Up = 0x26, + Right = 0x27, + Down = 0x28, + Select = 0x29, + Print = 0x2A, + Execute = 0x2B, + Snapshot = 0x2C, + Insert = 0x2D, + Delete = 0x2E, + Help = 0x2F, + Num0 = 0x30, + Num1 = 0x31, + Num2 = 0x32, + Num3 = 0x33, + Num4 = 0x34, + Num5 = 0x35, + Num6 = 0x36, + Num7 = 0x37, + Num8 = 0x38, + Num9 = 0x39, + A = 0x41, + B = 0x42, + C = 0x43, + D = 0x44, + E = 0x45, + F = 0x46, + G = 0x47, + H = 0x48, + I = 0x49, + J = 0x4A, + K = 0x4B, + L = 0x4C, + M = 0x4D, + N = 0x4E, + O = 0x4F, + P = 0x50, + Q = 0x51, + R = 0x52, + S = 0x53, + T = 0x54, + U = 0x55, + V = 0x56, + W = 0x57, + X = 0x58, + Y = 0x59, + Z = 0x5A, + LWin = 0x5B, + RWin = 0x5C, + Apps = 0x5D, + Sleep = 0x5F, + NumPad0 = 0x60, + NumPad1 = 0x61, + NumPad2 = 0x62, + NumPad3 = 0x63, + NumPad4 = 0x64, + NumPad5 = 0x65, + NumPad6 = 0x66, + NumPad7 = 0x67, + NumPad8 = 0x68, + NumPad9 = 0x69, + Multiply = 0x6A, + Add = 0x6B, + Separator = 0x6C, + Subtract = 0x6D, + Decimal = 0x6E, + Divide = 0x6F, + F1 = 0x70, + F2 = 0x71, + F3 = 0x72, + F4 = 0x73, + F5 = 0x74, + F6 = 0x75, + F7 = 0x76, + F8 = 0x77, + F9 = 0x78, + F10 = 0x79, + F11 = 0x7A, + F12 = 0x7B, + F13 = 0x7C, + F14 = 0x7D, + F15 = 0x7E, + F16 = 0x7F, + F17 = 0x80, + F18 = 0x81, + F19 = 0x82, + F20 = 0x83, + F21 = 0x84, + F22 = 0x85, + F23 = 0x86, + F24 = 0x87, + NumLock = 0x90, + Scroll = 0x91, + LShift = 0xA0, + RShift = 0xA1, + LControl = 0xA2, + RControl = 0xA3, + LMenu = 0xA4, + RMenu = 0xA5, + BrowserBack = 0xA6, + BrowserForward = 0xA7, + BrowserRefresh = 0xA8, + BrowserStop = 0xA9, + BrowserSearch = 0xAA, + BrowserFavorites = 0xAB, + BrowserHome = 0xAC, + VolumeMute = 0xAD, + VolumeDown = 0xAE, + VolumeUp = 0xAF, + MediaNextTrack = 0xB0, + MediaPrevTrack = 0xB1, + MediaStop = 0xB2, + MediaPlayPause = 0xB3, + LaunchMail = 0xB4, + LaunchMediaSelect = 0xB5, + LaunchApp1 = 0xB6, + LaunchApp2 = 0xB7, + Oem1 = 0xBA, + OemPlus = 0xBB, + OemComma = 0xBC, + OemMinus = 0xBD, + OemPeriod = 0xBE, + Oem2 = 0xBF, + Oem3 = 0xC0, + Oem4 = 0xDB, + Oem5 = 0xDC, + Oem6 = 0xDD, + Oem7 = 0xDE, + Oem8 = 0xDF, + Oem102 = 0xE2, + ProcessKey = 0xE5, + Packet = 0xE7, + Attn = 0xF6, + CrSel = 0xF7, + ExSel = 0xF8, + ErEof = 0xF9, + Play = 0xFA, + Zoom = 0xFB, + NoName = 0xFC, + Pa1 = 0xFD, + OemClear = 0xFE, +} + +pub struct Hook { + thread_id: DWORD, +} + +impl Drop for Hook { + fn drop(&mut self) { + unsafe { + PostThreadMessageW(self.thread_id, MSG_EXIT, 0, 0); + } + } +} + +struct State { + hook: HHOOK, + events: Sender, +} + +thread_local! { + static STATE: RefCell> = RefCell::new(None); +} + +unsafe extern "system" fn callback_proc(code: c_int, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + STATE.with(|state| { + let mut state = state.borrow_mut(); + let state = state.as_mut().expect("State should be initialized by now"); + + if code >= 0 { + let key_code = mem::transmute((*(lparam as *const KBDLLHOOKSTRUCT)).vkCode as u8); + let event = wparam as UINT; + if event == WM_KEYDOWN { + state.events + .send(KeyEvent::KeyDown(key_code)) + .expect("Callback Thread disconnected"); + } else if event == WM_KEYUP { + state.events.send(KeyEvent::KeyUp(key_code)).expect("Callback Thread disconnected"); + } + } + + CallNextHookEx(state.hook, code, wparam, lparam) + }) +} + +pub fn register_hook(mut callback: F) -> Result + where F: FnMut(KeyEvent) + Send + 'static +{ + let (initialized_tx, initialized_rx) = channel(); + let (events_tx, events_rx) = channel(); + + thread::spawn(move || { + let mut hook = ptr::null_mut(); + + STATE.with(|state| { + hook = unsafe { + SetWindowsHookExW(WH_KEYBOARD_LL, + Some(callback_proc), + GetModuleHandleW(ptr::null()), + 0) + }; + + if hook != ptr::null_mut() { + initialized_tx.send(Ok(unsafe { GetCurrentThreadId() })) + .map_err(|_| ())?; + } else { + initialized_tx.send(Err(())).map_err(|_| ())?; + } + + *state.borrow_mut() = Some(State { + hook: hook, + events: events_tx, + }); + + Ok(()) + })?; + + let mut msg = unsafe { mem::uninitialized() }; + loop { + let ret = unsafe { GetMessageW(&mut msg, ptr::null_mut(), 0, 0) }; + + if msg.message == MSG_EXIT { + break; + } else if ret < 0 { + return Err(()); + } else { + break; + } + } + + unsafe { + UnhookWindowsHookEx(hook); + } + + Ok(()) + }); + + + thread::spawn(move || while let Ok(event) = events_rx.recv() { + callback(event); + }); + + let thread_id = initialized_rx.recv().map_err(|_| ())??; + + Ok(Hook { thread_id: thread_id }) +} diff --git a/src/hotkey_config.rs b/src/hotkey_config.rs new file mode 100644 index 00000000..afec79ba --- /dev/null +++ b/src/hotkey_config.rs @@ -0,0 +1,43 @@ +use hotkey::KeyCode; + +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct HotkeyConfig { + pub split: KeyCode, + pub reset: KeyCode, + pub undo: KeyCode, + pub skip: KeyCode, + pub pause: KeyCode, + pub previous_comparison: KeyCode, + pub next_comparison: KeyCode, +} + +#[cfg(windows)] +impl Default for HotkeyConfig { + fn default() -> Self { + use hotkey::KeyCode::*; + Self { + split: NumPad1, + reset: NumPad3, + undo: NumPad8, + skip: NumPad2, + pause: NumPad5, + previous_comparison: NumPad4, + next_comparison: NumPad6, + } + } +} + +#[cfg(not(any(windows)))] +impl Default for HotkeyConfig { + fn default() -> Self { + Self { + split: KeyCode, + reset: KeyCode, + undo: KeyCode, + skip: KeyCode, + pause: KeyCode, + previous_comparison: KeyCode, + next_comparison: KeyCode, + } + } +} diff --git a/src/hotkey_timer.rs b/src/hotkey_timer.rs new file mode 100644 index 00000000..fd270f7a --- /dev/null +++ b/src/hotkey_timer.rs @@ -0,0 +1,59 @@ +use {Timer, HotkeyConfig}; +use hotkey::{register_hook, Hook, KeyEvent}; +use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; + +pub struct HotkeyTimer { + timer: Arc>, + config: Arc>, + _hook: Hook, +} + +impl HotkeyTimer { + pub fn new(timer: Timer) -> Result { + Self::with_config(timer, Default::default()) + } + + pub fn with_config(timer: Timer, config: HotkeyConfig) -> Result { + let timer = Arc::new(RwLock::new(timer)); + let config = Arc::new(RwLock::new(config)); + + Ok(Self { + timer: timer.clone(), + config: config.clone(), + _hook: register_hook(move |k| if let KeyEvent::KeyDown(k) = k { + let config = config.read().unwrap(); + if k == config.split { + timer.write().unwrap().split(); + } else if k == config.reset { + timer.write().unwrap().reset(true); + } else if k == config.undo { + timer.write().unwrap().undo_split(); + } else if k == config.skip { + timer.write().unwrap().skip_split(); + } else if k == config.pause { + timer.write().unwrap().pause(); + } else if k == config.previous_comparison { + timer.write().unwrap().switch_to_previous_comparison(); + } else if k == config.next_comparison { + timer.write().unwrap().switch_to_next_comparison(); + } + })?, + }) + } + + pub fn read(&self) -> RwLockReadGuard { + self.timer.read().unwrap() + } + + pub fn write(&self) -> RwLockWriteGuard { + self.timer.write().unwrap() + } + + pub fn read_config(&self) -> RwLockReadGuard { + self.config.read().unwrap() + } + + pub fn write_config(&self) -> RwLockWriteGuard { + self.config.write().unwrap() + } +} diff --git a/src/lib.rs b/src/lib.rs index 0195e287..0d1aaab8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,10 +17,13 @@ extern crate derive_more; #[macro_use] extern crate derive_new; extern crate pdqsort; +extern crate hotkey; mod atomic_date_time; mod attempt; mod color; +mod hotkey_config; +mod hotkey_timer; mod image; mod run_metadata; mod run; @@ -57,3 +60,5 @@ pub use self::time_stamp::TimeStamp; pub use self::timer::Timer; pub use self::timer_phase::TimerPhase; pub use self::timing_method::TimingMethod; +pub use self::hotkey_config::HotkeyConfig; +pub use self::hotkey_timer::HotkeyTimer; diff --git a/src/timer.rs b/src/timer.rs index 5a572206..c6e85929 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -119,7 +119,7 @@ impl Timer { self.current_split_index } - pub fn start(&mut self) { + pub fn split(&mut self) { if self.phase == NotRunning { self.phase = Running; self.current_split_index = 0; @@ -131,22 +131,20 @@ impl Timer { self.run.start_next_run(); // TODO OnStart - } - } + } else { + let current_time = self.current_time(); + if self.phase == TimerPhase::Running && + current_time.real_time.map_or(false, |t| t >= TimeSpan::zero()) { + self.current_split_mut().unwrap().set_split_time(current_time); + self.current_split_index += 1; + if self.run.len() as isize == self.current_split_index { + self.phase = TimerPhase::Ended; + self.attempt_ended = Some(AtomicDateTime::now()); + } + self.run.mark_as_changed(); - pub fn split(&mut self) { - let current_time = self.current_time(); - if self.phase == TimerPhase::Running && - current_time.real_time.map_or(false, |t| t >= TimeSpan::zero()) { - self.current_split_mut().unwrap().set_split_time(current_time); - self.current_split_index += 1; - if self.run.len() as isize == self.current_split_index { - self.phase = TimerPhase::Ended; - self.attempt_ended = Some(AtomicDateTime::now()); + // TODO OnSplit } - self.run.mark_as_changed(); - - // TODO OnSplit } } @@ -221,7 +219,7 @@ impl Timer { // TODO OnResume } - TimerPhase::NotRunning => self.start(), // Fuck abahbob + TimerPhase::NotRunning => self.split(), // Fuck abahbob _ => {} } }