2016-01-13 00:49:29 -05: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-13 01:20:13 -05:00
# include "portapack.hpp"
2016-07-26 21:03:40 -04:00
# include "portapack_persistent_memory.hpp"
2023-03-13 10:03:40 -04:00
# include "debug.hpp"
2016-01-13 01:20:13 -05:00
# include "sd_card.hpp"
2016-08-21 20:49:06 -04:00
# include "rtc_time.hpp"
2016-01-13 01:20:13 -05:00
# include "message.hpp"
# include "message_queue.hpp"
# include "irq_controls.hpp"
2016-10-06 16:38:56 -04:00
# include "buffer_exchange.hpp"
2016-03-28 01:49:30 -04:00
2016-01-13 00:49:29 -05:00
# include "ch.h"
# include "lpc43xx_cpp.hpp"
using namespace lpc43xx ;
2016-01-13 01:20:13 -05:00
# include <array>
2023-04-23 10:21:33 -04:00
# include "ui_navigation.hpp"
2016-02-27 15:10:39 -05:00
2024-02-13 07:06:47 -05:00
static int delayed_error = 0 ;
2016-01-13 00:49:29 -05:00
extern " C " {
CH_IRQ_HANDLER ( M4Core_IRQHandler ) {
2023-05-18 16:16:05 -04:00
CH_IRQ_PROLOGUE ( ) ;
2016-01-13 00:49:29 -05:00
2023-05-18 16:16:05 -04:00
chSysLockFromIsr ( ) ;
BufferExchange : : handle_isr ( ) ;
EventDispatcher : : check_fifo_isr ( ) ;
chSysUnlockFromIsr ( ) ;
2016-01-13 00:49:29 -05:00
2023-05-18 16:16:05 -04:00
creg : : m4txevent : : clear ( ) ;
2016-01-13 00:49:29 -05:00
2023-05-18 16:16:05 -04:00
CH_IRQ_EPILOGUE ( ) ;
2016-01-13 00:49:29 -05:00
}
}
2016-01-13 01:00:42 -05:00
2016-06-20 01:56:06 -04:00
class MessageHandlerMap {
2023-05-18 16:16:05 -04:00
public :
using MessageHandler = std : : function < void ( Message * const p ) > ;
void register_handler ( const Message : : ID id , MessageHandler & & handler ) {
if ( map_ [ toUType ( id ) ] ! = nullptr ) {
chDbgPanic ( " MsgDblReg " ) ;
}
map_ [ toUType ( id ) ] = std : : move ( handler ) ;
}
void unregister_handler ( const Message : : ID id ) {
map_ [ toUType ( id ) ] = nullptr ;
}
void send ( Message * const message ) {
if ( message - > id < Message : : ID : : MAX ) {
auto & fn = map_ [ toUType ( message - > id ) ] ;
if ( fn ) {
fn ( message ) ;
}
}
}
private :
using MapType = std : : array < MessageHandler , toUType ( Message : : ID : : MAX ) > ;
MapType map_ { } ;
2016-06-20 01:56:06 -04:00
} ;
static MessageHandlerMap message_map ;
2016-01-13 01:00:42 -05:00
Thread * EventDispatcher : : thread_event_loop = nullptr ;
2016-07-02 19:38:01 -04:00
bool EventDispatcher : : is_running = false ;
2016-12-09 12:21:47 -05:00
bool EventDispatcher : : display_sleep = false ;
2016-01-13 01:20:13 -05:00
EventDispatcher : : EventDispatcher (
2023-05-18 16:16:05 -04:00
ui : : Widget * const top_widget ,
ui : : Context & context )
: top_widget { top_widget } ,
painter { } ,
context ( context ) {
init_message_queues ( ) ;
thread_event_loop = chThdSelf ( ) ;
is_running = true ;
touch_manager . on_event = [ this ] ( const ui : : TouchEvent event ) {
this - > on_touch_event ( event ) ;
} ;
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : run ( ) {
2023-05-18 16:16:05 -04:00
while ( is_running ) {
const auto events = wait ( ) ;
dispatch ( events ) ;
}
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : request_stop ( ) {
2023-05-18 16:16:05 -04:00
is_running = false ;
2016-01-13 01:20:13 -05:00
}
2016-01-28 00:03:34 -05:00
void EventDispatcher : : set_display_sleep ( const bool sleep ) {
2023-05-18 16:16:05 -04:00
// TODO: Distribute display sleep message more broadly, shut down data generation
// on baseband side, since all that data is being discarded during sleep.
if ( sleep ) {
portapack : : backlight ( ) - > off ( ) ;
portapack : : display . sleep ( ) ;
} else {
portapack : : display . wake ( ) ;
// Don't turn on backlight here.
// Let frame sync handler turn on backlight after repaint.
}
EventDispatcher : : display_sleep = sleep ;
2016-01-28 00:03:34 -05:00
} ;
2016-01-13 01:20:13 -05:00
eventmask_t EventDispatcher : : wait ( ) {
2023-05-18 16:16:05 -04:00
return chEvtWaitAny ( ALL_EVENTS ) ;
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : dispatch ( const eventmask_t events ) {
2023-05-18 16:16:05 -04: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 ] ) ;
}
2024-01-07 16:25:43 -05:00
handle_shell ( ) ;
2023-05-18 16:16:05 -04:00
if ( events & EVT_MASK_APPLICATION ) {
handle_application_queue ( ) ;
}
if ( events & EVT_MASK_LOCAL ) {
handle_local_queue ( ) ;
}
if ( events & EVT_MASK_RTC_TICK ) {
2024-02-13 07:06:47 -05: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-18 16:16:05 -04:00
handle_rtc_tick ( ) ;
}
2024-01-13 12:05:29 -05:00
handle_usb_transfer ( ) ;
handle_usb ( ) ;
2024-01-07 08:09:22 -05:00
2023-05-18 16:16:05 -04:00
if ( events & EVT_MASK_SWITCHES ) {
handle_switches ( ) ;
}
/*if( events & EVT_MASK_LCD_FRAME_SYNC ) {
blink_timer ( ) ;
} */
if ( ! EventDispatcher : : display_sleep ) {
if ( events & EVT_MASK_LCD_FRAME_SYNC ) {
handle_lcd_frame_sync ( ) ;
}
if ( events & EVT_MASK_ENCODER ) {
handle_encoder ( ) ;
}
if ( events & EVT_MASK_TOUCH ) {
handle_touch ( ) ;
}
}
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : handle_application_queue ( ) {
2023-05-18 16:16:05 -04:00
shared_memory . application_queue . handle ( [ ] ( Message * const message ) {
message_map . send ( message ) ;
} ) ;
2016-01-13 01:20:13 -05:00
}
2016-06-21 13:57:44 -04:00
void EventDispatcher : : handle_local_queue ( ) {
2023-05-18 16:16:05 -04:00
shared_memory . app_local_queue . handle ( [ ] ( Message * const message ) {
message_map . send ( message ) ;
} ) ;
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : handle_rtc_tick ( ) {
2023-05-18 16:16:05 -04:00
sd_card : : poll_inserted ( ) ;
portapack : : temperature_logger . second_tick ( ) ;
2016-01-13 01:20:13 -05:00
2023-05-18 16:16:05 -04: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 + + ;
}
2016-05-09 15:05:11 -04:00
2023-05-18 16:16:05 -04:00
rtc_time : : on_tick_second ( ) ;
2022-05-28 16:55:18 -04:00
2023-05-18 16:16:05 -04:00
portapack : : persistent_memory : : cache : : persist ( ) ;
2016-01-13 01:20:13 -05:00
}
2024-01-07 08:09:22 -05:00
void EventDispatcher : : handle_usb ( ) {
portapack : : usb_serial . dispatch ( ) ;
}
2024-01-13 12:05:29 -05:00
void EventDispatcher : : handle_usb_transfer ( ) {
portapack : : usb_serial . dispatch_transfer ( ) ;
}
2024-01-07 16:25:43 -05: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-13 01:20:13 -05:00
ui : : Widget * EventDispatcher : : touch_widget ( ui : : Widget * const w , ui : : TouchEvent event ) {
2023-05-18 16:16:05 -04: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 ;
}
}
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-13 01:20:13 -05:00
}
2024-01-03 08:06:29 -05:00
void EventDispatcher : : emulateTouch ( ui : : TouchEvent event ) {
2024-01-07 16:25:43 -05:00
injected_touch_event = & event ;
while ( injected_touch_event ! = nullptr ) {
chThdSleepMilliseconds ( 5 ) ;
}
2024-08-15 13:31:34 -04: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 08:06:29 -05:00
}
2024-01-04 11:36:31 -05:00
void EventDispatcher : : emulateKeyboard ( ui : : KeyboardEvent event ) {
2024-01-07 16:25:43 -05:00
injected_keyboard_event = & event ;
while ( injected_keyboard_event ! = nullptr ) {
chThdSleepMilliseconds ( 5 ) ;
}
2024-08-15 13:31:34 -04: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 11:36:31 -05: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-13 01:20:13 -05:00
void EventDispatcher : : on_touch_event ( ui : : TouchEvent event ) {
2023-05-18 16:16:05 -04: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 ) ;
}
if ( captured_widget ) {
captured_widget - > on_touch ( event ) ;
}
2016-01-13 01:20:13 -05:00
}
2024-01-05 01:43:30 -05:00
ui : : Widget * EventDispatcher : : getTopWidget ( ) {
return top_widget ;
}
ui : : Widget * EventDispatcher : : getFocusedWidget ( ) {
return context . focus_manager ( ) . focus_widget ( ) ;
}
2016-01-13 01:20:13 -05:00
void EventDispatcher : : handle_lcd_frame_sync ( ) {
2024-01-07 16:25:43 -05:00
bool waiting_for_frame = this - > waiting_for_frame ;
2023-05-18 16:16:05 -04:00
DisplayFrameSyncMessage message ;
message_map . send ( & message ) ;
2023-04-23 10:21:33 -04:00
2023-05-18 16:16:05 -04:00
static_cast < ui : : SystemView * > ( top_widget ) - > paint_overlay ( ) ;
painter . paint_widget_tree ( top_widget ) ;
2017-07-19 00:29:32 -04:00
2023-05-18 16:16:05 -04:00
portapack : : backlight ( ) - > on ( ) ;
2024-01-07 16:25:43 -05: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-13 01:20:13 -05:00
}
void EventDispatcher : : handle_switches ( ) {
2023-05-18 16:16:05 -04:00
const auto switches_state = get_switches_state ( ) ;
portapack : : bl_tick_counter = 0 ;
if ( switches_state . count ( ) = = 0 ) {
// If all keys are released, we are no longer in a key event.
in_key_event = false ;
}
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 ) ;
}
// 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 ;
}
if ( EventDispatcher : : display_sleep ) {
// Swallow event, wake up display.
if ( switches_state . any ( ) ) {
set_display_sleep ( false ) ;
}
return ;
}
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 ) ;
}
}
in_key_event = true ;
}
}
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : handle_encoder ( ) {
2023-05-18 16:16:05 -04: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-10 00:32:58 -05:00
if ( delta = = 0 )
return ;
2023-05-18 16:16:05 -04:00
encoder_last = encoder_now ;
const auto event = static_cast < ui : : EncoderEvent > ( delta ) ;
event_bubble_encoder ( event ) ;
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : handle_touch ( ) {
2023-05-18 16:16:05 -04:00
portapack : : bl_tick_counter = 0 ;
2020-05-09 10:23:22 -04:00
2023-05-18 16:16:05 -04:00
touch_manager . feed ( get_touch_frame ( ) ) ;
2016-01-13 01:20:13 -05:00
}
bool EventDispatcher : : event_bubble_key ( const ui : : KeyEvent event ) {
2023-05-18 16:16:05 -04:00
auto target = context . focus_manager ( ) . focus_widget ( ) ;
while ( ( target ! = nullptr ) & & ! target - > on_key ( event ) ) {
target = target - > parent ( ) ;
}
2016-01-13 01:20:13 -05:00
2023-05-18 16:16:05 -04:00
/* Return true if event was consumed. */
return ( target ! = nullptr ) ;
2016-01-13 01:20:13 -05:00
}
void EventDispatcher : : event_bubble_encoder ( const ui : : EncoderEvent event ) {
2023-05-18 16:16:05 -04:00
auto target = context . focus_manager ( ) . focus_widget ( ) ;
while ( ( target ! = nullptr ) & & ! target - > on_encoder ( event ) ) {
target = target - > parent ( ) ;
}
2016-02-16 14:23:02 -05:00
}
void EventDispatcher : : init_message_queues ( ) {
2023-05-18 16:16:05 -04:00
new ( & shared_memory ) SharedMemory ;
2016-02-16 14:23:02 -05:00
}
2016-05-12 18:24:08 -04:00
MessageHandlerRegistration : : MessageHandlerRegistration (
2023-05-18 16:16:05 -04: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 18:24:08 -04:00
}
2016-05-09 15:16:24 -04:00
2016-05-12 18:24:08 -04:00
MessageHandlerRegistration : : ~ MessageHandlerRegistration ( ) {
2023-05-18 16:16:05 -04:00
message_map . unregister_handler ( message_id ) ;
2016-02-16 14:23:02 -05:00
}