]> git.tdb.fi Git - libs/demoscene.git/blob - source/launcher.cpp
Initial files lifted from the Skrolliparty 2 demo
[libs/demoscene.git] / source / launcher.cpp
1 #include <cmath>
2 #include <msp/core/getopt.h>
3 #include <msp/input/keys.h>
4 #include <msp/io/console.h>
5 #include <msp/io/file.h>
6 #include <msp/strings/regex.h>
7 #include <msp/time/utils.h>
8 #include "demo.h"
9 #include "launcher.h"
10 #include "launchscreen.h"
11
12 using namespace std;
13 using namespace Msp;
14
15 LauncherBase::Options::Options(Graphics::Display &dpy, int argc, char **argv):
16         start_fullscreen(-1),
17         framerate(0),
18         no_music(false),
19         no_vsync(false)
20 {
21         unsigned fullscreen = 0;
22         string fullscreen_size;
23         unsigned windowed = 0;
24         string windowed_size;
25         float seek_seconds = 0.0f;
26
27         GetOpt getopt;
28         getopt.add_option('f', "fullscreen", fullscreen_size, GetOpt::OPTIONAL_ARG).bind_seen_count(fullscreen).set_help("Start in fullscreen mode", "SIZE");
29         getopt.add_option('w', "windowed", windowed_size, GetOpt::OPTIONAL_ARG).bind_seen_count(windowed).set_help("Start in windowed mode without delay", "SIZE");
30 #ifdef WITH_DEVELOPER
31         getopt.add_option('s', "seek", seek_seconds, GetOpt::REQUIRED_ARG).set_help("Start at TIME seconds", "TIME");
32         getopt.add_option("record-frames", frame_dump_fn, GetOpt::REQUIRED_ARG).set_help("Record raw frame data to FILE", "FILE");
33 #endif
34         getopt.add_option("no-music", no_music, GetOpt::NO_ARG).set_help("Disable music playback");
35         getopt.add_option("fps", framerate, GetOpt::REQUIRED_ARG).set_help("Run at NUM fps", "NUM");
36         getopt.add_option("no-vsync", no_vsync, GetOpt::NO_ARG).set_help("Disable vertical sync");
37         getopt(argc, argv);
38
39         if(fullscreen && windowed)
40                 throw usage_error("--fullscreen and --windowed are exclusive");
41
42         if(fullscreen)
43                 start_fullscreen = 1;
44         else if(windowed)
45                 start_fullscreen = 0;
46
47         const Graphics::VideoMode &desktop_mode = dpy.get_desktop_mode();
48         if(!windowed_size.empty())
49                 parse_size(windowed_size, win_opts);
50         else if(desktop_mode.width>1920 && desktop_mode.height>1080)
51         {
52                 win_opts.width = 1920;
53                 win_opts.height = 1080;
54         }
55         else
56         {
57                 win_opts.width = 1280;
58                 win_opts.height = 720;
59         }
60
61         if(!fullscreen_size.empty())
62                 parse_size(fullscreen_size, fullscreen_opts);
63         else
64         {
65                 fullscreen_opts.width = desktop_mode.width;
66                 fullscreen_opts.height = desktop_mode.height;
67         }
68         fullscreen_opts.fullscreen = true;
69         fullscreen_opts.fullscreen_monitor = desktop_mode.monitor;
70         fullscreen_opts.fullscreen_exclusive = false;
71
72         seek = seek_seconds*Time::sec;
73 }
74
75 void LauncherBase::Options::parse_size(const string &size, Graphics::WindowOptions &win_opts)
76 {
77         static Regex r_size("^([0-9]+)x([0-9]+)$");
78         RegMatch m = r_size.match(size);
79         if(!m)
80                 throw invalid_argument("Options::parse_size");
81
82         win_opts.width = lexical_cast<unsigned>(m[1].str);
83         win_opts.height = lexical_cast<unsigned>(m[2].str);
84 }
85
86
87 LauncherBase::LauncherBase(int argc, char **argv):
88         options(display, argc, argv),
89         window(display, options.start_fullscreen>0 ? options.fullscreen_opts : options.win_opts),
90         gl_context(window),
91         keyboard(window),
92         al_device(options.no_music ? 0 : new AL::Device),
93         al_context(options.no_music ? 0 : new AL::Context(*al_device)),
94         launch_screen(0),
95         demo(0),
96         frame_dump(0),
97         frame_size(0),
98         frame_dump_buffer(0)
99 {
100         gl_context.set_swap_interval(options.no_vsync ? 0 : 1);
101         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &LauncherBase::exit), 0));
102         keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &LauncherBase::key_press), false));
103 }
104
105 LauncherBase::~LauncherBase()
106 {
107         delete launch_screen;
108         delete demo;
109         delete[] frame_dump_buffer;
110         delete al_context;
111         delete al_device;
112 }
113
114 void LauncherBase::start()
115 {
116         window.show();
117         if(options.start_fullscreen>=0)
118                 start_demo(options.start_fullscreen);
119         else
120         {
121                 launch_screen = new LaunchScreen(get_resources());
122                 timeout = Time::now()+5*Time::sec;
123         }
124 }
125
126 void LauncherBase::tick()
127 {
128         if(demo)
129                 tick_demo();
130         else if(launch_screen)
131                 tick_launch_screen();
132 }
133
134 void LauncherBase::tick_demo()
135 {
136         Time::TimeStamp t = Time::now();
137         if(timeout)
138         {
139                 if(t<timeout)
140                         return;
141                 timeout = Time::TimeStamp();
142         }
143
144         if(!options.no_vsync && t<demo->get_next_frame_time())
145                 return;
146
147         display.tick();
148         demo->tick();
149
150         if(frame_dump)
151         {
152                 glReadPixels(0, 0, window.get_width(), window.get_height(), GL_RGB, GL_UNSIGNED_BYTE, frame_dump_buffer);
153                 frame_dump->write(frame_dump_buffer, frame_size);
154         }
155 }
156
157 void LauncherBase::tick_launch_screen()
158 {
159         Time::TimeStamp t = Time::now();
160         Time::TimeDelta time_left = timeout-t;
161         if(time_left>Time::zero)
162                 launch_screen->set_countdown(ceil(time_left/Time::sec));
163         else
164                 start_demo(true);
165
166         display.tick();
167         launch_screen->render();
168         gl_context.swap_buffers();
169
170         if(demo)
171         {
172                 delete launch_screen;
173                 launch_screen = 0;
174         }
175 }
176
177 void LauncherBase::start_demo(bool fullscreen)
178 {
179         if(fullscreen)
180         {
181                 window.reconfigure(options.fullscreen_opts);
182                 window.show_cursor(false);
183                 timeout = Time::now()+Time::sec;
184         }
185         else
186                 timeout = Time::TimeStamp();
187
188         demo = create_demo();
189         if(!options.no_music)
190                 demo->enable_music();
191         if(options.framerate)
192                 demo->set_fixed_framerate(options.framerate);
193         if(options.seek)
194                 demo->seek(options.seek);
195         demo->signal_finished.connect(sigc::bind(sigc::mem_fun(this, &LauncherBase::exit), 0));
196
197         if(!options.frame_dump_fn.empty())
198         {
199                 if(options.frame_dump_fn=="-")
200                         frame_dump = &IO::cout;
201                 else
202                         frame_dump = new IO::File(options.frame_dump_fn, IO::M_WRITE);
203                 frame_size = window.get_width()*window.get_height()*3;
204                 frame_dump_buffer = new char[frame_size];
205         }
206 }
207
208 void LauncherBase::key_press(unsigned key)
209 {
210         if(key==Input::KEY_ENTER && !demo)
211                 start_demo(false);
212         else if(key==Input::KEY_ESC)
213                 exit(0);
214 }