virtual void TJS_INTF_METHOD OnContinuousCallback(tjs_uint64 tick) { int diff = (int)((prevTime + 1000 / fpsLimitValue) - TVPGetTickCount()); if (diff > 0) { Sleep(diff); } prevTime = TVPGetTickCount(); }
extern "C" __declspec(dllexport) HRESULT __stdcall V2Link(iTVPFunctionExporter *exporter) { // スタブの初期化(必ず記述する) TVPInitImportStub(exporter); // 初期化 prevTime = TVPGetTickCount(); TVPAddContinuousEventHook(&limit); { // TJS のグローバルオブジェクトを取得する iTJSDispatch2 * global = TVPGetScriptDispatch(); // Layer クラスオブジェクトを取得 tTJSVariant varScripts; TVPExecuteExpression(TJS_W("System"), &varScripts); iTJSDispatch2 *dispatch = varScripts.AsObjectNoAddRef(); if (dispatch) { tTJSDispatch *method = new tFpsLimitProp(); tTJSVariant var(method); dispatch->PropSet(TJS_MEMBERENSURE, FPSLIMITNAME, NULL, &var, dispatch); method->Release(); } global->Release(); } GlobalRefCountAtInit = TVPPluginGlobalRefCount; return S_OK; }
//--------------------------------------------------------------------------- void tTVPTimerThread::SetInterval(tTJSNI_Timer *item, tjs_uint64 interval) { { // thread-protected tTJSCriticalSectionHolder holder(TVPTimerCS); item->InternalSetInterval(interval); if(item->GetEnabled()) { item->CancelEvents(); item->ZeroPendingCount(); item->SetNextTick((TVPGetTickCount() << TVP_SUBMILLI_FRAC_BITS) + item->GetInterval()); } } // end-of-thread-protected if(item->GetEnabled()) Event.Set(); }
//--------------------------------------------------------------------------- void tTVPTimerThread::Execute() { while(!GetTerminated()) { tjs_uint64 step_next = (tjs_uint64)(tjs_int64)-1L; // invalid value tjs_uint64 curtick = TVPGetTickCount() << TVP_SUBMILLI_FRAC_BITS; DWORD sleeptime; { // thread-protected tTJSCriticalSectionHolder holder(TVPTimerCS); bool any_triggered = false; std::vector<tTJSNI_Timer*>::iterator i; for(i = List.begin(); i!=List.end(); i ++) { tTJSNI_Timer * item = *i; if(!item->GetEnabled() || item->GetInterval() == 0) continue; if(item->GetNextTick() < curtick) { tjs_uint n = static_cast<tjs_uint>( (curtick - item->GetNextTick()) / item->GetInterval() ); n++; if(n > 40) { // too large amount of event at once; discard rest item->Trigger(1); any_triggered = true; item->SetNextTick(curtick + item->GetInterval()); } else { item->Trigger(n); any_triggered = true; item->SetNextTick(item->GetNextTick() + n * item->GetInterval()); } } tjs_uint64 to_next = item->GetNextTick() - curtick; if(step_next == (tjs_uint64)(tjs_int64)-1L) { step_next = to_next; } else { if(step_next > to_next) step_next = to_next; } } if(step_next != (tjs_uint64)(tjs_int64)-1L) { // too large step_next must be diminished to size of DWORD. if(step_next >= 0x80000000) sleeptime = 0x7fffffff; // smaller value than step_next is OK else sleeptime = static_cast<DWORD>( step_next ); } else { sleeptime = INFINITE; } if(List.size() == 0) sleeptime = INFINITE; if(any_triggered) { // triggered; post notification message to the UtilWindow if(!PendingEventsAvailable) { PendingEventsAvailable = true; EventQueue.PostEvent( NativeEvent(TVP_EV_TIMER_THREAD) ); } } } // end-of-thread-protected // now, sleeptime has sub-milliseconds precision but we need millisecond // precision time. if(sleeptime != INFINITE) sleeptime = (sleeptime >> TVP_SUBMILLI_FRAC_BITS) + (sleeptime & ((1<<TVP_SUBMILLI_FRAC_BITS)-1) ? 1: 0); // round up // clamp to TVP_LEAST_TIMER_INTERVAL ... if(sleeptime != INFINITE && sleeptime < TVP_LEAST_TIMER_INTERVAL) sleeptime = TVP_LEAST_TIMER_INTERVAL; Event.WaitFor(sleeptime); // wait until sleeptime is elapsed or // Event->SetEvent() is executed. } }
//--------------------------------------------------------------------------- static void _TVPDeliverContinuousEvent() // internal { TVPStartTickCount(); tjs_uint64 tick = TVPGetTickCount(); if(TVPContinuousEventVector.size()) { bool emptyflag = false; for(tjs_uint32 i = 0; i < TVPContinuousEventVector.size(); i++) { // note that the handler can remove itself while the event if(TVPContinuousEventVector[i]) TVPContinuousEventVector[i]->OnContinuousCallback(tick); else emptyflag = true; if(TVPExclusiveEventPosted) return; // check exclusive events } if(emptyflag) { // the array has empty cell // eliminate empty std::vector<tTVPContinuousEventCallbackIntf *>::iterator i; for(i = TVPContinuousEventVector.begin(); i !=TVPContinuousEventVector.end();) { if(*i == NULL) i = TVPContinuousEventVector.erase(i); else i++; } } } if(!TVPEventDisabled && TVPContinuousHandlerVector.size()) { bool emptyflag = false; tTJSVariant vtick((tjs_int64)tick); tTJSVariant *pvtick = &vtick; for(tjs_uint i = 0; i < TVPContinuousHandlerVector.size(); i++) { if(TVPContinuousHandlerVector[i].Object) { tjs_error er; try { er = TVPContinuousHandlerVector[i].FuncCall(0, NULL, NULL, NULL, 1, &pvtick, NULL); } catch(...) { // failed TVPContinuousHandlerVector[i].Release(); TVPContinuousHandlerVector[i].Object = TVPContinuousHandlerVector[i].ObjThis = NULL; throw; } if(TJS_FAILED(er)) { // failed TVPContinuousHandlerVector[i].Release(); TVPContinuousHandlerVector[i].Object = TVPContinuousHandlerVector[i].ObjThis = NULL; emptyflag = true; } if(TVPExclusiveEventPosted) return; // check exclusive events } else { emptyflag = true; } } if(emptyflag) { // the array has empty cell // eliminate empty std::vector<tTJSVariantClosure>::iterator i; for(i = TVPContinuousHandlerVector.begin(); i !=TVPContinuousHandlerVector.end();) { if(!i->Object) { i->Release(); i = TVPContinuousHandlerVector.erase(i); } else { i++; } } } } if(!TVPContinuousEventVector.size() && !TVPContinuousHandlerVector.size()) TVPEndContinuousEvent(); }