00001 /* 00002 libwt - Vassilis Virvilis Toolkit - a widget library 00003 Copyright (C) 2006 Vassilis Virvilis <vasvir2@fastmail.fm> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public 00007 License as published by the Free Software Foundation; either 00008 version 2.1 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the 00017 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, SA. 00019 */ 00020 00021 // for Fatal constructor 00022 #include <iostream> 00023 00024 #include <wt/application.h> 00025 #include <wt/trace.h> 00026 #include <wt/rootwindow.h> 00027 #include <wt/color.h> 00028 #include <wt/font.h> 00029 #include <wt/audio.h> 00030 00031 #include "sdlevent.h" 00032 #include "sdltimer.h" 00033 #include "sdlinput.h" 00034 #include "sdlmisc.h" 00035 00036 #include <wt/event.h> 00037 00038 namespace Wt { 00039 00040 Application::EventQueueItem::EventQueueItem(Object *receiver, Event *event) 00041 : receiver(receiver), event(event) {} 00042 00043 bool Application::EventQueueItem::operator==(const EventQueueItem& other) const { 00044 return (other.receiver == receiver && other.event->type() == event->type()); 00045 } 00046 00047 const char * Application::Exception::what() const throw() { 00048 return SDL_GetError(); 00049 } 00050 00051 Application::Application(int argc, char **argv) 00052 : Object(0, "Application::anon"), 00053 Singleton<Application>(this), 00054 exitcode_(0), 00055 running_(true), 00056 width_(Display::defaultWidth), 00057 height_(Display::defaultHeight), 00058 depth_(Display::defaultDepth) { 00059 parseArgs(argc, argv); 00060 Uint32 sdl_flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER; 00061 00062 #if !defined( __WIN32__ ) && !defined( __CYGWIN32__ ) 00063 00064 sdl_flags |= SDL_INIT_EVENTTHREAD; 00065 #endif 00066 00067 /* Initialize SDL step by step*/ 00068 trace("app") << "trying to initialize SDLvideo ... " << std::endl; 00069 if ( SDL_Init(sdl_flags) < 0 ) { 00070 Error("Cannot initialize SDL\n"); 00071 throw Exception(); 00072 } 00073 00074 //init SDLEventHandler 00075 SDLEvent::init(); 00076 //init SDLInput 00077 SDLInput::init(); 00078 //init SDLTimers 00079 SDLTimer::init(); 00080 // initialize audio handling 00081 Audio::init(); 00082 // initialize font handling 00083 Font::init(); 00084 // initialize rootwindow 00085 new RootWindow(width_, height_, depth_); 00086 00087 //parseArgs(argc, argv, other_flags); 00088 00089 trace("app", "Enabling SDL Unicode support\n"); 00090 SDL_EnableUNICODE(1); 00091 00092 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); 00093 } 00094 00095 std::list<std::string>& Application::argv() { 00096 return argv_; 00097 } 00098 00099 void Application::parseArgs(int argc, char** argv) { 00100 enum { 00101 TRACE, XSYNC, WT_WIDTH, WT_HEIGHT, WT_DEPTH, WT_HELP, HELP 00102 }; 00103 00104 class Option { 00105 public: 00106 Option(const std::string& name, const std::string& help) 00107 : name(name), 00108 help(help) {} 00109 std::string name; 00110 std::string help; 00111 }; 00112 00113 Option options[] = { 00114 Option("--wt-trace=", "enables tracing for a comma separated list of subsystems"), 00115 Option("--wt-xsync", "enables X sync to help debugging"), 00116 Option("--wt-width=", "the preffered width of the application"), 00117 Option("--wt-height=", "the preffered height of the application"), 00118 Option("--wt-depth=", "the preffered color depth of the application"), 00119 Option("--wt-help", "display this help and quits"), 00120 Option("--help", "display this help and quits") 00121 }; 00122 00123 const int numopts = sizeof(options) / sizeof(options[0]); 00124 00125 for (int i = 0; i < argc; i++) { 00126 std::string s = argv[i]; 00127 if (s.find(options[TRACE].name) == 0) { 00128 std::string rest = std::string(s, options[TRACE].name.length()); 00129 do { 00130 //look for comma 00131 std::string channel; 00132 int comma_index = rest.find(","); 00133 if (comma_index > 0) { 00134 channel = std::string(rest, 0, comma_index); 00135 rest = std::string(rest, comma_index + 1); 00136 enableTrace(channel); 00137 } else if (comma_index < 0) { 00138 channel = std::string(rest, 0); 00139 rest = ""; 00140 enableTrace(channel); 00141 } else { 00142 // == 0 00143 rest = std::string(rest, comma_index + 1); 00144 } 00145 } while(rest.length()); 00146 } else if (s.find(options[XSYNC].name) == 0) { 00147 trace("app", "Enabling X-sync for debugging\n"); 00148 SDL::enableDebug(); 00149 } else if (s.find(options[WT_WIDTH].name) == 0) { 00150 width_ = boost::lexical_cast<int>(std::string(s, options[WT_WIDTH].name.length())); 00151 } else if (s.find(options[WT_HEIGHT].name) == 0) { 00152 height_ = boost::lexical_cast<int>(std::string(s, options[WT_HEIGHT].name.length())); 00153 } else if (s.find(options[WT_DEPTH].name) == 0) { 00154 depth_ = boost::lexical_cast<int>(std::string(s, options[WT_DEPTH].name.length())); 00155 } else if (s.find(options[WT_HELP].name) == 0 || 00156 s.find(options[HELP].name) == 0) { 00157 std::cout << "Standard wt options are:\n"; 00158 for (int j = 0; j < numopts; j++) { 00159 std::cout << "\t" << options[j].name << 00160 "\t" << options[j].help << std::endl; 00161 } 00162 quit(); 00163 } else { 00164 argv_.push_back(s); 00165 } 00166 } 00167 } 00168 00169 Application::~Application() { 00170 trace("app") << "In application destructor" << std::endl; 00171 00172 aboutToQuit.emit(); 00173 00174 trace("app") << "Doing video shutdown" << std::endl; 00175 00176 delete RootWindow::instance(); 00177 00178 trace("app") << "Quitting SDL" << std::endl; 00179 00180 Font::quit(); 00181 Audio::quit(); 00182 SDLTimer::quit(); 00183 SDLInput::quit(); 00184 SDLEvent::quit(); 00185 // shut down video (to kill the window) before shutting down 00186 // audio, since audio shutdown takes _so_ long 00187 SDL_QuitSubSystem(SDL_INIT_VIDEO); 00188 SDL_Quit(); 00189 } 00190 00191 Widget *Application::mainWidget() { 00192 return static_cast<Widget *>(RootWindow::instance()); 00193 } 00194 00195 void Application::sendPostedEvents() { 00196 Application *app = existingInstance(); 00197 if (!app) 00198 return; 00199 EventQueueList& queue = app->queue; 00200 while (!queue.empty()) { 00201 const EventQueueItem item = queue.front(); 00202 Object *receiver = item.receiver; 00203 Event *e = item.event; 00204 queue.pop_front(); 00205 trace("event") << "Handling " << e << std::endl; 00206 sendEvent(receiver, *e); 00207 delete e; 00208 } 00209 } 00210 00211 void Application::processEvents(Condition* cond) { 00212 condition_stack.push(cond); 00213 while (!(*cond)()) { 00214 if (queue.empty()) 00215 SDLEvent::wait(); 00216 sendPostedEvents(); 00217 } 00218 condition_stack.pop(); 00219 } 00220 00221 class SingleShot : public Application::Condition { 00222 public: 00223 SingleShot() 00224 : count_(0) {} 00225 00226 virtual bool operator()() { 00227 if (count_) 00228 return true; 00229 00230 count_++; 00231 00232 return false; 00233 } 00234 00235 private: 00236 int count_; 00237 }; 00238 00239 void Application::processEvent() { 00240 SingleShot c; 00241 processEvents(&c); 00242 } 00243 00244 class TimerBlock : public Application::Condition { 00245 public: 00246 TimerBlock(int ms) 00247 : ms(ms) { 00248 time.start(); 00249 } 00250 00251 virtual bool operator()() { 00252 return (time.elapsed() > ms); 00253 } 00254 00255 private: 00256 Time time; 00257 int ms; 00258 }; 00259 00260 void Application::processEvents(int ms) { 00261 TimerBlock t(ms); 00262 processEvents(&t); 00263 } 00264 00265 class VarBlock : public Application::Condition { 00266 public: 00267 VarBlock(const bool& var) 00268 : var(var) {} 00269 00270 virtual bool operator()() { 00271 return !var; 00272 } 00273 00274 private: 00275 const bool& var; 00276 }; 00277 00278 void Application::processEvents(const bool& var) { 00279 VarBlock v(var); 00280 processEvents(&v); 00281 } 00282 00283 /// process all events so far until the end of the application 00284 int Application::exec() { 00285 processEvents(running_); 00286 sendPostedEvents(); 00287 return exitcode_; 00288 } 00289 00290 void Application::exit(int exitcode) { 00291 Application *app = existingInstance(); 00292 if (!app) 00293 return; 00294 trace("app") << "Application: was requested to exit." << std::endl; 00295 app->running_ = false; 00296 app->exitcode_ = exitcode; 00297 } 00298 00299 void Application::quit() { 00300 Application::exit(0); 00301 } 00302 00303 bool Application::event(Event *e) { 00304 return filterEvent(e); 00305 } 00306 00307 bool Application::notify(Object *receiver, Event *event) { 00308 if (this->event(event)) 00309 return true; 00310 const int type = event->type(); 00311 Widget *w = dynamic_cast<Widget *>(receiver); 00312 if (w) { 00313 const bool input_event = (type == Event::MouseButtonPress) || 00314 (type == Event::MouseButtonRelease) || 00315 (type == Event::MouseButtonDblClick) || 00316 (type == Event::MouseMove) || 00317 (type == Event::KeyPress) || 00318 (type == Event::KeyRelease) || 00319 (type == Event::Wheel); 00320 00321 if (input_event && !w->enabled) { 00322 return false; 00323 } 00324 return receiver->event(event); 00325 } 00326 00327 return receiver->event(event); 00328 } 00329 00330 bool Application::sendEvent(Object *receiver, Event& event) { 00331 Application *app = existingInstance(); 00332 if (app) 00333 return app->notify(receiver, &event); 00334 else if (receiver) 00335 return receiver->event(&event); 00336 return 0; 00337 } 00338 00339 void Application::postEvent(Object *receiver, Event *event) { 00340 Application *app = existingInstance(); 00341 if (!app) { 00342 sendEvent(receiver, *event); 00343 delete event; 00344 return; 00345 } 00346 EventQueueList& queue = app->queue; 00347 EventQueueItem new_item(receiver, event); 00348 int type = event->type(); 00349 00350 trace("event") << "Posting " << event 00351 << " for " << receiver << std::endl; 00352 00353 // obvious optimization: eliminate unnedded/unprocessed/overwritten events 00354 // let only the last one suvive for move, size, layouthint 00355 // aggregate paint 00356 // also eliminate child events when they are followed by destruction 00357 switch (type) { 00358 case Event::ChildRemoved: { 00359 Object *dead_obj = (static_cast<ChildEvent *>(event))->child(); 00360 for (EventQueueList::iterator I(queue.begin()), 00361 E(queue.end()); I != E;) { 00362 const EventQueueItem& item = *I; 00363 if ((item.receiver == new_item.receiver && 00364 item.event->type() == Event::ChildInserted) || 00365 item.receiver == dead_obj) { 00366 trace("event") << "Eliminating " << item.event << 00367 " for " << static_cast<void *>(item.receiver) << std::endl; 00368 delete item.event; 00369 I = queue.erase(I); 00370 } else { 00371 ++I; 00372 } 00373 } 00374 // we are not going to deliver this sucker 00375 if (!receiver) { 00376 delete event; 00377 return; 00378 } 00379 } 00380 break; 00381 case Event::Paint: { 00382 Region region; 00383 00384 for (EventQueueList::iterator I(queue.begin()), 00385 E(queue.end()); I != E;) { 00386 const EventQueueItem& item = *I; 00387 if(item == new_item) { 00388 PaintEvent *paint_e; 00389 if (region.isEmpty()) { 00390 paint_e = static_cast<PaintEvent *>(new_item.event); 00391 region = paint_e->region(); 00392 } 00393 // aggregate paint events 00394 paint_e = static_cast<PaintEvent *>(item.event); 00395 trace("event") << "Before Aggregation: " << region; 00396 region |= paint_e->region(); 00397 trace("event") << "Aggregate: " << paint_e->region(); 00398 trace("event") << "After Aggregation: " << region; 00399 trace("event") << "Eliminating " 00400 << item.event << " " << static_cast<void *>(item.event) 00401 << " for " << receiver << std::endl; 00402 delete item.event; 00403 I = queue.erase(I); 00404 } else { 00405 ++I; 00406 } 00407 } 00408 00409 if (!region.isEmpty()) { 00410 bool erased = static_cast<PaintEvent *>(new_item.event)->erased(); 00411 delete new_item.event; 00412 // clip to parent if exists 00413 Widget *w = static_cast<Widget *>(new_item.receiver); 00414 Widget *wp = static_cast<Widget *>(w->parent()); 00415 if (wp) { 00416 Widget::update(wp, w->mapToParent(region)); 00417 } 00418 new_item.event = new PaintEvent(region, erased); 00419 } 00420 00421 } 00422 break; 00423 case Event::Resize: 00424 case Event::Move: 00425 case Event::LayoutHint: 00426 for (EventQueueList::iterator I(queue.begin()), 00427 E(queue.end()); I != E;) { 00428 const EventQueueItem& item = *I; 00429 if(item == new_item) { 00430 trace("event") << "Eliminating " << item.event << " for " << receiver << std::endl; 00431 delete item.event; 00432 I = queue.erase(I); 00433 } else { 00434 ++I; 00435 } 00436 } 00437 break; 00438 default: 00439 break; 00440 } 00441 00442 queue.push_back(new_item); 00443 } 00444 00445 } // namespace Wt
This document is licensed under the terms of the GNU Free Documentation License and may be freely distributed under the conditions given by this license.