2016-01-12 21:49:29 -08:00
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "event_m0.hpp"
2016-01-12 22:20:13 -08:00
#include "portapack.hpp"
2016-07-27 03:03:40 +02:00
#include "portapack_persistent_memory.hpp"
2023-03-13 15:03:40 +01:00
#include "debug.hpp"
2016-01-12 22:20:13 -08:00
#include "sd_card.hpp"
2016-08-21 17:49:06 -07:00
#include "rtc_time.hpp"
2016-01-12 22:20:13 -08:00
#include "message.hpp"
#include "message_queue.hpp"
#include "irq_controls.hpp"
2016-10-06 13:38:56 -07:00
#include "buffer_exchange.hpp"
2016-03-27 22:49:30 -07:00
2016-01-12 21:49:29 -08:00
#include "ch.h"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx ;
2016-01-12 22:20:13 -08:00
#include <array>
2023-04-23 16:21:33 +02:00
#include "ui_navigation.hpp"
2016-02-27 12:10:39 -08:00
2024-02-13 13:06:47 +01:00
static int delayed_error = 0 ;
2016-01-12 21:49:29 -08:00
extern "C" {
CH_IRQ_HANDLER ( M4Core_IRQHandler ) {
2023-05-19 08:16:05 +12:00
CH_IRQ_PROLOGUE ();
2016-01-12 21:49:29 -08:00
2023-05-19 08:16:05 +12:00
chSysLockFromIsr ();
BufferExchange :: handle_isr ();
EventDispatcher :: check_fifo_isr ();
chSysUnlockFromIsr ();
2016-01-12 21:49:29 -08:00
2023-05-19 08:16:05 +12:00
creg :: m4txevent :: clear ();
2016-01-12 21:49:29 -08:00
2023-05-19 08:16:05 +12:00
CH_IRQ_EPILOGUE ();
2016-01-12 21:49:29 -08:00
}
}
2016-01-12 22:00:42 -08:00
2016-06-19 22:56:06 -07:00
class MessageHandlerMap {
2023-05-19 08:16:05 +12:00
public :
using MessageHandler = std :: function < void ( Message * const p ) > ;
2016-06-19 22:56:06 -07:00
2023-05-19 08:16:05 +12:00
void register_handler ( const Message :: ID id , MessageHandler && handler ) {
if ( map_ [ toUType ( id )] != nullptr ) {
chDbgPanic ( "MsgDblReg" );
}
map_ [ toUType ( id )] = std :: move ( handler );
}
2016-06-19 22:56:06 -07:00
2023-05-19 08:16:05 +12:00
void unregister_handler ( const Message :: ID id ) {
map_ [ toUType ( id )] = nullptr ;
}
2016-06-19 22:56:06 -07:00
2023-05-19 08:16:05 +12:00
void send ( Message * const message ) {
if ( message -> id < Message :: ID :: MAX ) {
auto & fn = map_ [ toUType ( message -> id )];
if ( fn ) {
fn ( message );
}
}
}
2016-06-19 22:56:06 -07:00
2023-05-19 08:16:05 +12:00
private :
using MapType = std :: array < MessageHandler , toUType ( Message :: ID :: MAX ) > ;
MapType map_ {};
2016-06-19 22:56:06 -07:00
};
static MessageHandlerMap message_map ;
2016-01-12 22:00:42 -08:00
Thread * EventDispatcher :: thread_event_loop = nullptr ;
2016-07-02 16:38:01 -07:00
bool EventDispatcher :: is_running = false ;
2016-12-09 18:21:47 +01:00
bool EventDispatcher :: display_sleep = false ;
2016-01-12 22:20:13 -08:00
EventDispatcher :: EventDispatcher (
2023-05-19 08:16:05 +12:00
ui :: Widget * const top_widget ,
ui :: Context & context )
: top_widget { top_widget },
painter {},
context ( context ) {
init_message_queues ();
2016-02-16 11:23:02 -08:00
2023-05-19 08:16:05 +12:00
thread_event_loop = chThdSelf ();
is_running = true ;
touch_manager . on_event = [ this ]( const ui :: TouchEvent event ) {
this -> on_touch_event ( event );
};
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: run () {
2023-05-19 08:16:05 +12:00
while ( is_running ) {
const auto events = wait ();
dispatch ( events );
}
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: request_stop () {
2023-05-19 08:16:05 +12:00
is_running = false ;
2016-01-12 22:20:13 -08:00
}
2016-01-27 21:03:34 -08:00
void EventDispatcher :: set_display_sleep ( const bool sleep ) {
2023-05-19 08:16:05 +12:00
// TODO: Distribute display sleep message more broadly, shut down data generation
2025-10-18 13:13:51 +02:00
// on baseband side, since all that data is being discarded during sleep. -- DON'T TODO it, sincethe stealth mode want to send with screen off!
2023-05-19 08:16:05 +12:00
if ( sleep ) {
portapack :: backlight () -> off ();
2025-10-18 13:13:51 +02:00
portapack :: display . sleep ( false ); // when called the hw_sleep = true, the irq wont fire, so the EVT_MASK_LCD_FRAME_SYNC won't set.
2023-05-19 08:16:05 +12:00
} else {
2025-10-18 13:13:51 +02:00
portapack :: display . wake ( true ); // not important, command not affect if already hw waken up
2023-05-19 08:16:05 +12:00
// Don't turn on backlight here.
// Let frame sync handler turn on backlight after repaint.
}
EventDispatcher :: display_sleep = sleep ;
2016-01-27 21:03:34 -08:00
};
2016-01-12 22:20:13 -08:00
eventmask_t EventDispatcher :: wait () {
2023-05-19 08:16:05 +12:00
return chEvtWaitAny ( ALL_EVENTS );
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: dispatch ( const eventmask_t events ) {
2023-05-19 08:16:05 +12:00
if ( shared_memory . m4_panic_msg [ 0 ] != 0 ) {
if ( shared_memory . bb_data . data [ 0 ] == 0 )
draw_guru_meditation ( CORTEX_M4 , shared_memory . m4_panic_msg );
else
draw_guru_meditation (
CORTEX_M4 ,
shared_memory . m4_panic_msg ,
( struct extctx * ) & shared_memory . bb_data . data [ 8 ],
* ( uint32_t * ) & shared_memory . bb_data . data [ 4 ]);
}
2016-02-27 12:10:39 -08:00
2024-01-07 22:25:43 +01:00
handle_shell ();
2023-05-19 08:16:05 +12:00
if ( events & EVT_MASK_APPLICATION ) {
handle_application_queue ();
}
2016-01-12 22:20:13 -08:00
2023-05-19 08:16:05 +12:00
if ( events & EVT_MASK_LOCAL ) {
handle_local_queue ();
}
2016-06-21 10:57:44 -07:00
2023-05-19 08:16:05 +12:00
if ( events & EVT_MASK_RTC_TICK ) {
2024-02-13 13:06:47 +01:00
// delay error message by 2 seconds to wait for LCD being ready
if ( portapack :: init_error != nullptr && ++ delayed_error > 1 )
draw_guru_meditation ( CORTEX_M4 , portapack :: init_error );
2023-05-19 08:16:05 +12:00
handle_rtc_tick ();
}
2016-01-27 21:03:34 -08:00
2024-01-13 18:05:29 +01:00
handle_usb_transfer ();
handle_usb ();
2024-01-07 14:09:22 +01:00
2023-05-19 08:16:05 +12:00
if ( events & EVT_MASK_SWITCHES ) {
handle_switches ();
}
2016-01-12 22:20:13 -08:00
2023-05-19 08:16:05 +12:00
/*if( events & EVT_MASK_LCD_FRAME_SYNC ) {
blink_timer();
}*/
2025-10-18 13:13:51 +02:00
if ( events & EVT_MASK_LCD_FRAME_SYNC ) {
handle_lcd_frame_sync ( ! EventDispatcher :: display_sleep );
}
2023-05-19 08:16:05 +12:00
if ( ! EventDispatcher :: display_sleep ) {
if ( events & EVT_MASK_ENCODER ) {
handle_encoder ();
}
if ( events & EVT_MASK_TOUCH ) {
handle_touch ();
}
}
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: handle_application_queue () {
2023-05-19 08:16:05 +12:00
shared_memory . application_queue . handle ([]( Message * const message ) {
message_map . send ( message );
});
2016-01-12 22:20:13 -08:00
}
2016-06-21 10:57:44 -07:00
void EventDispatcher :: handle_local_queue () {
2023-05-19 08:16:05 +12:00
shared_memory . app_local_queue . handle ([]( Message * const message ) {
message_map . send ( message );
});
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: handle_rtc_tick () {
2023-05-19 08:16:05 +12:00
sd_card :: poll_inserted ();
2016-01-12 22:20:13 -08:00
2023-05-19 08:16:05 +12:00
portapack :: temperature_logger . second_tick ();
2016-05-09 21:05:11 +02:00
2023-05-19 08:16:05 +12:00
const auto backlight_timer = portapack :: persistent_memory :: config_backlight_timer ();
if ( backlight_timer . timeout_enabled ()) {
if ( portapack :: bl_tick_counter == backlight_timer . timeout_seconds ())
set_display_sleep ( true );
else
portapack :: bl_tick_counter ++ ;
}
2022-05-28 13:55:18 -07:00
2023-05-19 08:16:05 +12:00
rtc_time :: on_tick_second ();
portapack :: persistent_memory :: cache :: persist ();
2016-01-12 22:20:13 -08:00
}
2024-01-07 14:09:22 +01:00
void EventDispatcher :: handle_usb () {
portapack :: usb_serial . dispatch ();
}
2024-01-13 18:05:29 +01:00
void EventDispatcher :: handle_usb_transfer () {
portapack :: usb_serial . dispatch_transfer ();
}
2024-01-07 22:25:43 +01:00
void EventDispatcher :: handle_shell () {
if ( waiting_for_shellmode ) {
waiting_for_shellmode = false ;
shellmode_active = true ;
while ( shellmode_active ) {
chThdSleepMilliseconds ( 5 );
}
}
if ( injected_touch_event != nullptr ) {
on_touch_event ( * injected_touch_event );
injected_touch_event = nullptr ;
}
if ( injected_keyboard_event != nullptr ) {
on_keyboard_event ( * injected_keyboard_event );
injected_keyboard_event = nullptr ;
}
}
2016-01-12 22:20:13 -08:00
ui :: Widget * EventDispatcher :: touch_widget ( ui :: Widget * const w , ui :: TouchEvent event ) {
2023-05-19 08:16:05 +12:00
if ( ! w -> hidden ()) {
// To achieve reverse depth ordering (last object drawn is
// considered "top"), descend first.
for ( const auto child : w -> children ()) {
const auto touched_widget = touch_widget ( child , event );
if ( touched_widget ) {
return touched_widget ;
}
}
2016-01-12 22:20:13 -08:00
2023-05-19 08:16:05 +12:00
const auto r = w -> screen_rect ();
if ( r . contains ( event . point )) {
if ( w -> on_touch ( event )) {
// This widget responded. Return it up the call stack.
return w ;
}
}
}
return nullptr ;
2016-01-12 22:20:13 -08:00
}
2024-01-03 14:06:29 +01:00
void EventDispatcher :: emulateTouch ( ui :: TouchEvent event ) {
2024-01-07 22:25:43 +01:00
injected_touch_event = & event ;
while ( injected_touch_event != nullptr ) {
chThdSleepMilliseconds ( 5 );
}
2024-08-15 19:31:34 +02:00
injected_touch_event = nullptr ; // to clean event_mo.cpp, compile warning error : "storing the address of local variable 'event' in 'this_4(D)->injected_touch_event' [-Wdangling-pointer=]"
2024-01-03 14:06:29 +01:00
}
2024-01-04 17:36:31 +01:00
void EventDispatcher :: emulateKeyboard ( ui :: KeyboardEvent event ) {
2024-01-07 22:25:43 +01:00
injected_keyboard_event = & event ;
while ( injected_keyboard_event != nullptr ) {
chThdSleepMilliseconds ( 5 );
}
2024-08-15 19:31:34 +02:00
injected_keyboard_event = nullptr ; // to clean event_mo.cpp, compile warning error : "storing the address of local variable 'event' in 'this_4(D)->injected_keyboard_event' [-Wdangling-pointer=]"
2024-01-04 17:36:31 +01:00
}
void EventDispatcher :: on_keyboard_event ( ui :: KeyboardEvent event ) {
// send the key to focused widget, or parent if not accepts it
auto target = context . focus_manager (). focus_widget ();
while (( target != nullptr ) && ! target -> on_keyboard ( event )) {
target = target -> parent ();
}
}
2016-01-12 22:20:13 -08:00
void EventDispatcher :: on_touch_event ( ui :: TouchEvent event ) {
2023-05-19 08:16:05 +12:00
/* TODO: Capture widget receiving the Start event, send Move and
* End events to the same widget.
*/
/* Capture Start widget.
* If touch is over Start widget at Move event, then the widget
* should be highlighted. If the touch is not over the Start
* widget at Move event, widget should un-highlight.
* If touch is over Start widget at End event, then the widget
* action should occur.
*/
if ( event . type == ui :: TouchEvent :: Type :: Start ) {
captured_widget = touch_widget ( this -> top_widget , event );
}
2016-01-12 22:20:13 -08:00
2023-05-19 08:16:05 +12:00
if ( captured_widget ) {
captured_widget -> on_touch ( event );
}
2016-01-12 22:20:13 -08:00
}
2024-01-05 07:43:30 +01:00
ui :: Widget * EventDispatcher :: getTopWidget () {
return top_widget ;
}
ui :: Widget * EventDispatcher :: getFocusedWidget () {
return context . focus_manager (). focus_widget ();
}
2025-10-18 13:13:51 +02:00
void EventDispatcher :: handle_lcd_frame_sync ( bool screen_on ) {
2024-01-07 22:25:43 +01:00
bool waiting_for_frame = this -> waiting_for_frame ;
2025-10-18 13:13:51 +02:00
DisplayFrameSyncMessage message ; // send framesync msg all the time, bc some apps relay on it
2023-05-19 08:16:05 +12:00
message_map . send ( & message );
2025-10-18 13:13:51 +02:00
if ( screen_on ) { // only draw when screen is on
static_cast < ui :: SystemView *> ( top_widget ) -> paint_overlay ();
painter . paint_widget_tree ( top_widget );
portapack :: backlight () -> on ();
}
2024-01-07 22:25:43 +01:00
if ( waiting_for_frame )
this -> waiting_for_frame = false ;
}
void EventDispatcher :: wait_finish_frame () {
waiting_for_frame = true ;
while ( waiting_for_frame ) {
chThdSleepMilliseconds ( 5 );
}
}
void EventDispatcher :: enter_shell_working_mode () {
waiting_for_shellmode = true ;
while ( waiting_for_shellmode ) {
chThdSleepMilliseconds ( 5 );
}
}
void EventDispatcher :: exit_shell_working_mode () {
shellmode_active = false ;
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: handle_switches () {
2023-05-19 08:16:05 +12:00
const auto switches_state = get_switches_state ();
2016-01-27 21:03:34 -08:00
2023-05-19 08:16:05 +12:00
portapack :: bl_tick_counter = 0 ;
2016-04-28 14:59:14 +02:00
2023-05-19 08:16:05 +12:00
if ( switches_state . count () == 0 ) {
// If all keys are released, we are no longer in a key event.
in_key_event = false ;
}
2019-02-03 19:25:11 +01:00
2023-05-19 08:16:05 +12:00
if ( in_key_event ) {
if ( switches_state [( size_t ) ui :: KeyEvent :: Left ] && switches_state [( size_t ) ui :: KeyEvent :: Up ]) {
const auto event = static_cast < ui :: KeyEvent > ( ui :: KeyEvent :: Back );
context . focus_manager (). update ( top_widget , event );
}
2021-10-09 22:46:05 +01:00
2023-05-19 08:16:05 +12:00
// If we're in a key event, return. We will ignore all additional key
// presses until the first key is released. We also want to ignore events
// where the last key held generates a key event when other pressed keys
// are released.
return ;
}
2019-02-03 19:25:11 +01:00
2023-05-19 08:16:05 +12:00
if ( EventDispatcher :: display_sleep ) {
// Swallow event, wake up display.
if ( switches_state . any ()) {
set_display_sleep ( false );
}
return ;
}
2016-01-27 21:03:34 -08:00
2023-05-19 08:16:05 +12:00
for ( size_t i = 0 ; i < switches_state . size (); i ++ ) {
// TODO: Ignore multiple keys at the same time?
if ( switches_state [ i ]) {
const auto event = static_cast < ui :: KeyEvent > ( i );
if ( ! event_bubble_key ( event )) {
if ( switches_state [( size_t ) ui :: KeyEvent :: Dfu ]) {
static_cast < ui :: SystemView *> ( top_widget ) -> toggle_overlay ();
} else {
context . focus_manager (). update ( top_widget , event );
}
}
2019-02-03 19:25:11 +01:00
2023-05-19 08:16:05 +12:00
in_key_event = true ;
}
}
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: handle_encoder () {
2023-05-19 08:16:05 +12:00
portapack :: bl_tick_counter = 0 ;
if ( EventDispatcher :: display_sleep ) {
// Swallow event, wake up display.
set_display_sleep ( false );
return ;
}
const uint32_t encoder_now = get_encoder_position ();
const int32_t delta = static_cast < int32_t > ( encoder_now - encoder_last );
2024-01-09 23:32:58 -06:00
if ( delta == 0 )
return ;
2023-05-19 08:16:05 +12:00
encoder_last = encoder_now ;
const auto event = static_cast < ui :: EncoderEvent > ( delta );
event_bubble_encoder ( event );
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: handle_touch () {
2023-05-19 08:16:05 +12:00
portapack :: bl_tick_counter = 0 ;
2020-05-09 16:23:22 +02:00
2023-05-19 08:16:05 +12:00
touch_manager . feed ( get_touch_frame ());
2016-01-12 22:20:13 -08:00
}
bool EventDispatcher :: event_bubble_key ( const ui :: KeyEvent event ) {
2023-05-19 08:16:05 +12:00
auto target = context . focus_manager (). focus_widget ();
while (( target != nullptr ) && ! target -> on_key ( event )) {
target = target -> parent ();
}
2016-01-12 22:20:13 -08:00
2023-05-19 08:16:05 +12:00
/* Return true if event was consumed. */
return ( target != nullptr );
2016-01-12 22:20:13 -08:00
}
void EventDispatcher :: event_bubble_encoder ( const ui :: EncoderEvent event ) {
2023-05-19 08:16:05 +12:00
auto target = context . focus_manager (). focus_widget ();
while (( target != nullptr ) && ! target -> on_encoder ( event )) {
target = target -> parent ();
}
2016-02-16 11:23:02 -08:00
}
void EventDispatcher :: init_message_queues () {
2023-05-19 08:16:05 +12:00
new ( & shared_memory ) SharedMemory ;
2016-02-16 11:23:02 -08:00
}
2016-05-09 21:16:24 +02:00
2016-05-12 15:24:08 -07:00
MessageHandlerRegistration :: MessageHandlerRegistration (
2023-05-19 08:16:05 +12:00
const Message :: ID message_id ,
MessageHandlerMap :: MessageHandler && callback )
: message_id { message_id } {
message_map . register_handler ( message_id , std :: move ( callback ));
2016-05-12 15:24:08 -07:00
}
MessageHandlerRegistration ::~ MessageHandlerRegistration () {
2023-05-19 08:16:05 +12:00
message_map . unregister_handler ( message_id );
2016-02-16 11:23:02 -08:00
}