/******************************************************************************!
* \file mpssaver.cpp
* \author Sebastien Beaugrand
* \sa http://beaugrand.chez.com/
* \copyright CeCILL 2.1 Free Software license
******************************************************************************/
#include <filesystem>
#include <variant>
#include <csignal>
#include <stdlib.h>
#include <argp.h>
#include "Input.h"
#include "Output.h"
#include "log.h"
bool gLoop = true;
std::unique_ptr<Input> gInput;
std::unique_ptr<Output> gOutput;
/******************************************************************************!
* \fn signalHandler
******************************************************************************/
void
signalHandler(int signal)
{
if (signal == SIGINT) {
gLoop = false;
gInput->hasEvent = true;
gInput->hasEvent.notify_one();
}
}
/******************************************************************************!
* \namespace state
******************************************************************************/
namespace state {
struct Normal {};
struct Menu {};
struct Date {};
struct Hour {};
}
using State = std::variant<state::Normal,
state::Menu,
state::Date,
state::Hour>;
/******************************************************************************!
* \namespace event
******************************************************************************/
namespace event {
struct Up {};
struct Down {};
struct Left {};
struct Right {};
struct Ok {};
}
using Event = std::variant<event::Up,
event::Down,
event::Left,
event::Right,
event::Ok>;
/******************************************************************************!
* \fn drawDate
******************************************************************************/
State
drawDate(int isDay)
{
char line1[6];
char line2[6];
time_t tOfTheDay;
struct tm* tmOfTheDay;
tOfTheDay = ::time(NULL);
tmOfTheDay = ::localtime(&tOfTheDay);
if (isDay) {
::strftime(line1, sizeof(line1), "%dX%m", tmOfTheDay);
*line2 = '\0';
} else {
::strftime(line2, sizeof(line2), "%H:%M", tmOfTheDay);
*line1 = '\0';
}
gOutput->write("", line1, line2, "");
if (isDay) {
return state::Date{};
} else {
return state::Hour{};
}
}
/******************************************************************************!
* \class Machine
******************************************************************************/
State
onEvent(const state::Normal& state, const event::Up&) {
return state;
}
State
onEvent(const state::Normal& state, const event::Down&) {
return state;
}
State
onEvent(const state::Normal& state, const event::Left&) {
return state;
}
State
onEvent(const state::Normal& state, const event::Right&) {
return state;
}
State
onEvent(const state::Normal&, const event::Ok&) {
gOutput->write("",
" reboot",
"date cancel rtc",
" halt");
return state::Menu{};
}
State
onEvent(const state::Menu&, const event::Up&) {
if (::system("sudo /sbin/reboot") == 0) {
gOutput->write("", "reboot", "", "");
}
return state::Normal{};
}
State
onEvent(const state::Menu&, const event::Down&) {
if (::system("sudo /sbin/halt") == 0) {
gOutput->write("", "halt", "", "");
}
return state::Normal{};
}
State
onEvent(const state::Menu&, const event::Left&) {
return drawDate(1);
}
State
onEvent(const state::Menu&, const event::Right&) {
if (::system("sudo /usr/sbin/rtc") == 0) {
}
gOutput->screensaver();
return state::Normal{};
}
State
onEvent(const state::Menu&, const event::Ok&) {
gOutput->screensaver();
return state::Normal{};
}
State
onEvent(const state::Date&, const event::Up&) {
if (::system("sudo /usr/sbin/rtc `date --date='+1 month' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(1);
}
State
onEvent(const state::Date&, const event::Down&) {
if (::system("sudo /usr/sbin/rtc `date --date='-1 month' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(1);
}
State
onEvent(const state::Date&, const event::Left&) {
if (::system("sudo /usr/sbin/rtc `date --date='-1 day' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(1);
}
State
onEvent(const state::Date&, const event::Right&) {
if (::system("sudo /usr/sbin/rtc `date --date='+1 day' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(1);
}
State
onEvent(const state::Date&, const event::Ok&) {
return drawDate(0);
}
State
onEvent(const state::Hour&, const event::Up&) {
if (::system("sudo /usr/sbin/rtc `date --date='+1 hour' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(0);
}
State
onEvent(const state::Hour&, const event::Down&) {
if (::system("sudo /usr/sbin/rtc `date --date='-1 hour' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(0);
}
State
onEvent(const state::Hour&, const event::Left&) {
if (::system("sudo /usr/sbin/rtc `date --date='-1 min' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(0);
}
State
onEvent(const state::Hour&, const event::Right&) {
if (::system("sudo /usr/sbin/rtc `date --date='+1 min' +%FT%Tw%w`;"
" sudo /usr/sbin/rtc") == 0) {
}
return drawDate(0);
}
State
onEvent(const state::Hour&, const event::Ok&) {
gOutput->screensaver();
return state::Normal{};
}
class Machine
{
public:
~Machine() {
std::cout << "Machine::dtor" << std::endl;
}
void processEvent(const Event& event) {
state = std::visit(
[](const auto& st, const auto& ev) { return onEvent(st, ev); },
state, event);
}
State state = state::Normal{};
};
/******************************************************************************!
* argp
******************************************************************************/
const char *argp_program_version =
"mpssaver 1.0.0";
const char *argp_program_bug_address =
"<sbeaugrand@toto.fr>";
static char doc[] =
"mpssaver -- "
"mps screensaver";
static struct argp_option options[] = {
{ "dir", 'd', "DIR", 0, "music_directory", 0 },
{ 0, 0, 0, 0, 0, 0 }
};
struct arguments
{
const char* music_directory;
};
static error_t
parse_opt(int key, char* arg, struct argp_state* state)
{
struct arguments* arguments = static_cast<struct arguments*>(state->input);
switch (key) {
case 'd':
arguments->music_directory = arg;
DEBUG("music_directory: " << arguments->music_directory);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, 0, doc, 0, 0, 0 };
/******************************************************************************!
* \fn main
******************************************************************************/
int
main(int argc, char** argv)
{
struct arguments arguments = { 0 };
argp_parse(&argp, argc, argv, 0, 0, &arguments);
gInput = std::unique_ptr<Input>(new Input);
gOutput = std::unique_ptr<Output>(new Output);
Machine machine;
if (arguments.music_directory) {
gOutput->musicDirectory = arguments.music_directory;
}
gOutput->screensaver();
std::signal(SIGINT, ::signalHandler);
while (gLoop) {
gInput->hasEvent.wait(false);
if (! gLoop) {
return 0;
}
auto key = gInput->key;
gInput->hasEvent = false;
switch (key) {
case Input::KEY_UP:
machine.processEvent(event::Up{});
break;
case Input::KEY_DOWN:
machine.processEvent(event::Down{});
break;
case Input::KEY_LEFT:
machine.processEvent(event::Left{});
break;
case Input::KEY_RIGHT:
machine.processEvent(event::Right{});
break;
case Input::KEY_OK:
machine.processEvent(event::Ok{});
break;
case Input::KEY_SETUP:
case Input::KEY_BACK:
case Input::KEY_UNDEFINED:
break;
}
}
return 0;
}