+#include <cmath>
+#include <msp/core/getopt.h>
+#include <msp/input/keys.h>
+#include <msp/io/console.h>
+#include <msp/io/file.h>
+#include <msp/strings/regex.h>
+#include <msp/time/utils.h>
+#include "demo.h"
+#include "launcher.h"
+#include "launchscreen.h"
+
+using namespace std;
+using namespace Msp;
+
+LauncherBase::Options::Options(Graphics::Display &dpy, int argc, char **argv):
+ start_fullscreen(-1),
+ framerate(0),
+ no_music(false),
+ no_vsync(false)
+{
+ unsigned fullscreen = 0;
+ string fullscreen_size;
+ unsigned windowed = 0;
+ string windowed_size;
+ float seek_seconds = 0.0f;
+
+ GetOpt getopt;
+ getopt.add_option('f', "fullscreen", fullscreen_size, GetOpt::OPTIONAL_ARG).bind_seen_count(fullscreen).set_help("Start in fullscreen mode", "SIZE");
+ getopt.add_option('w', "windowed", windowed_size, GetOpt::OPTIONAL_ARG).bind_seen_count(windowed).set_help("Start in windowed mode without delay", "SIZE");
+#ifdef WITH_DEVELOPER
+ getopt.add_option('s', "seek", seek_seconds, GetOpt::REQUIRED_ARG).set_help("Start at TIME seconds", "TIME");
+ getopt.add_option("record-frames", frame_dump_fn, GetOpt::REQUIRED_ARG).set_help("Record raw frame data to FILE", "FILE");
+#endif
+ getopt.add_option("no-music", no_music, GetOpt::NO_ARG).set_help("Disable music playback");
+ getopt.add_option("fps", framerate, GetOpt::REQUIRED_ARG).set_help("Run at NUM fps", "NUM");
+ getopt.add_option("no-vsync", no_vsync, GetOpt::NO_ARG).set_help("Disable vertical sync");
+ getopt(argc, argv);
+
+ if(fullscreen && windowed)
+ throw usage_error("--fullscreen and --windowed are exclusive");
+
+ if(fullscreen)
+ start_fullscreen = 1;
+ else if(windowed)
+ start_fullscreen = 0;
+
+ const Graphics::VideoMode &desktop_mode = dpy.get_desktop_mode();
+ if(!windowed_size.empty())
+ parse_size(windowed_size, win_opts);
+ else if(desktop_mode.width>1920 && desktop_mode.height>1080)
+ {
+ win_opts.width = 1920;
+ win_opts.height = 1080;
+ }
+ else
+ {
+ win_opts.width = 1280;
+ win_opts.height = 720;
+ }
+
+ if(!fullscreen_size.empty())
+ parse_size(fullscreen_size, fullscreen_opts);
+ else
+ {
+ fullscreen_opts.width = desktop_mode.width;
+ fullscreen_opts.height = desktop_mode.height;
+ }
+ fullscreen_opts.fullscreen = true;
+ fullscreen_opts.fullscreen_monitor = desktop_mode.monitor;
+ fullscreen_opts.fullscreen_exclusive = false;
+
+ seek = seek_seconds*Time::sec;
+}
+
+void LauncherBase::Options::parse_size(const string &size, Graphics::WindowOptions &win_opts)
+{
+ static Regex r_size("^([0-9]+)x([0-9]+)$");
+ RegMatch m = r_size.match(size);
+ if(!m)
+ throw invalid_argument("Options::parse_size");
+
+ win_opts.width = lexical_cast<unsigned>(m[1].str);
+ win_opts.height = lexical_cast<unsigned>(m[2].str);
+}
+
+
+LauncherBase::LauncherBase(int argc, char **argv):
+ options(display, argc, argv),
+ window(display, options.start_fullscreen>0 ? options.fullscreen_opts : options.win_opts),
+ gl_context(window),
+ keyboard(window),
+ al_device(options.no_music ? 0 : new AL::Device),
+ al_context(options.no_music ? 0 : new AL::Context(*al_device)),
+ launch_screen(0),
+ demo(0),
+ frame_dump(0),
+ frame_size(0),
+ frame_dump_buffer(0)
+{
+ gl_context.set_swap_interval(options.no_vsync ? 0 : 1);
+ window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &LauncherBase::exit), 0));
+ keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &LauncherBase::key_press), false));
+}
+
+LauncherBase::~LauncherBase()
+{
+ delete launch_screen;
+ delete demo;
+ delete[] frame_dump_buffer;
+ delete al_context;
+ delete al_device;
+}
+
+void LauncherBase::start()
+{
+ window.show();
+ if(options.start_fullscreen>=0)
+ start_demo(options.start_fullscreen);
+ else
+ {
+ launch_screen = new LaunchScreen(get_resources());
+ timeout = Time::now()+5*Time::sec;
+ }
+}
+
+void LauncherBase::tick()
+{
+ if(demo)
+ tick_demo();
+ else if(launch_screen)
+ tick_launch_screen();
+}
+
+void LauncherBase::tick_demo()
+{
+ Time::TimeStamp t = Time::now();
+ if(timeout)
+ {
+ if(t<timeout)
+ return;
+ timeout = Time::TimeStamp();
+ }
+
+ if(!options.no_vsync && t<demo->get_next_frame_time())
+ return;
+
+ display.tick();
+ demo->tick();
+
+ if(frame_dump)
+ {
+ glReadPixels(0, 0, window.get_width(), window.get_height(), GL_RGB, GL_UNSIGNED_BYTE, frame_dump_buffer);
+ frame_dump->write(frame_dump_buffer, frame_size);
+ }
+}
+
+void LauncherBase::tick_launch_screen()
+{
+ Time::TimeStamp t = Time::now();
+ Time::TimeDelta time_left = timeout-t;
+ if(time_left>Time::zero)
+ launch_screen->set_countdown(ceil(time_left/Time::sec));
+ else
+ start_demo(true);
+
+ display.tick();
+ launch_screen->render();
+ gl_context.swap_buffers();
+
+ if(demo)
+ {
+ delete launch_screen;
+ launch_screen = 0;
+ }
+}
+
+void LauncherBase::start_demo(bool fullscreen)
+{
+ if(fullscreen)
+ {
+ window.reconfigure(options.fullscreen_opts);
+ window.show_cursor(false);
+ timeout = Time::now()+Time::sec;
+ }
+ else
+ timeout = Time::TimeStamp();
+
+ demo = create_demo();
+ if(!options.no_music)
+ demo->enable_music();
+ if(options.framerate)
+ demo->set_fixed_framerate(options.framerate);
+ if(options.seek)
+ demo->seek(options.seek);
+ demo->signal_finished.connect(sigc::bind(sigc::mem_fun(this, &LauncherBase::exit), 0));
+
+ if(!options.frame_dump_fn.empty())
+ {
+ if(options.frame_dump_fn=="-")
+ frame_dump = &IO::cout;
+ else
+ frame_dump = new IO::File(options.frame_dump_fn, IO::M_WRITE);
+ frame_size = window.get_width()*window.get_height()*3;
+ frame_dump_buffer = new char[frame_size];
+ }
+}
+
+void LauncherBase::key_press(unsigned key)
+{
+ if(key==Input::KEY_ENTER && !demo)
+ start_demo(false);
+ else if(key==Input::KEY_ESC)
+ exit(0);
+}