nana/source/gui/timer.cpp

253 lines
5.3 KiB
C++

/*
* A Timer Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2019 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/timer.hpp
* @description:
* A timer can repeatedly call a piece of code. The duration between
* calls is specified in milliseconds. Timer is different from other graphics
* controls, it has no graphics interface.
* @contributors: Benjamin Navarro(pr#81)
*/
#include <nana/deploy.hpp>
#include <nana/gui/timer.hpp>
#include <map>
#include <memory>
#if defined(STD_THREAD_NOT_SUPPORTED)
#include <nana/std_mutex.hpp>
#else
#include <mutex>
#endif
#if defined(NANA_WINDOWS)
#include <windows.h>
#elif defined(NANA_POSIX)
#include "../detail/posix/platform_spec.hpp"
#include <nana/system/platform.hpp>
#endif
namespace nana
{
namespace detail
{
class timer_core;
}
#if defined(NANA_WINDOWS)
typedef UINT_PTR timer_identifier;
#else
typedef const detail::timer_core* timer_identifier;
#endif
class timer_driver
{
friend class detail::timer_core;
timer_driver() = default;
public:
static timer_driver& instance()
{
static timer_driver obj;
return obj;
}
template<typename Factory>
detail::timer_core* create(unsigned ms, Factory && factory)
{
#if defined(NANA_WINDOWS)
auto tmid = ::SetTimer(nullptr, 0, ms, &timer_driver::_m_timer_proc);
#endif
try
{
#if defined(NANA_WINDOWS)
auto p = factory(tmid);
#else
auto p = factory();
auto tmid = p;
::nana::detail::platform_spec::instance().set_timer(tmid, ms, &timer_driver::_m_timer_proc);
#endif
::nana::internal_scope_guard lock;
timer_table_[tmid].reset(p);
return p;
}
catch (...)
{
}
return nullptr;
}
void destroy(timer_identifier handle)
{
::nana::internal_scope_guard lock;
auto i = timer_table_.find(handle);
if (i != timer_table_.end())
{
#if defined(NANA_WINDOWS)
::KillTimer(nullptr, handle);
#else
::nana::detail::platform_spec::instance().kill_timer(handle);
#endif
timer_table_.erase(i);
}
}
private:
#if defined(NANA_WINDOWS)
static void __stdcall _m_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime);
#else
static void _m_timer_proc(timer_identifier id);
#endif
private:
std::map<timer_identifier, std::unique_ptr<detail::timer_core>> timer_table_;
};
class detail::timer_core
{
public:
#if defined(NANA_WINDOWS)
timer_core(timer* sender, timer_identifier tmid, std::shared_ptr<basic_event<arg_elapse>> evt_elapse):
sender_(sender),
timer_(tmid),
evt_elapse_(evt_elapse)
{}
#else
timer_core(timer* sender, std::shared_ptr<basic_event<arg_elapse>> evt_elapse):
sender_(sender),
timer_(this),
evt_elapse_(evt_elapse)
{}
#endif
timer_identifier id() const
{
return timer_;
}
void interval(unsigned ms)
{
#if defined(NANA_WINDOWS)
::SetTimer(nullptr, timer_, ms, &timer_driver::_m_timer_proc);
#else
::nana::detail::platform_spec::instance().set_timer(timer_, ms, &timer_driver::_m_timer_proc);
#endif
}
void emit()
{
arg_elapse arg;
arg.sender = sender_;
//retain the object to avoid it to be deleted during calling of emit
auto retain = evt_elapse_;
retain->emit(arg, nullptr);
}
private:
timer * const sender_;
const timer_identifier timer_;
std::shared_ptr<nana::basic_event<arg_elapse>> evt_elapse_;
}; //end class timer_core
#if defined(NANA_WINDOWS)
void __stdcall timer_driver::_m_timer_proc(HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR handle, DWORD /*dwTime*/)
#else
void timer_driver::_m_timer_proc(timer_identifier handle)
#endif
{
auto & time_tbl = instance().timer_table_;
::nana::internal_scope_guard lock;
auto i = time_tbl.find(handle);
if (i == time_tbl.end())
return;
i->second->emit();
}
struct timer::implement
{
unsigned interval{ 1000 }; //1 second in default
detail::timer_core * tm_core{ nullptr };
};
//class timer
timer::timer():
elapse_(std::make_shared<nana::basic_event<arg_elapse>>()),
impl_(new implement)
{
}
timer::timer(std::chrono::milliseconds ms):
timer()
{
this->interval(ms);
}
timer::~timer()
{
stop();
delete impl_;
}
void timer::reset()
{
stop();
elapse_->clear();
}
void timer::start()
{
if (impl_->tm_core)
return;
#if defined(NANA_WINDOWS)
impl_->tm_core = timer_driver::instance().create(impl_->interval, [this](timer_identifier id)
{
return new detail::timer_core(this, id, elapse_);
});
#else
impl_->tm_core = timer_driver::instance().create(impl_->interval, [this]
{
return new detail::timer_core(this, elapse_);
});
#endif
}
bool timer::started() const
{
return (nullptr != impl_->tm_core);
}
void timer::stop()
{
if (nullptr == impl_->tm_core)
return;
auto tmid = impl_->tm_core->id();
impl_->tm_core = nullptr;
timer_driver::instance().destroy(tmid);
}
void timer::interval(std::chrono::milliseconds ms)
{
if (ms.count() != static_cast<long long>(impl_->interval))
{
impl_->interval = static_cast<unsigned>(ms.count());
if (impl_->tm_core)
impl_->tm_core->interval(impl_->interval);
}
}
unsigned timer::_m_interval() const
{
return impl_->interval;
}
//end class timer
}//end namespace nana