diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-06 11:52:26 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-06 11:52:26 +0200 |
commit | 6a565ea71745aaf4c91a7698bbf56f7d906fcaaa (patch) | |
tree | e68ed6140097be20ab51193dfd25ac1a4d1c502d /src/app.c | |
parent | e43ecd3eb279e117cbce8fd507ecef247938e4ac (diff) |
Added build option for sleeping while idle
It appears at least on macOS, SDL is doing a while lot of stuff while waiting for new events. Perhaps because it has some sort of high-frequency input/sensor processing? Not sure.
Now Lagrange will idle by polling events every 15 ms and sleeping in between. This reduces CPU time by an order of magnitude on macOS. Need to still test on other platforms.
Diffstat (limited to 'src/app.c')
-rw-r--r-- | src/app.c | 79 |
1 files changed, 70 insertions, 9 deletions
@@ -89,6 +89,8 @@ static const char *prefsFileName_App_ = "prefs.cfg"; | |||
89 | static const char *stateFileName_App_ = "state.binary"; | 89 | static const char *stateFileName_App_ = "state.binary"; |
90 | static const char *downloadDir_App_ = "~/Downloads"; | 90 | static const char *downloadDir_App_ = "~/Downloads"; |
91 | 91 | ||
92 | static const int idleThreshold_App_ = 1000; /* ms */ | ||
93 | |||
92 | struct Impl_App { | 94 | struct Impl_App { |
93 | iCommandLine args; | 95 | iCommandLine args; |
94 | iString * execPath; | 96 | iString * execPath; |
@@ -100,7 +102,12 @@ struct Impl_App { | |||
100 | iSortedArray tickers; | 102 | iSortedArray tickers; |
101 | uint32_t lastTickerTime; | 103 | uint32_t lastTickerTime; |
102 | uint32_t elapsedSinceLastTicker; | 104 | uint32_t elapsedSinceLastTicker; |
103 | iBool running; | 105 | iBool isRunning; |
106 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
107 | iBool isIdling; | ||
108 | uint32_t lastEventTime; | ||
109 | int sleepTimer; | ||
110 | #endif | ||
104 | iAtomicInt pendingRefresh; | 111 | iAtomicInt pendingRefresh; |
105 | int tabEnum; | 112 | int tabEnum; |
106 | iStringList *launchCommands; | 113 | iStringList *launchCommands; |
@@ -320,6 +327,16 @@ static void saveState_App_(const iApp *d) { | |||
320 | iRelease(f); | 327 | iRelease(f); |
321 | } | 328 | } |
322 | 329 | ||
330 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
331 | static uint32_t checkAsleep_App_(uint32_t interval, void *param) { | ||
332 | iApp *d = param; | ||
333 | SDL_Event ev = { .type = SDL_USEREVENT }; | ||
334 | ev.user.code = asleep_UserEventCode; | ||
335 | SDL_PushEvent(&ev); | ||
336 | return interval; | ||
337 | } | ||
338 | #endif | ||
339 | |||
323 | static void init_App_(iApp *d, int argc, char **argv) { | 340 | static void init_App_(iApp *d, int argc, char **argv) { |
324 | const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_)); | 341 | const iBool isFirstRun = !fileExistsCStr_FileInfo(cleanedPath_CStr(dataDir_App_)); |
325 | d->isFinishedLaunching = iFalse; | 342 | d->isFinishedLaunching = iFalse; |
@@ -349,7 +366,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
349 | #endif | 366 | #endif |
350 | init_Prefs(&d->prefs); | 367 | init_Prefs(&d->prefs); |
351 | setCStr_String(&d->prefs.downloadDir, downloadDir_App_); | 368 | setCStr_String(&d->prefs.downloadDir, downloadDir_App_); |
352 | d->running = iFalse; | 369 | d->isRunning = iFalse; |
353 | d->window = NULL; | 370 | d->window = NULL; |
354 | set_Atomic(&d->pendingRefresh, iFalse); | 371 | set_Atomic(&d->pendingRefresh, iFalse); |
355 | d->mimehooks = new_MimeHooks(); | 372 | d->mimehooks = new_MimeHooks(); |
@@ -358,6 +375,11 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
358 | d->bookmarks = new_Bookmarks(); | 375 | d->bookmarks = new_Bookmarks(); |
359 | d->tabEnum = 0; /* generates unique IDs for tab pages */ | 376 | d->tabEnum = 0; /* generates unique IDs for tab pages */ |
360 | setThemePalette_Color(d->prefs.theme); | 377 | setThemePalette_Color(d->prefs.theme); |
378 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
379 | d->isIdling = iFalse; | ||
380 | d->lastEventTime = 0; | ||
381 | d->sleepTimer = SDL_AddTimer(1000, checkAsleep_App_, d); | ||
382 | #endif | ||
361 | #if defined (iPlatformApple) | 383 | #if defined (iPlatformApple) |
362 | setupApplication_MacOS(); | 384 | setupApplication_MacOS(); |
363 | #endif | 385 | #endif |
@@ -484,19 +506,25 @@ const iString *debugInfo_App(void) { | |||
484 | } | 506 | } |
485 | 507 | ||
486 | iLocalDef iBool isWaitingAllowed_App_(iApp *d) { | 508 | iLocalDef iBool isWaitingAllowed_App_(iApp *d) { |
509 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
510 | if (d->isIdling) { | ||
511 | return iFalse; | ||
512 | } | ||
513 | #endif | ||
487 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); | 514 | return !value_Atomic(&d->pendingRefresh) && isEmpty_SortedArray(&d->tickers); |
488 | } | 515 | } |
489 | 516 | ||
490 | void processEvents_App(enum iAppEventMode eventMode) { | 517 | void processEvents_App(enum iAppEventMode eventMode) { |
491 | iApp *d = &app_; | 518 | iApp *d = &app_; |
492 | SDL_Event ev; | 519 | SDL_Event ev; |
520 | iBool gotEvents = iFalse; | ||
493 | while ((isWaitingAllowed_App_(d) && eventMode == waitForNewEvents_AppEventMode && | 521 | while ((isWaitingAllowed_App_(d) && eventMode == waitForNewEvents_AppEventMode && |
494 | SDL_WaitEvent(&ev)) || | 522 | SDL_WaitEvent(&ev)) || |
495 | ((!isWaitingAllowed_App_(d) || eventMode == postedEventsOnly_AppEventMode) && | 523 | ((!isWaitingAllowed_App_(d) || eventMode == postedEventsOnly_AppEventMode) && |
496 | SDL_PollEvent(&ev))) { | 524 | SDL_PollEvent(&ev))) { |
497 | switch (ev.type) { | 525 | switch (ev.type) { |
498 | case SDL_QUIT: | 526 | case SDL_QUIT: |
499 | d->running = iFalse; | 527 | d->isRunning = iFalse; |
500 | goto backToMainLoop; | 528 | goto backToMainLoop; |
501 | case SDL_DROPFILE: { | 529 | case SDL_DROPFILE: { |
502 | iBool newTab = iFalse; | 530 | iBool newTab = iFalse; |
@@ -516,6 +544,25 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
516 | break; | 544 | break; |
517 | } | 545 | } |
518 | default: { | 546 | default: { |
547 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
548 | if (ev.type == SDL_USEREVENT && ev.user.code == asleep_UserEventCode) { | ||
549 | if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_) { | ||
550 | if (!d->isIdling) { | ||
551 | printf("[App] idling...\n"); | ||
552 | fflush(stdout); | ||
553 | } | ||
554 | d->isIdling = iTrue; | ||
555 | } | ||
556 | continue; | ||
557 | } | ||
558 | d->lastEventTime = SDL_GetTicks(); | ||
559 | if (d->isIdling) { | ||
560 | printf("[App] ...woke up\n"); | ||
561 | fflush(stdout); | ||
562 | } | ||
563 | d->isIdling = iFalse; | ||
564 | #endif | ||
565 | gotEvents = iTrue; | ||
519 | iBool wasUsed = processEvent_Window(d->window, &ev); | 566 | iBool wasUsed = processEvent_Window(d->window, &ev); |
520 | if (!wasUsed) { | 567 | if (!wasUsed) { |
521 | /* There may be a key bindings for this. */ | 568 | /* There may be a key bindings for this. */ |
@@ -539,6 +586,14 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
539 | } | 586 | } |
540 | } | 587 | } |
541 | } | 588 | } |
589 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
590 | if (d->isIdling && !gotEvents) { | ||
591 | /* This is where we spend most of our time when idle. 60 Hz still quite a lot but we | ||
592 | can't wait too long after the user tries to interact again with the app. In any | ||
593 | case, on macOS SDL_WaitEvent() seems to use 10x more CPU time than sleeping. */ | ||
594 | SDL_Delay(1000 / 60); | ||
595 | } | ||
596 | #endif | ||
542 | backToMainLoop:; | 597 | backToMainLoop:; |
543 | } | 598 | } |
544 | 599 | ||
@@ -586,10 +641,10 @@ static int resizeWatcher_(void *user, SDL_Event *event) { | |||
586 | 641 | ||
587 | static int run_App_(iApp *d) { | 642 | static int run_App_(iApp *d) { |
588 | arrange_Widget(findWidget_App("root")); | 643 | arrange_Widget(findWidget_App("root")); |
589 | d->running = iTrue; | 644 | d->isRunning = iTrue; |
590 | SDL_EventState(SDL_DROPFILE, SDL_ENABLE); /* open files via drag'n'drop */ | 645 | SDL_EventState(SDL_DROPFILE, SDL_ENABLE); /* open files via drag'n'drop */ |
591 | SDL_AddEventWatch(resizeWatcher_, d); | 646 | SDL_AddEventWatch(resizeWatcher_, d); |
592 | while (d->running) { | 647 | while (d->isRunning) { |
593 | processEvents_App(waitForNewEvents_AppEventMode); | 648 | processEvents_App(waitForNewEvents_AppEventMode); |
594 | runTickers_App_(d); | 649 | runTickers_App_(d); |
595 | refresh_App(); | 650 | refresh_App(); |
@@ -600,6 +655,9 @@ static int run_App_(iApp *d) { | |||
600 | 655 | ||
601 | void refresh_App(void) { | 656 | void refresh_App(void) { |
602 | iApp *d = &app_; | 657 | iApp *d = &app_; |
658 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
659 | if (d->isIdling) return; | ||
660 | #endif | ||
603 | destroyPending_Widget(); | 661 | destroyPending_Widget(); |
604 | draw_Window(d->window); | 662 | draw_Window(d->window); |
605 | set_Atomic(&d->pendingRefresh, iFalse); | 663 | set_Atomic(&d->pendingRefresh, iFalse); |
@@ -657,6 +715,9 @@ int run_App(int argc, char **argv) { | |||
657 | 715 | ||
658 | void postRefresh_App(void) { | 716 | void postRefresh_App(void) { |
659 | iApp *d = &app_; | 717 | iApp *d = &app_; |
718 | #if defined (LAGRANGE_IDLE_SLEEP) | ||
719 | d->isIdling = iFalse; | ||
720 | #endif | ||
660 | const iBool wasPending = exchange_Atomic(&d->pendingRefresh, iTrue); | 721 | const iBool wasPending = exchange_Atomic(&d->pendingRefresh, iTrue); |
661 | if (!wasPending) { | 722 | if (!wasPending) { |
662 | SDL_Event ev; | 723 | SDL_Event ev; |
@@ -1085,12 +1146,12 @@ iBool handleCommand_App(const char *cmd) { | |||
1085 | } | 1146 | } |
1086 | else if (equal_Command(cmd, "prefs.sideicon.changed")) { | 1147 | else if (equal_Command(cmd, "prefs.sideicon.changed")) { |
1087 | d->prefs.sideIcon = arg_Command(cmd) != 0; | 1148 | d->prefs.sideIcon = arg_Command(cmd) != 0; |
1088 | refresh_App(); | 1149 | postRefresh_App(); |
1089 | return iTrue; | 1150 | return iTrue; |
1090 | } | 1151 | } |
1091 | else if (equal_Command(cmd, "prefs.hoveroutline.changed")) { | 1152 | else if (equal_Command(cmd, "prefs.hoveroutline.changed")) { |
1092 | d->prefs.hoverOutline = arg_Command(cmd) != 0; | 1153 | d->prefs.hoverOutline = arg_Command(cmd) != 0; |
1093 | refresh_App(); | 1154 | postRefresh_App(); |
1094 | return iTrue; | 1155 | return iTrue; |
1095 | } | 1156 | } |
1096 | else if (equal_Command(cmd, "saturation.set")) { | 1157 | else if (equal_Command(cmd, "saturation.set")) { |
@@ -1373,13 +1434,13 @@ iBool handleCommand_App(const char *cmd) { | |||
1373 | } | 1434 | } |
1374 | else if (equal_Command(cmd, "feeds.update.started")) { | 1435 | else if (equal_Command(cmd, "feeds.update.started")) { |
1375 | setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iFalse); | 1436 | setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iFalse); |
1376 | refresh_App(); | 1437 | postRefresh_App(); |
1377 | return iFalse; | 1438 | return iFalse; |
1378 | } | 1439 | } |
1379 | else if (equal_Command(cmd, "feeds.update.finished")) { | 1440 | else if (equal_Command(cmd, "feeds.update.finished")) { |
1380 | setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iTrue); | 1441 | setFlags_Widget(findWidget_App("feeds.progress"), hidden_WidgetFlag, iTrue); |
1381 | refreshFinished_Feeds(); | 1442 | refreshFinished_Feeds(); |
1382 | refresh_App(); | 1443 | postRefresh_App(); |
1383 | return iFalse; | 1444 | return iFalse; |
1384 | } | 1445 | } |
1385 | else if (equal_Command(cmd, "visited.changed")) { | 1446 | else if (equal_Command(cmd, "visited.changed")) { |