2017-11-14 19:07:10 +00:00
|
|
|
/*! \file apps.c
|
|
|
|
\brief Application manager.
|
|
|
|
|
|
|
|
This module manages the different applications, and the switching
|
|
|
|
between them.
|
|
|
|
*/
|
|
|
|
|
2017-09-26 15:44:10 +00:00
|
|
|
#include <msp430.h>
|
2017-12-03 21:16:41 +00:00
|
|
|
#include <stdio.h>
|
2017-09-26 15:44:10 +00:00
|
|
|
|
2017-11-30 01:20:06 +00:00
|
|
|
#include "api.h"
|
2017-09-26 15:44:10 +00:00
|
|
|
#include "applist.h"
|
|
|
|
|
2017-09-26 16:59:02 +00:00
|
|
|
//! We begin on the clock.
|
2017-11-30 01:20:06 +00:00
|
|
|
static int appindex=DEFAULTAPP, idlecount=0;
|
2017-09-26 17:25:50 +00:00
|
|
|
|
2018-01-28 22:22:54 +00:00
|
|
|
//! The currently selected application.
|
2018-04-21 02:29:16 +00:00
|
|
|
const struct app *applet=&apps[0];
|
2018-01-28 22:22:54 +00:00
|
|
|
|
2017-09-26 17:25:50 +00:00
|
|
|
//! Every 3 minutes we return to the clock unless this is called.
|
|
|
|
void app_cleartimer(){
|
|
|
|
idlecount=0;
|
|
|
|
}
|
2017-09-26 15:44:10 +00:00
|
|
|
|
|
|
|
//! Renders the current app to the screen.
|
2017-09-26 16:59:02 +00:00
|
|
|
void app_draw(){
|
2017-09-26 17:25:50 +00:00
|
|
|
static int lastmin=0;;
|
|
|
|
|
|
|
|
//If we go three minutes without action, return to main screen.
|
|
|
|
if(lastmin!=RTCMIN){
|
|
|
|
lastmin=RTCMIN;
|
|
|
|
idlecount++;
|
|
|
|
}
|
|
|
|
if(idlecount>3){
|
|
|
|
app_cleartimer();
|
2017-09-27 23:09:17 +00:00
|
|
|
app_forcehome();
|
2017-09-26 17:25:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//Call the cap if it exists, or switch to the clock if we're at the
|
|
|
|
//end of the list.
|
2018-01-28 22:22:54 +00:00
|
|
|
if(applet->draw)
|
|
|
|
applet->draw();
|
2017-09-26 15:44:10 +00:00
|
|
|
else
|
2017-09-27 20:10:24 +00:00
|
|
|
app_forcehome();
|
2017-09-26 15:44:10 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-27 20:10:24 +00:00
|
|
|
//! Force return to the home app.
|
|
|
|
void app_forcehome(){
|
2017-11-28 23:26:41 +00:00
|
|
|
//First we try to exit politely.
|
2018-01-28 22:22:54 +00:00
|
|
|
if(applet->exit)
|
|
|
|
applet->exit();
|
2017-11-28 23:26:41 +00:00
|
|
|
|
|
|
|
//And force it if that doesn't work.
|
2018-02-13 22:57:23 +00:00
|
|
|
ucs_slow(); //Gotta drop the clock rate, in case it was high.
|
2017-09-27 20:10:24 +00:00
|
|
|
appindex=0;
|
2018-01-28 22:22:54 +00:00
|
|
|
applet = &apps[appindex];
|
|
|
|
applet->init();
|
2017-09-27 20:10:24 +00:00
|
|
|
}
|
|
|
|
|
2017-12-03 21:16:41 +00:00
|
|
|
//! Initializes the set of applications.
|
2017-09-26 16:59:02 +00:00
|
|
|
void app_init(){
|
2018-01-28 22:22:54 +00:00
|
|
|
if(applet->init)
|
|
|
|
applet->init();
|
|
|
|
else if(!applet->name){
|
2017-09-26 16:59:02 +00:00
|
|
|
appindex=0;
|
2018-01-28 22:22:54 +00:00
|
|
|
applet = &apps[appindex];
|
|
|
|
}
|
2017-12-03 21:16:41 +00:00
|
|
|
|
2017-09-26 16:59:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-28 22:22:54 +00:00
|
|
|
//! Sets an app by a pointer to its structure. Used for submenus.
|
|
|
|
void app_set(const struct app *newapplet){
|
|
|
|
applet=newapplet;
|
|
|
|
app_init();
|
|
|
|
}
|
|
|
|
|
2017-09-27 20:10:24 +00:00
|
|
|
//! Move to the next application if the current allows it.
|
2017-09-26 16:59:02 +00:00
|
|
|
void app_next(){
|
2017-09-27 20:10:24 +00:00
|
|
|
//Clear the 3-minute timer when we switch apps. This is also
|
|
|
|
//cleared by keypresses.
|
2017-09-26 17:25:50 +00:00
|
|
|
app_cleartimer();
|
2017-09-27 20:10:24 +00:00
|
|
|
|
|
|
|
/* First we ask the current app if it will allow the transaction by
|
|
|
|
calling its exit() routine. Zero or a null function pointer allow
|
|
|
|
for the transition, but non-zero indicates that the transition has
|
|
|
|
been cancelled. For example, this is done by the RPN calculator
|
|
|
|
when the item on the stack is not zero.
|
|
|
|
*/
|
2018-01-28 22:22:54 +00:00
|
|
|
|
2017-09-27 20:10:24 +00:00
|
|
|
//Return if there is an exit function and it returns non-zero.
|
2018-01-28 22:22:54 +00:00
|
|
|
if(applet->exit && applet->exit())
|
2017-09-27 20:10:24 +00:00
|
|
|
return;
|
2018-01-28 22:22:54 +00:00
|
|
|
|
|
|
|
applet = &apps[++appindex];
|
|
|
|
if(!applet->draw){
|
2017-09-26 16:59:02 +00:00
|
|
|
appindex=0;
|
2018-01-28 22:22:54 +00:00
|
|
|
applet = &apps[appindex];
|
|
|
|
}
|
2017-09-27 20:10:24 +00:00
|
|
|
|
2017-12-03 21:16:41 +00:00
|
|
|
//Initialize the new application.
|
2018-01-28 22:22:54 +00:00
|
|
|
if(applet->init)
|
|
|
|
applet->init();
|
2018-04-05 19:32:05 +00:00
|
|
|
|
2017-09-26 16:59:02 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-12-07 18:08:54 +00:00
|
|
|
|
|
|
|
//! Provide an incoming packet.
|
|
|
|
void app_packetrx(uint8_t *packet, int len){
|
2017-12-10 17:27:40 +00:00
|
|
|
/* In monitor mode, we forward the packet to the monitor, rather
|
|
|
|
than to the application.
|
|
|
|
*/
|
2017-12-12 22:33:13 +00:00
|
|
|
if(uartactive){
|
2017-12-10 17:27:40 +00:00
|
|
|
monitor_packetrx(packet,len);
|
2017-12-12 22:33:13 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-12-10 17:27:40 +00:00
|
|
|
|
|
|
|
/* Otherwise, we send it to the active application, but only if that
|
|
|
|
application has a handler.
|
|
|
|
*/
|
2018-01-28 22:22:54 +00:00
|
|
|
if(!applet->packetrx){
|
2017-12-07 18:08:54 +00:00
|
|
|
printf("No packet RX handler for %s.",
|
2018-01-28 22:22:54 +00:00
|
|
|
applet->name);
|
2017-12-07 18:08:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-28 22:22:54 +00:00
|
|
|
applet->packetrx(packet,len);
|
2017-12-07 18:08:54 +00:00
|
|
|
}
|
2018-01-27 22:08:55 +00:00
|
|
|
|
2018-04-05 19:32:05 +00:00
|
|
|
//! Callback after sending a packet.
|
|
|
|
void app_packettx(uint8_t *packet, int len){
|
|
|
|
/* We send it to the active application, but only if that
|
|
|
|
application has a handler.
|
|
|
|
*/
|
|
|
|
if(!applet->packetrx){
|
|
|
|
printf("No packet TX handler for %s.",
|
|
|
|
applet->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
applet->packettx();
|
|
|
|
}
|
|
|
|
|
2018-01-27 22:08:55 +00:00
|
|
|
//! Handles a keypress, if a handler is registered.
|
|
|
|
void app_keypress(char ch){
|
2018-02-08 22:21:58 +00:00
|
|
|
//We only pass it to applications that have a handler.
|
|
|
|
if(applet->keypress){
|
|
|
|
if(applet->keypress(ch)){
|
|
|
|
/* Some applets need to visually respond at the moment of their
|
|
|
|
keypress, while others need to be drawn at a predictable
|
|
|
|
framerate. So if--any only if--the keypress() function tells
|
|
|
|
us to do we redraw it.
|
|
|
|
*/
|
|
|
|
lcd_predraw();
|
|
|
|
app_draw();
|
|
|
|
lcd_postdraw();
|
|
|
|
}
|
|
|
|
}
|
2018-01-27 22:08:55 +00:00
|
|
|
}
|