mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[escher] Add RunLoop and Timer
Change-Id: Icb5b2e82cc9fe999eb4b1c7e9dff75ca92dcca43
This commit is contained in:
@@ -57,15 +57,16 @@ VariableBoxController * AppsContainer::variableBoxController() {
|
||||
return &m_variableBoxController;
|
||||
}
|
||||
|
||||
bool AppsContainer::handleEvent(Ion::Events::Event event) {
|
||||
void AppsContainer::dispatchEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Home) {
|
||||
switchTo(appAtIndex(0));
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if (event == Ion::Events::OnOff) {
|
||||
Ion::Power::suspend();
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
Container::dispatchEvent(event);
|
||||
}
|
||||
|
||||
void AppsContainer::switchTo(App * app) {
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
Poincare::Context * globalContext();
|
||||
MathToolbox * mathToolbox();
|
||||
VariableBoxController * variableBoxController();
|
||||
bool handleEvent(Ion::Events::Event event) override;
|
||||
void dispatchEvent(Ion::Events::Event event) override;
|
||||
void switchTo(App * app) override;
|
||||
void refreshPreferences();
|
||||
private:
|
||||
|
||||
@@ -36,6 +36,7 @@ objs += $(addprefix escher/src/,\
|
||||
pointer_table_cell_with_switch.o\
|
||||
pointer_text_view.o\
|
||||
responder.o\
|
||||
run_loop.o\
|
||||
scroll_view.o\
|
||||
scroll_view_indicator.o\
|
||||
selectable_table_view.o\
|
||||
@@ -55,6 +56,7 @@ objs += $(addprefix escher/src/,\
|
||||
text_view.o\
|
||||
tiled_view.o\
|
||||
toolbox.o\
|
||||
timer.o\
|
||||
view.o\
|
||||
view_controller.o\
|
||||
warning_controller.o\
|
||||
|
||||
@@ -10,16 +10,17 @@
|
||||
* When writing an Escher program, you typically subclass Container, and your
|
||||
* subclass owns one or more App. You then call "run()" on your container. */
|
||||
|
||||
#include <escher/run_loop.h>
|
||||
#include <escher/app.h>
|
||||
#include <escher/window.h>
|
||||
#include <ion/events.h>
|
||||
|
||||
class Container {
|
||||
class Container : private RunLoop {
|
||||
public:
|
||||
Container();
|
||||
void run();
|
||||
App * activeApp();
|
||||
virtual bool handleEvent(Ion::Events::Event event);
|
||||
virtual void dispatchEvent(Ion::Events::Event event) override;
|
||||
virtual void switchTo(App * app);
|
||||
protected:
|
||||
virtual Window * window() = 0;
|
||||
|
||||
20
escher/include/escher/run_loop.h
Normal file
20
escher/include/escher/run_loop.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef ESCHER_RUN_LOOP_H
|
||||
#define ESCHER_RUN_LOOP_H
|
||||
|
||||
#include <ion.h>
|
||||
#include <escher/timer.h>
|
||||
|
||||
class RunLoop {
|
||||
public:
|
||||
RunLoop();
|
||||
void run();
|
||||
protected:
|
||||
virtual void dispatchEvent(Ion::Events::Event e) = 0;
|
||||
virtual int numberOfTimers();
|
||||
virtual Timer * timerAtIndex(int i);
|
||||
private:
|
||||
void step();
|
||||
int m_time;
|
||||
};
|
||||
|
||||
#endif
|
||||
29
escher/include/escher/timer.h
Normal file
29
escher/include/escher/timer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef ESCHER_TIMER_H
|
||||
#define ESCHER_TIMER_H
|
||||
|
||||
#include <escher/invocation.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Timers we'll need
|
||||
* - Blink cursor timer
|
||||
* - Dim Screen timer
|
||||
* - Power down timer
|
||||
* - Watchdog timer ?
|
||||
* - Battery level timer
|
||||
* - LED blink timer
|
||||
*/
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
static constexpr int TickDuration = 100; // In Miliseconds
|
||||
Timer(Invocation invocation, uint32_t period); // Period is in ticks
|
||||
void tick();
|
||||
void reset();
|
||||
private:
|
||||
void fire();
|
||||
Invocation m_invocation;
|
||||
uint32_t m_period;
|
||||
uint32_t m_numberOfTicksBeforeFire;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,8 @@
|
||||
#include <escher/container.h>
|
||||
#include <ion.h>
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
Container::Container() :
|
||||
RunLoop(),
|
||||
m_activeApp(nullptr)
|
||||
{
|
||||
}
|
||||
@@ -26,36 +24,26 @@ App * Container::activeApp() {
|
||||
return m_activeApp;
|
||||
}
|
||||
|
||||
bool Container::handleEvent(Ion::Events::Event event) {
|
||||
return false;
|
||||
void Container::dispatchEvent(Ion::Events::Event event) {
|
||||
#if ESCHER_LOG_EVENTS_BINARY
|
||||
char message[2] = { (char)event.id(), 0};
|
||||
ion_log_string(message);
|
||||
#endif
|
||||
#if ESCHER_LOG_EVENTS_NAME
|
||||
const char * name = event.name();
|
||||
if (name == nullptr) {
|
||||
name = "UNDEFINED";
|
||||
}
|
||||
ion_log_string("Ion::Events::");
|
||||
ion_log_string(name);
|
||||
ion_log_string("\n");
|
||||
#endif
|
||||
m_activeApp->processEvent(event);
|
||||
window()->redraw();
|
||||
}
|
||||
|
||||
void Container::run() {
|
||||
window()->setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height));
|
||||
window()->redraw();
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_set_main_loop_arg([](void * ctx){ ((Container *)ctx)->step(); }, this, 0, 1);
|
||||
#else
|
||||
while (true) {
|
||||
step();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Container::step() {
|
||||
Ion::Events::Event event = Ion::Events::getEvent(); // This is a blocking call
|
||||
#if ESCHER_LOG_EVENTS
|
||||
char message[2] = { (char)event.id(), 0};
|
||||
Ion::Log::print(message);
|
||||
#endif
|
||||
|
||||
if (event == Ion::Events::None) {
|
||||
return;
|
||||
}
|
||||
if (handleEvent(event)) {
|
||||
return;
|
||||
}
|
||||
m_activeApp->processEvent(event);
|
||||
window()->redraw();
|
||||
RunLoop::run();
|
||||
}
|
||||
|
||||
72
escher/src/run_loop.cpp
Normal file
72
escher/src/run_loop.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <escher/run_loop.h>
|
||||
#include <assert.h>
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
RunLoop::RunLoop() :
|
||||
m_time(0) {
|
||||
}
|
||||
|
||||
int RunLoop::numberOfTimers() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Timer * RunLoop::timerAtIndex(int i) {
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RunLoop::run() {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
emscripten_set_main_loop_arg([](void * ctx){ ((Container *)ctx)->step(); }, this, 0, 1);
|
||||
#else
|
||||
while (true) {
|
||||
step();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RunLoop::step() {
|
||||
// Fetch the event, if any
|
||||
int eventDuration = Timer::TickDuration;
|
||||
int timeout = eventDuration;
|
||||
Ion::Events::Event event = Ion::Events::getEvent(&timeout);
|
||||
eventDuration -= timeout;
|
||||
|
||||
assert(eventDuration >= 0);
|
||||
assert(eventDuration <= Timer::TickDuration);
|
||||
|
||||
/* At this point, eventDuration contains the time it took to retrieve the
|
||||
* event. It can be zero, and is at most equal to the timeout value, Timer::
|
||||
* TickDuration. The event returned can be None if nothing worth taking care
|
||||
* of happened. In other words, getEvent is a blocking call with a timeout. */
|
||||
|
||||
m_time += eventDuration;
|
||||
|
||||
if (m_time >= Timer::TickDuration) {
|
||||
m_time -= Timer::TickDuration;
|
||||
for (int i=0; i<numberOfTimers(); i++) {
|
||||
Timer * timer = timerAtIndex(i);
|
||||
timer->tick();
|
||||
}
|
||||
}
|
||||
|
||||
#if ESCHER_LOG_EVENTS_BINARY
|
||||
char message[2] = { (char)event.id(), 0};
|
||||
ion_log_string(message);
|
||||
#endif
|
||||
#if ESCHER_LOG_EVENTS_NAME
|
||||
const char * name = event.name();
|
||||
if (name == nullptr) {
|
||||
name = "UNDEFINED";
|
||||
}
|
||||
ion_log_string("Ion::Events::");
|
||||
ion_log_string(name);
|
||||
ion_log_string("\n");
|
||||
#endif
|
||||
|
||||
if (event != Ion::Events::None) {
|
||||
dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
24
escher/src/timer.cpp
Normal file
24
escher/src/timer.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include <escher/timer.h>
|
||||
|
||||
Timer::Timer(Invocation invocation, uint32_t period) :
|
||||
m_invocation(invocation),
|
||||
m_period(period),
|
||||
m_numberOfTicksBeforeFire(period)
|
||||
{
|
||||
}
|
||||
|
||||
void Timer::tick() {
|
||||
m_numberOfTicksBeforeFire--;
|
||||
if (m_numberOfTicksBeforeFire == 0) {
|
||||
fire();
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::reset() {
|
||||
m_numberOfTicksBeforeFire = m_period;
|
||||
}
|
||||
|
||||
void Timer::fire() {
|
||||
m_invocation.perform(this);
|
||||
}
|
||||
@@ -34,7 +34,8 @@ private:
|
||||
uint8_t m_id;
|
||||
};
|
||||
|
||||
Event getEvent();
|
||||
// Timeout is decremented
|
||||
Event getEvent(int * timeout);
|
||||
|
||||
bool isShiftActive();
|
||||
bool isAlphaActive();
|
||||
|
||||
@@ -38,8 +38,20 @@ void updateModifiersFromEvent(Event e) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool sleepWithTimeout(int duration, int * timeout) {
|
||||
if (*timeout >= duration) {
|
||||
msleep(duration);
|
||||
*timeout -= duration;
|
||||
return false;
|
||||
} else {
|
||||
msleep(*timeout);
|
||||
*timeout = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Debouncing, and change to get_key event.
|
||||
Event getEvent() {
|
||||
Event getEvent(int * timeout) {
|
||||
// Let's start by saving which keys we've seen up
|
||||
bool keySeenUp[Keyboard::NumberOfKeys];
|
||||
for (int k=0; k<Keyboard::NumberOfKeys; k++) {
|
||||
@@ -47,7 +59,9 @@ Event getEvent() {
|
||||
}
|
||||
|
||||
// Wait a little to debounce the button.
|
||||
msleep(10);
|
||||
if (sleepWithTimeout(10, timeout)) {
|
||||
return Ion::Events::None;
|
||||
}
|
||||
|
||||
/* Let's discard the keys we previously saw up but which aren't anymore: those
|
||||
* were probably bouncing! */
|
||||
@@ -55,7 +69,7 @@ Event getEvent() {
|
||||
keySeenUp[k] &= !Keyboard::keyDown((Ion::Keyboard::Key)k);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
for (int k=0; k<Keyboard::NumberOfKeys; k++) {
|
||||
if (Keyboard::keyDown((Ion::Keyboard::Key)k)) {
|
||||
if (keySeenUp[k]) {
|
||||
@@ -67,7 +81,9 @@ Event getEvent() {
|
||||
keySeenUp[k] = true;
|
||||
}
|
||||
}
|
||||
msleep(10);
|
||||
if (sleepWithTimeout(10, timeout)) {
|
||||
return Ion::Events::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user