265 lines
6.6 KiB
C++
265 lines
6.6 KiB
C++
// Timer.cc for FbTk - Fluxbox Toolkit
|
|
// Copyright (c) 2003 - 2012 Henrik Kinnunen (fluxgen at fluxbox dot org)
|
|
//
|
|
// Timer.cc for Blackbox - An X11 Window Manager
|
|
// Copyright (c) 1997 - 2000 Brad Hughes (bhughes at tcac.net)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
#include "Timer.hh"
|
|
|
|
#include "CommandParser.hh"
|
|
#include "StringUtil.hh"
|
|
|
|
#ifdef HAVE_CASSERT
|
|
#include <cassert>
|
|
#else
|
|
#include <assert.h>
|
|
#endif
|
|
|
|
// sys/select.h on solaris wants to use memset()
|
|
#ifdef HAVE_CSTRING
|
|
# include <cstring>
|
|
#else
|
|
# include <string.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
# include <sys/select.h>
|
|
#elif defined(_WIN32)
|
|
# include <winsock.h>
|
|
#endif
|
|
|
|
#include <cstdio>
|
|
#include <vector>
|
|
#include <set>
|
|
|
|
namespace {
|
|
|
|
struct TimerCompare {
|
|
// stable sort order and allows multiple timers to have
|
|
// the same end-time
|
|
bool operator() (const FbTk::Timer* a, const FbTk::Timer* b) const {
|
|
uint64_t ae = a->getEndTime();
|
|
uint64_t be = b->getEndTime();
|
|
return (ae < be) || (ae == be && a < b);
|
|
}
|
|
};
|
|
typedef std::set<FbTk::Timer*, TimerCompare> TimerList;
|
|
TimerList s_timerlist;
|
|
|
|
}
|
|
|
|
|
|
namespace FbTk {
|
|
|
|
Timer::Timer() :
|
|
m_once(false),
|
|
m_interval(0),
|
|
m_start(0),
|
|
m_timeout(0) {
|
|
|
|
}
|
|
|
|
Timer::Timer(const RefCount<Slot<void> > &handler):
|
|
m_handler(handler),
|
|
m_once(false),
|
|
m_interval(0),
|
|
m_start(0),
|
|
m_timeout(0) {
|
|
}
|
|
|
|
|
|
Timer::~Timer() {
|
|
stop();
|
|
}
|
|
|
|
|
|
void Timer::setTimeout(uint64_t timeout, bool force_start) {
|
|
|
|
bool was_timing = isTiming();
|
|
if (was_timing) {
|
|
stop();
|
|
}
|
|
m_timeout = timeout;
|
|
|
|
if (force_start || was_timing) {
|
|
start();
|
|
}
|
|
}
|
|
|
|
void Timer::setCommand(const RefCount<Slot<void> > &cmd) {
|
|
m_handler = cmd;
|
|
}
|
|
|
|
void Timer::start() {
|
|
|
|
// only add Timers that actually DO something
|
|
if ( ( ! isTiming() || m_interval > 0 ) && m_handler) {
|
|
|
|
// in case start() gets triggered on a started
|
|
// timer with 'm_interval != 0' we have to remove
|
|
// it from s_timerlist before restarting it
|
|
stop();
|
|
|
|
m_start = FbTk::FbTime::mono();
|
|
|
|
// interval timers have their timeout change every
|
|
// time they are started!
|
|
if (m_interval != 0) {
|
|
m_timeout = m_interval * FbTk::FbTime::IN_SECONDS;
|
|
}
|
|
s_timerlist.insert(this);
|
|
}
|
|
}
|
|
|
|
|
|
void Timer::stop() {
|
|
s_timerlist.erase(this);
|
|
}
|
|
|
|
uint64_t Timer::getEndTime() const {
|
|
return m_start + m_timeout;
|
|
}
|
|
|
|
int Timer::isTiming() const {
|
|
return s_timerlist.find(const_cast<FbTk::Timer*>(this)) != s_timerlist.end();
|
|
}
|
|
|
|
void Timer::fireTimeout() {
|
|
if (m_handler)
|
|
(*m_handler)();
|
|
}
|
|
|
|
|
|
void Timer::updateTimers(int fd) {
|
|
|
|
fd_set rfds;
|
|
timeval* tout;
|
|
timeval tm;
|
|
TimerList::iterator t;
|
|
bool overdue = false;
|
|
uint64_t now;
|
|
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(fd, &rfds);
|
|
tout = NULL;
|
|
|
|
// search for overdue timers
|
|
if (!s_timerlist.empty()) {
|
|
|
|
Timer* timer = *s_timerlist.begin();
|
|
uint64_t end_time = timer->getEndTime();
|
|
|
|
now = FbTime::mono();
|
|
if (end_time <= now) {
|
|
overdue = true;
|
|
} else {
|
|
uint64_t diff = (end_time - now);
|
|
tm.tv_sec = diff / FbTime::IN_SECONDS;
|
|
tm.tv_usec = diff % FbTime::IN_SECONDS;
|
|
tout = &tm;
|
|
}
|
|
}
|
|
|
|
// if not overdue, wait for the next xevent via the blocking
|
|
// select(), so OS sends fluxbox to sleep. the select() will
|
|
// time out when the next timer has to be handled
|
|
if (!overdue && select(fd + 1, &rfds, 0, 0, tout) != 0) {
|
|
// didn't time out! x events are pending
|
|
return;
|
|
}
|
|
|
|
// stoping / restarting the timers modifies the list in an upredictable
|
|
// way. to avoid problems (infinite loops etc) we copy the current overdue
|
|
// timers from the gloabl (and ordered) list of timers and work on it.
|
|
|
|
static std::vector<FbTk::Timer*> timeouts;
|
|
|
|
now = FbTime::mono();
|
|
for (t = s_timerlist.begin(); t != s_timerlist.end(); ++t ) {
|
|
if (now < (*t)->getEndTime()) {
|
|
break;
|
|
}
|
|
timeouts.push_back(*t);
|
|
}
|
|
|
|
size_t i;
|
|
const size_t ts = timeouts.size();
|
|
for (i = 0; i < ts; ++i) {
|
|
|
|
FbTk::Timer& timer = *timeouts[i];
|
|
|
|
// first we stop the timer to remove it
|
|
// from s_timerlist
|
|
timer.stop();
|
|
|
|
// then we call the handler which might (re)start 't'
|
|
// on it's own
|
|
timer.fireTimeout();
|
|
|
|
// restart 't' if needed
|
|
if (!timer.doOnce() && !timer.isTiming()) {
|
|
timer.start();
|
|
}
|
|
}
|
|
|
|
timeouts.clear();
|
|
}
|
|
|
|
|
|
Command<void> *DelayedCmd::parse(const std::string &command,
|
|
const std::string &args, bool trusted) {
|
|
|
|
std::string cmd_str;
|
|
int err = StringUtil::getStringBetween(cmd_str, args.c_str(), '{', '}',
|
|
" \t\n", true);
|
|
if (err == 0)
|
|
return 0;
|
|
|
|
RefCount<Command<void> > cmd(CommandParser<void>::instance().parse(cmd_str, trusted));
|
|
if (cmd == 0)
|
|
return 0;
|
|
|
|
uint64_t delay = 200;
|
|
StringUtil::extractNumber(args.c_str() + err, delay);
|
|
|
|
return new DelayedCmd(cmd, delay);
|
|
}
|
|
|
|
REGISTER_COMMAND_PARSER(delay, DelayedCmd::parse, void);
|
|
|
|
DelayedCmd::DelayedCmd(const RefCount<Slot<void> > &cmd, uint64_t timeout) {
|
|
initTimer(timeout);
|
|
m_timer.setCommand(cmd);
|
|
}
|
|
|
|
void DelayedCmd::initTimer(uint64_t timeout) {
|
|
m_timer.setTimeout(timeout);
|
|
m_timer.fireOnce(true);
|
|
}
|
|
|
|
void DelayedCmd::execute() {
|
|
if (m_timer.isTiming())
|
|
m_timer.stop();
|
|
m_timer.start();
|
|
}
|
|
|
|
} // end namespace FbTk
|