diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-20 18:51:03 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-20 18:51:03 +0200 |
commit | 5dfb890cf841579edaeb2633025db218e86f45c2 (patch) | |
tree | a6937d904579b8d7051cf033e03e99464d298c98 /src | |
parent | aa70507d43d17ca2c426acd6bf91548fe267ac19 (diff) |
App: Periodic commands without timers/threads
Now the periodic commands get posted on the main thread at intervals, and the event loop cooperates by not sleeping if there are periodic commands pending.
The macOS animation hangup seems to be unrelated, though — perhaps some internal SDL/Metal machinery related to app refresh stopping for a while?
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 32 | ||||
-rw-r--r-- | src/periodic.c | 44 | ||||
-rw-r--r-- | src/periodic.h | 16 | ||||
-rw-r--r-- | src/ui/scrollwidget.c | 2 |
4 files changed, 51 insertions, 43 deletions
@@ -837,6 +837,9 @@ void trimCache_App(void) { | |||
837 | } | 837 | } |
838 | 838 | ||
839 | iLocalDef iBool isWaitingAllowed_App_(iApp *d) { | 839 | iLocalDef iBool isWaitingAllowed_App_(iApp *d) { |
840 | if (!isEmpty_Periodic(&d->periodic)) { | ||
841 | return iFalse; | ||
842 | } | ||
840 | if (d->warmupFrames > 0) { | 843 | if (d->warmupFrames > 0) { |
841 | return iFalse; | 844 | return iFalse; |
842 | } | 845 | } |
@@ -853,14 +856,26 @@ iLocalDef iBool isWaitingAllowed_App_(iApp *d) { | |||
853 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); | 856 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); |
854 | } | 857 | } |
855 | 858 | ||
859 | static iBool nextEvent_App_(iApp *d, enum iAppEventMode eventMode, SDL_Event *event) { | ||
860 | if (eventMode == waitForNewEvents_AppEventMode && isWaitingAllowed_App_(d)) { | ||
861 | /* If there are periodic commands pending, wait only for a short while. */ | ||
862 | if (!isEmpty_Periodic(&d->periodic)) { | ||
863 | return SDL_WaitEventTimeout(event, 500); | ||
864 | } | ||
865 | /* We may be allowed to block here until an event comes in. */ | ||
866 | if (isWaitingAllowed_App_(d)) { | ||
867 | return SDL_WaitEvent(event); | ||
868 | } | ||
869 | } | ||
870 | return SDL_PollEvent(event); | ||
871 | } | ||
872 | |||
856 | void processEvents_App(enum iAppEventMode eventMode) { | 873 | void processEvents_App(enum iAppEventMode eventMode) { |
857 | iApp *d = &app_; | 874 | iApp *d = &app_; |
858 | SDL_Event ev; | 875 | SDL_Event ev; |
859 | iBool gotEvents = iFalse; | 876 | iBool gotEvents = iFalse; |
860 | while ((isWaitingAllowed_App_(d) && eventMode == waitForNewEvents_AppEventMode && | 877 | postCommands_Periodic(&d->periodic); |
861 | SDL_WaitEvent(&ev)) || | 878 | while (nextEvent_App_(d, eventMode, &ev)) { |
862 | ((!isWaitingAllowed_App_(d) || eventMode == postedEventsOnly_AppEventMode) && | ||
863 | SDL_PollEvent(&ev))) { | ||
864 | #if defined (iPlatformAppleMobile) | 879 | #if defined (iPlatformAppleMobile) |
865 | if (processEvent_iOS(&ev)) { | 880 | if (processEvent_iOS(&ev)) { |
866 | continue; | 881 | continue; |
@@ -1064,13 +1079,18 @@ static int run_App_(iApp *d) { | |||
1064 | 1079 | ||
1065 | void refresh_App(void) { | 1080 | void refresh_App(void) { |
1066 | iApp *d = &app_; | 1081 | iApp *d = &app_; |
1082 | destroyPending_Widget(); | ||
1067 | #if defined (LAGRANGE_IDLE_SLEEP) | 1083 | #if defined (LAGRANGE_IDLE_SLEEP) |
1068 | if (d->warmupFrames == 0 && d->isIdling) { | 1084 | if (d->warmupFrames == 0 && d->isIdling) { |
1069 | return; | 1085 | return; |
1070 | } | 1086 | } |
1071 | #endif | 1087 | #endif |
1072 | set_Atomic(&d->pendingRefresh, iFalse); | 1088 | if (!exchange_Atomic(&d->pendingRefresh, iFalse)) { |
1073 | destroyPending_Widget(); | 1089 | /* Refreshing wasn't pending. */ |
1090 | if (isFinished_Anim(&d->window->rootOffset)) { | ||
1091 | return; | ||
1092 | } | ||
1093 | } | ||
1074 | // iTime draw; | 1094 | // iTime draw; |
1075 | // initCurrent_Time(&draw); | 1095 | // initCurrent_Time(&draw); |
1076 | draw_Window(d->window); | 1096 | draw_Window(d->window); |
diff --git a/src/periodic.c b/src/periodic.c index c039097d..29c26de9 100644 --- a/src/periodic.c +++ b/src/periodic.c | |||
@@ -52,50 +52,35 @@ iDefineTypeConstructionArgs(PeriodicCommand, (iAny *ctx, const char *cmd), ctx, | |||
52 | 52 | ||
53 | /*----------------------------------------------------------------------------------------------*/ | 53 | /*----------------------------------------------------------------------------------------------*/ |
54 | 54 | ||
55 | //static uint32_t postCommands_Periodic_(uint32_t interval, void *param) { | 55 | static const uint32_t postingInterval_Periodic_ = 500; |
56 | static iThreadResult poster_Periodic_(iThread *thread) { | 56 | |
57 | iPeriodic *d = userData_Thread(thread); | 57 | iBool postCommands_Periodic(iPeriodic *d) { |
58 | const uint32_t now = SDL_GetTicks(); | ||
59 | if (now - d->lastPostTime < postingInterval_Periodic_) { | ||
60 | return iFalse; | ||
61 | } | ||
62 | d->lastPostTime = now; | ||
63 | iBool wasPosted = iFalse; | ||
58 | lock_Mutex(d->mutex); | 64 | lock_Mutex(d->mutex); |
59 | while (!value_Atomic(&d->isStopping)) { | 65 | iConstForEach(Array, i, &d->commands.values) { |
60 | if (isEmpty_SortedArray(&d->commands)) { | 66 | postCommandString_App(&((const iPeriodicCommand *) i.value)->command); |
61 | /* Sleep until we have something to post. */ | 67 | wasPosted = iTrue; |
62 | wait_Condition(&d->haveCommands, d->mutex); | ||
63 | continue; | ||
64 | } | ||
65 | iConstForEach(Array, i, &d->commands.values) { | ||
66 | postCommandString_App(&((const iPeriodicCommand *) i.value)->command); | ||
67 | } | ||
68 | /* Sleep for a while. */ | ||
69 | iTime until; | ||
70 | initTimeout_Time(&until, 0.5f); | ||
71 | waitTimeout_Condition(&d->haveCommands, d->mutex, &until); | ||
72 | } | 68 | } |
73 | unlock_Mutex(d->mutex); | 69 | unlock_Mutex(d->mutex); |
74 | return 0; | 70 | return wasPosted; |
75 | } | 71 | } |
76 | 72 | ||
77 | void init_Periodic(iPeriodic *d) { | 73 | void init_Periodic(iPeriodic *d) { |
78 | d->mutex = new_Mutex(); | 74 | d->mutex = new_Mutex(); |
79 | init_SortedArray(&d->commands, sizeof(iPeriodicCommand), cmp_PeriodicCommand_); | 75 | init_SortedArray(&d->commands, sizeof(iPeriodicCommand), cmp_PeriodicCommand_); |
80 | // d->timer = SDL_AddTimer(500, postCommands_Periodic_, d); | 76 | d->lastPostTime = 0; |
81 | set_Atomic(&d->isStopping, iFalse); | ||
82 | init_Condition(&d->haveCommands); | ||
83 | d->thread = new_Thread(poster_Periodic_); | ||
84 | setUserData_Thread(d->thread, d); | ||
85 | start_Thread(d->thread); | ||
86 | } | 77 | } |
87 | 78 | ||
88 | void deinit_Periodic(iPeriodic *d) { | 79 | void deinit_Periodic(iPeriodic *d) { |
89 | // SDL_RemoveTimer(d->timer); | ||
90 | set_Atomic(&d->isStopping, iTrue); | ||
91 | signal_Condition(&d->haveCommands); | ||
92 | join_Thread(d->thread); | ||
93 | iRelease(d->thread); | ||
94 | iForEach(Array, i, &d->commands.values) { | 80 | iForEach(Array, i, &d->commands.values) { |
95 | deinit_PeriodicCommand(i.value); | 81 | deinit_PeriodicCommand(i.value); |
96 | } | 82 | } |
97 | deinit_SortedArray(&d->commands); | 83 | deinit_SortedArray(&d->commands); |
98 | deinit_Condition(&d->haveCommands); | ||
99 | delete_Mutex(d->mutex); | 84 | delete_Mutex(d->mutex); |
100 | } | 85 | } |
101 | 86 | ||
@@ -112,7 +97,6 @@ void add_Periodic(iPeriodic *d, iAny *context, const char *command) { | |||
112 | init_PeriodicCommand(&pc, context, command); | 97 | init_PeriodicCommand(&pc, context, command); |
113 | insert_SortedArray(&d->commands, &pc); | 98 | insert_SortedArray(&d->commands, &pc); |
114 | } | 99 | } |
115 | signal_Condition(&d->haveCommands); | ||
116 | unlock_Mutex(d->mutex); | 100 | unlock_Mutex(d->mutex); |
117 | } | 101 | } |
118 | 102 | ||
diff --git a/src/periodic.h b/src/periodic.h index c643a2fe..db90b848 100644 --- a/src/periodic.h +++ b/src/periodic.h | |||
@@ -26,17 +26,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
26 | iDeclareType(Periodic) | 26 | iDeclareType(Periodic) |
27 | iDeclareType(Thread) | 27 | iDeclareType(Thread) |
28 | 28 | ||
29 | /* Animation utility. Not per frame but several times per second. */ | 29 | /* Animation utility. Not per frame but several times per second. Thread safe. */ |
30 | struct Impl_Periodic { | 30 | struct Impl_Periodic { |
31 | iMutex * mutex; | 31 | iMutex * mutex; |
32 | iSortedArray commands; | 32 | iSortedArray commands; |
33 | iCondition haveCommands; | 33 | uint32_t lastPostTime; |
34 | iThread * thread; | ||
35 | iAtomicInt isStopping; | ||
36 | }; | 34 | }; |
37 | 35 | ||
38 | void init_Periodic (iPeriodic *); | 36 | void init_Periodic (iPeriodic *); |
39 | void deinit_Periodic (iPeriodic *); | 37 | void deinit_Periodic (iPeriodic *); |
40 | 38 | ||
41 | void add_Periodic (iPeriodic *, iAny *context, const char *command); | 39 | iLocalDef iBool isEmpty_Periodic(const iPeriodic *d) { |
42 | void remove_Periodic (iPeriodic *, iAny *context); | 40 | return isEmpty_SortedArray(&d->commands); |
41 | } | ||
42 | |||
43 | void add_Periodic (iPeriodic *, iAny *context, const char *command); | ||
44 | void remove_Periodic (iPeriodic *, iAny *context); | ||
45 | |||
46 | iBool postCommands_Periodic (iPeriodic *); | ||
diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c index b5b204a1..55ede426 100644 --- a/src/ui/scrollwidget.c +++ b/src/ui/scrollwidget.c | |||
@@ -112,7 +112,7 @@ static void unfade_ScrollWidget_(iScrollWidget *d, float opacity) { | |||
112 | d->willCheckFade = iTrue; | 112 | d->willCheckFade = iTrue; |
113 | /* TODO: This causes an inexplicable refresh issue on macOS: the drawing of one frame | 113 | /* TODO: This causes an inexplicable refresh issue on macOS: the drawing of one frame |
114 | takes 100ms for some reason (not the current frame but some time after). */ | 114 | takes 100ms for some reason (not the current frame but some time after). */ |
115 | // add_Periodic(periodic_App(), d, "scrollbar.fade"); | 115 | add_Periodic(periodic_App(), d, "scrollbar.fade"); |
116 | } | 116 | } |
117 | refresh_Widget(d); | 117 | refresh_Widget(d); |
118 | } | 118 | } |