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