Firmware2/Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/extended/event_loop.cpp
2021-05-15 15:05:53 -05:00

227 lines
7.1 KiB
C++

/******************
* event_loop.cpp *
******************/
/****************************************************************************
* Written By Mark Pelletier 2017 - Aleph Objects, Inc. *
* Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
* *
* 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 3 of the License, 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. *
* *
* To view a copy of the GNU General Public License, go to the following *
* location: <https://www.gnu.org/licenses/>. *
****************************************************************************/
#include "ftdi_extended.h"
#if ENABLED(FTDI_EXTENDED)
using namespace FTDI;
enum {
UNPRESSED = 0x00
};
tiny_timer_t touch_timer;
UIData::flags_t UIData::flags;
uint8_t pressed_tag = UNPRESSED;
uint8_t UIData::get_persistent_data_mask() {
// A bit mask for flags that should be stored to the EEPROM.
// Others are considered temporarily values that need not be
// saved.
constexpr flags_t persistent_flags = {
bits: {
touch_start_sound: true,
touch_end_sound: true,
touch_repeat_sound: true,
show_animations: true
}
};
return persistent_flags.value;
}
void UIData::reset_persistent_data() {
// Default values for persistent data
constexpr flags_t default_flags = {
bits: {
touch_start_sound: true,
touch_end_sound: true,
touch_repeat_sound: true,
show_animations: true,
touch_debouncing: false,
ignore_unpress: false
}
};
flags.value = default_flags.value;
}
uint8_t UIData::get_persistent_data() {
return flags.value & get_persistent_data_mask();
}
void UIData::set_persistent_data(uint8_t value) {
flags.value = value & get_persistent_data_mask();
}
void UIData::enable_touch_sounds(bool enabled) {
UIData::flags.bits.touch_start_sound = enabled;
UIData::flags.bits.touch_end_sound = enabled;
UIData::flags.bits.touch_repeat_sound = enabled;
}
bool UIData::touch_sounds_enabled() {
return UIData::flags.bits.touch_start_sound || UIData::flags.bits.touch_end_sound || UIData::flags.bits.touch_repeat_sound;
}
void UIData::enable_animations(bool enabled) {
UIData::flags.bits.show_animations = enabled;
}
bool UIData::animations_enabled() {
return UIData::flags.bits.show_animations;
}
namespace FTDI {
uint8_t EventLoop::get_pressed_tag() {
return pressed_tag;
}
bool EventLoop::is_touch_held() {
return pressed_tag != 0;
}
/**
* process_events(): Process events from the touch panel.
*
* This function consists of a state machine that accomplishes the following:
*
* - Reads the tag register from the touch panel
* - Dispatches onTouchStart and onTouchEnd events to the active screen.
* - Handles auto-repetition by sending onTouchHeld to the active screen periodically.
* - Plays touch feedback "click" sounds when appropriate.
* - Performs debouncing to supress spurious touch events.
*/
void EventLoop::process_events() {
// If the LCD is processing commands, don't check
// for tags since they may be changing and could
// cause spurious events.
if (!touch_timer.elapsed(TOUCH_UPDATE_INTERVAL) || CLCD::CommandFifo::is_processing()) {
return;
}
const uint8_t tag = CLCD::get_tag();
switch (pressed_tag) {
case UNPRESSED:
if (tag != 0) {
#if ENABLED(TOUCH_UI_DEBUG)
SERIAL_ECHO_MSG("Touch start: ", tag);
#endif
pressed_tag = tag;
current_screen.onRefresh();
// When the user taps on a button, activate the onTouchStart handler
const uint8_t lastScreen = current_screen.getScreen();
if (current_screen.onTouchStart(tag)) {
touch_timer.start();
if (UIData::flags.bits.touch_start_sound) sound.play(press_sound);
}
// In the case in which a touch event triggered a new screen to be
// drawn, we don't issue a touchEnd since it would be sent to the
// wrong screen.
UIData::flags.bits.ignore_unpress = (lastScreen != current_screen.getScreen());
}
else {
touch_timer.start();
}
break;
default: // PRESSED
if (!UIData::flags.bits.touch_debouncing) {
if (tag == pressed_tag) {
// The user is holding down a button.
if (touch_timer.elapsed(1000 / TOUCH_REPEATS_PER_SECOND)) {
if (current_screen.onTouchHeld(tag)) {
current_screen.onRefresh();
if (UIData::flags.bits.touch_repeat_sound) sound.play(repeat_sound);
}
touch_timer.start();
}
}
else if (tag == 0) {
touch_timer.start();
UIData::flags.bits.touch_debouncing = true;
}
}
else {
// Debouncing...
if (tag == pressed_tag) {
// If while debouncing, we detect a press, then cancel debouncing.
UIData::flags.bits.touch_debouncing = false;
}
else if (touch_timer.elapsed(DEBOUNCE_PERIOD)) {
UIData::flags.bits.touch_debouncing = false;
if (UIData::flags.bits.ignore_unpress) {
UIData::flags.bits.ignore_unpress = false;
pressed_tag = UNPRESSED;
break;
}
if (UIData::flags.bits.touch_end_sound) sound.play(unpress_sound);
#if ENABLED(TOUCH_UI_DEBUG)
SERIAL_ECHO_MSG("Touch end: ", pressed_tag);
#endif
const uint8_t saved_pressed_tag = pressed_tag;
pressed_tag = UNPRESSED;
current_screen.onTouchEnd(saved_pressed_tag);
current_screen.onRefresh();
}
}
break;
} // switch (pressed_tag)
} // processEvents()
void EventLoop::setup() {
CLCD::init();
DLCache::init();
UIData::reset_persistent_data();
current_screen.start();
}
void EventLoop::loop() {
sound.onIdle();
/**
* Guard against re-entry of UI methods, which can
* crash. Re-entry can happen because some functions
* (e.g. planner.synchronize) call idle().
*/
if (!UIData::flags.bits.prevent_reentry) {
UIData::flags.bits.prevent_reentry = true;
current_screen.onIdle();
process_events();
UIData::flags.bits.prevent_reentry = false;
}
}
} // namespace FTDI
#endif // FTDI_EXTENDED