gui/auxiliary/timer.cpp

Go to the documentation of this file.
00001 /* $Id: timer.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2009 - 2012 by Mark de Wever <koraq@xs4all.nl>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #include "gui/auxiliary/timer.hpp"
00017 
00018 #include "events.hpp"
00019 #include "gui/auxiliary/log.hpp"
00020 
00021 #include <boost/static_assert.hpp>
00022 
00023 #include <SDL_timer.h>
00024 
00025 #include <map>
00026 
00027 namespace gui2 {
00028 
00029 struct ttimer
00030 {
00031     ttimer()
00032         : sdl_id(NULL)
00033         , interval(0)
00034         , callback()
00035     {
00036     }
00037 
00038     SDL_TimerID sdl_id;
00039     Uint32 interval;
00040     boost::function<void(unsigned long id)> callback;
00041 };
00042 
00043     /** Ids for the timers. */
00044     static unsigned long id = 0;
00045 
00046     /** The active timers. */
00047     static std::map<unsigned long, ttimer> timers;
00048 
00049     /** The id of the event being executed, 0 if none. */
00050     static unsigned long executing_id = 0;
00051 
00052     /** Did somebody try to remove the timer during its execution? */
00053     static bool executing_id_removed = false;
00054 
00055 /**
00056  * Helper to make removing a timer in a callback safe.
00057  *
00058  * Upon creation it sets the executing id and clears the remove request flag.
00059  *
00060  * If an remove_timer() is called for the id being executed it requests a
00061  * remove the timer and exits remove_timer().
00062  *
00063  * Upon destruction it tests whether there was a request to remove the id and
00064  * does so. It also clears the executing id. It leaves the remove request flag
00065  * since the execution function needs to know whether or not the event was
00066  * removed.
00067  */
00068 class texecutor
00069 {
00070 public:
00071     texecutor(unsigned long id)
00072     {
00073         executing_id = id;
00074         executing_id_removed = false;
00075 
00076     }
00077 
00078     ~texecutor()
00079     {
00080         const unsigned long id = executing_id;
00081         executing_id = 0;
00082         if(executing_id_removed) {
00083             remove_timer(id);
00084         }
00085     }
00086 };
00087 
00088 extern "C" {
00089 
00090 static Uint32 timer_callback(Uint32, void* id)
00091 {
00092     DBG_GUI_E << "Pushing timer event in queue.\n";
00093 
00094     std::map<unsigned long, ttimer>::iterator itor =
00095             timers.find(reinterpret_cast<unsigned long>(id));
00096     if(itor == timers.end()) {
00097         return 0;
00098     }
00099 
00100     SDL_Event event;
00101     SDL_UserEvent data;
00102 
00103     data.type = TIMER_EVENT;
00104     data.code = 0;
00105     data.data1 = id;
00106     data.data2 = NULL;
00107 
00108     event.type = TIMER_EVENT;
00109     event.user = data;
00110 
00111     SDL_PushEvent(&event);
00112 
00113     return itor->second.interval;
00114 }
00115 
00116 } // extern "C"
00117 
00118 unsigned long
00119 add_timer(const Uint32 interval
00120         , const boost::function<void(unsigned long id)>& callback
00121         , const bool repeat)
00122 {
00123     BOOST_STATIC_ASSERT(sizeof(unsigned long) == sizeof(void*));
00124 
00125     DBG_GUI_E << "Adding timer.\n";
00126 
00127     do {
00128         ++id;
00129     } while(id == 0 || timers.find(id) != timers.end());
00130 
00131     ttimer timer;
00132     timer.sdl_id = SDL_AddTimer(
00133             interval, timer_callback, reinterpret_cast<void*>(id));
00134     if(timer.sdl_id == NULL) {
00135         WRN_GUI_E << "Failed to create an sdl timer.\n";
00136         return 0;
00137     }
00138 
00139     if(repeat) {
00140         timer.interval = interval;
00141     }
00142 
00143     timer.callback = callback;
00144 
00145     timers.insert(std::make_pair(id, timer));
00146 
00147     DBG_GUI_E << "Added timer " << id << ".\n";
00148     return id;
00149 }
00150 
00151 bool
00152 remove_timer(const unsigned long id)
00153 {
00154     DBG_GUI_E << "Removing timer " << id << ".\n";
00155 
00156     std::map<unsigned long, ttimer>::iterator itor = timers.find(id);
00157     if(itor == timers.end()) {
00158         LOG_GUI_E << "Can't remove timer since it no longer exists.\n";
00159         return false;
00160     }
00161 
00162     if(id == executing_id) {
00163         executing_id_removed = true;
00164         return true;
00165     }
00166 
00167     if(!SDL_RemoveTimer(itor->second.sdl_id)) {
00168         /*
00169          * This can happen if the caller of the timer didn't get the event yet
00170          * but the timer has already been fired. This due to the fact that a
00171          * timer pushes an event in the queue, which allows the following
00172          * condition:
00173          * - Timer fires
00174          * - Push event in queue
00175          * - Another event is processed and tries to remove the event.
00176          */
00177         DBG_GUI_E << "The timer is already out of the SDL timer list.\n";
00178     }
00179     timers.erase(itor);
00180     return true;
00181 }
00182 
00183 bool
00184 execute_timer(const unsigned long id)
00185 {
00186     DBG_GUI_E << "Executing timer " << id << ".\n";
00187 
00188     std::map<unsigned long, ttimer>::iterator itor = timers.find(id);
00189     if(itor == timers.end()) {
00190         LOG_GUI_E << "Can't execute timer since it no longer exists.\n";
00191         return false;
00192     }
00193 
00194     {
00195         texecutor executor(id);
00196         itor->second.callback(id);
00197     }
00198 
00199     if(!executing_id_removed && itor->second.interval == 0) {
00200         timers.erase(itor);
00201     }
00202     return true;
00203 }
00204 
00205 } //namespace gui2
00206 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:54 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs