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