]> git.tdb.fi Git - libs/core.git/blob - source/core/application.cpp
MSVC compatibility fixes
[libs/core.git] / source / core / application.cpp
1 /* $Id$
2
3 This file is part of libmspcore
4 Copyright © 2006 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <signal.h>
9 #include <iostream>
10 #include <typeinfo>
11 #include "../debug/backtrace.h"
12 #include "../debug/demangle.h"
13 #include "../time/units.h"
14 #include "../time/utils.h"
15 #include "application.h"
16 #include "except.h"
17
18 using namespace std;
19
20 namespace Msp {
21
22 /**
23 Constructs an instance of the registered application class and runs it.  If the
24 application throws a UsageError, the static usage() function is called.
25
26 This function can only be called once.  The global main() function provided by
27 the library normally does it automatically at program startup.
28 */
29 int Application::run(int argc, char **argv, void *data)
30 {
31         static bool called=false;
32         if(called)
33         {
34                 cerr<<"Trying to call Application::run_app twice!\n";
35                 return 125;
36         }
37         called=true;
38
39         if(!reg_app_)
40         {
41                 cerr<<"Trying to run with no application class registered!\n";
42                 return 126;
43         }
44
45         data_=data;
46
47         try
48         {
49                 try
50                 {
51                         app_=reg_app_->create_app(argc, argv);
52                 }
53                 catch(const UsageError &e)
54                 {
55                         reg_app_->usage(e.what(), argv[0], e.get_brief());
56                         return 1;
57                 }
58
59                 int result=app_->main();
60                 delete app_;
61                 return result;
62         }
63         catch(const exception &e)
64         {
65                 delete app_;
66
67 #ifdef WIN32
68                 string msg=Debug::demangle(typeid(e).name())+":\n"+e.what();
69                 MessageBoxA(0, msg.c_str(), "Uncaught exception", MB_OK|MB_ICONERROR);
70 #else
71                 cerr<<"An uncaught exception occurred.\n";
72                 cerr<<"  type:   "<<Debug::demangle(typeid(e).name())<<'\n';
73                 cerr<<"  what(): "<<e.what()<<'\n';
74
75                 const Exception *exc=dynamic_cast<const Exception *>(&e);
76                 if(exc && !exc->get_backtrace().get_frames().empty())
77                 {
78                         cerr<<"  backtrace:\n";
79                         const Debug::Backtrace::FrameSeq &frames=exc->get_backtrace().get_frames();
80                         for(Debug::Backtrace::FrameSeq::const_iterator i=frames.begin(); i!=frames.end(); ++i)
81                                 cerr<<"    "<<*i<<'\n';
82                 }
83 #endif
84
85                 return 124;
86         }
87 }
88
89 /**
90 Prints a message describing the usage of the application.  The default version
91 will blame the programmer for being lazy.
92
93 @param   reason  Why the function was called
94 @param   argv0   The value of argv[0], to be used in the message
95 @param   brief   Whether to print a brief or long usage message
96 */
97 void Application::usage(const char *reason, const char *, bool)
98 {
99         if(reason)
100                 cerr<<"UsageError: "<<reason<<'\n';
101         cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n";
102 }
103
104 Application::Application():
105         exit_code(0),
106         loop_mode_(TICK_SLEEP)
107 { }
108
109 /**
110 Default main loop.  Behavior depends on loop_mode_.  A custom main loop should
111 monitor the done member variable and return exit_code.
112 */
113 int Application::main()
114 {
115         if(loop_mode_==NONE)
116                 return 0;
117
118         done=false;
119         while(!done)
120         {
121                 if(loop_mode_==SLEEP)
122                 {
123                         sleep_sem_.wait();
124                         if(!done)
125                                 tick();
126                 }
127                 else if(loop_mode_==TICK_SLEEP)
128                 {
129                         tick();
130                         Time::sleep(Time::msec);
131                 }
132                 else if(loop_mode_==TICK_YIELD)
133                 {
134                         tick();
135 #ifdef WIN32
136                         Sleep(0);
137 #else
138                         sched_yield();
139 #endif
140                 }
141                 else if(loop_mode_==TICK_BUSY)
142                         tick();
143         }
144
145         return exit_code;
146 }
147
148 /**
149 Sets the specified signal to be delivered to the sighandler member function.
150 */
151 void Application::catch_signal(int s)
152 {
153         signal(s, &sighandler_);
154 }
155
156 /**
157 Changes the main loop mode.
158 */
159 void Application::set_loop_mode(LoopMode l)
160 {
161         LoopMode old_mode=loop_mode_;
162         loop_mode_=l;
163         if(old_mode==SLEEP)
164                 sleep_sem_.signal();
165 }
166
167 /**
168 Causes the tick() function to be executed once if loop mode is SLEEP.  Has no
169 effect with other loop modes.
170 */
171 void Application::induce_tick()
172 {
173         if(loop_mode_==SLEEP)
174                 sleep_sem_.signal();
175 }
176
177 /**
178 Causes the application to exit gracefully with the given exit code.
179 */
180 void Application::exit(int c)
181 {
182         done=true;
183         exit_code=c;
184         if(loop_mode_==SLEEP)
185                 sleep_sem_.signal();
186 }
187
188 /**
189 Static wrapper function to call a member function of the Application instance.
190 */
191 void Application::sighandler_(int s)
192 {
193         app_->sighandler(s);
194 }
195
196 Application::RegBase::RegBase()
197 {
198         if(reg_app_)
199         {
200                 cerr<<"Warning: registering the application twice\n";
201                 delete reg_app_;
202         }
203
204         reg_app_=this;
205 }
206
207 Application *Application::app_=0;
208 Application::RegBase *Application::reg_app_=0;
209 void *Application::data_=0;
210
211 } // namespace Msp