From befd31a57e47151f3c66b1e7ef1861eb1355edfa Mon Sep 17 00:00:00 2001 From: core Date: Fri, 24 Apr 2020 04:05:09 +1000 Subject: [PATCH 1/2] Added power save mode --- backends/imgui_impl_allegro5.cpp | 21 ++++++++++++ backends/imgui_impl_allegro5.h | 2 ++ backends/imgui_impl_glfw.cpp | 39 +++++++++++++++++++++++ backends/imgui_impl_glfw.h | 1 + backends/imgui_impl_sdl.cpp | 24 ++++++++++++++ backends/imgui_impl_sdl.h | 1 + backends/imgui_impl_win32.cpp | 18 +++++++++++ backends/imgui_impl_win32.h | 1 + docs/TODO.txt | 3 -- examples/example_allegro5/main.cpp | 2 ++ examples/example_glfw_opengl3/main.cpp | 2 ++ examples/example_sdl_opengl3/main.cpp | 2 ++ examples/example_win32_directx11/main.cpp | 6 +++- imgui.cpp | 19 +++++++++++ imgui.h | 11 +++++++ imgui_demo.cpp | 6 ++++ imgui_internal.h | 3 ++ imgui_widgets.cpp | 24 ++++++++++++-- 18 files changed, 179 insertions(+), 6 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index c4832b6b151a..d2a22a66ce59 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -35,6 +35,7 @@ #include // uint64_t #include // memcpy +#include // isinf #include "imgui.h" #include "imgui_impl_allegro5.h" @@ -320,6 +321,24 @@ void ImGui_ImplAllegro5_Shutdown() g_ClipboardTextData = NULL; } +void ImGui_ImplAllegro5_WaitForEvent(ALLEGRO_EVENT_QUEUE* queue) +{ + if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnablePowerSavingMode)) + return; + + int display_flags = al_get_display_flags(g_Display); + bool window_is_hidden = display_flags & ALLEGRO_MINIMIZED; + double waiting_time = window_is_hidden ? INFINITY : ImGui::GetEventWaitingTime(); + if (waiting_time > 0.0) + { + if (isinf(waiting_time)) + al_wait_for_event(queue, NULL); + else + al_wait_for_event_timed(queue, NULL, waiting_time); + } +} + + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -328,6 +347,8 @@ bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev) { ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; + switch (ev->type) { case ALLEGRO_EVENT_MOUSE_AXES: diff --git a/backends/imgui_impl_allegro5.h b/backends/imgui_impl_allegro5.h index ef91d4b32ac2..150c767c3cb0 100644 --- a/backends/imgui_impl_allegro5.h +++ b/backends/imgui_impl_allegro5.h @@ -17,12 +17,14 @@ #include "imgui.h" // IMGUI_IMPL_API struct ALLEGRO_DISPLAY; +struct ALLEGRO_EVENT_QUEUE; union ALLEGRO_EVENT; IMGUI_IMPL_API bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display); IMGUI_IMPL_API void ImGui_ImplAllegro5_Shutdown(); IMGUI_IMPL_API void ImGui_ImplAllegro5_NewFrame(); IMGUI_IMPL_API void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data); +IMGUI_IMPL_API void ImGui_ImplAllegro5_WaitForEvent(ALLEGRO_EVENT_QUEUE* queue); IMGUI_IMPL_API bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* event); // Use if you want to reset your rendering device without losing Dear ImGui state. diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 2b6c34ca430a..6eb0061c3f54 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -45,6 +45,8 @@ #include "imgui.h" #include "imgui_impl_glfw.h" +#include // isinf + // GLFW #include #ifdef _WIN32 @@ -91,6 +93,7 @@ static bool g_WantUpdateMonitors = true; // Chain GLFW callbacks for main viewport: our callbacks will call the user's previously installed callbacks, if any. static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; static GLFWscrollfun g_PrevUserCallbackScroll = NULL; +static GLFWcursorposfun g_PrevUserCallbackCursorPos = NULL; static GLFWkeyfun g_PrevUserCallbackKey = NULL; static GLFWcharfun g_PrevUserCallbackChar = NULL; static GLFWmonitorfun g_PrevUserCallbackMonitor = NULL; @@ -115,6 +118,9 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti if (g_PrevUserCallbackMousebutton != NULL && window == g_Window) g_PrevUserCallbackMousebutton(window, button, action, mods); + ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; + if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) g_MouseJustPressed[button] = true; } @@ -125,16 +131,31 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo g_PrevUserCallbackScroll(window, xoffset, yoffset); ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; io.MouseWheelH += (float)xoffset; io.MouseWheel += (float)yoffset; } +void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double xpos, double ypos) +{ + if (g_PrevUserCallbackCursorPos != NULL) + g_PrevUserCallbackCursorPos(window, xpos, ypos); + + ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; + + // Here, we just take note of the event without actually processing the cursor position. + // This is done in ImGui_ImplGlfw_NewFrame() / ImGui_ImplGlfw_UpdateMousePosAndButtons(). +} + void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (g_PrevUserCallbackKey != NULL && window == g_Window) g_PrevUserCallbackKey(window, key, scancode, action, mods); ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; + if (action == GLFW_PRESS) { io.KeysDown[key] = true; @@ -163,6 +184,7 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) g_PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; io.AddInputCharacter(c); } @@ -248,6 +270,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw g_InstalledCallbacks = true; g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + g_PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback); g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); g_PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); @@ -306,6 +329,22 @@ void ImGui_ImplGlfw_Shutdown() g_ClientApi = GlfwClientApi_Unknown; } +void ImGui_ImplGlfw_WaitForEvent() +{ + if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnablePowerSavingMode)) + return; + + bool window_is_hidden = !glfwGetWindowAttrib(g_Window, GLFW_VISIBLE) || glfwGetWindowAttrib(g_Window, GLFW_ICONIFIED); + double waiting_time = window_is_hidden ? INFINITY : ImGui::GetEventWaitingTime(); + if (waiting_time > 0.0) + { + if (isinf(waiting_time)) + glfwWaitEvents(); + else + glfwWaitEventsTimeout(waiting_time); + } +} + static void ImGui_ImplGlfw_UpdateMousePosAndButtons() { // Update buttons diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 18d7ce01ecb0..d064a0e13198 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -31,6 +31,7 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool in IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplGlfw_WaitForEvent(); // GLFW callbacks // - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any. diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 5a1d1f77dd48..352bf06accac 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -59,6 +59,8 @@ #include "TargetConditionals.h" #endif +#include // isinf + #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_WINDOW_ALPHA SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) @@ -97,6 +99,26 @@ static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) SDL_SetClipboardText(text); } +void ImGui_ImplSDL2_WaitForEvent() +{ + if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnablePowerSavingMode)) + return; + + Uint32 window_flags = SDL_GetWindowFlags(g_Window); + bool window_is_hidden = window_flags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED); + double waiting_time = window_is_hidden ? INFINITY : ImGui::GetEventWaitingTime(); + if (waiting_time > 0.0) + { + if (isinf(waiting_time)) + SDL_WaitEvent(NULL); + else + { + const int waiting_time_ms = (int)(1000.0 * ImGui::GetEventWaitingTime()); + SDL_WaitEventTimeout(NULL, waiting_time_ms); + } + } +} + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. @@ -105,6 +127,8 @@ static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text) bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; + switch (event->type) { case SDL_MOUSEWHEEL: diff --git a/backends/imgui_impl_sdl.h b/backends/imgui_impl_sdl.h index 6d8a071505b4..4ee23fd7867f 100644 --- a/backends/imgui_impl_sdl.h +++ b/backends/imgui_impl_sdl.h @@ -28,4 +28,5 @@ IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window); IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown(); IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window); +IMGUI_IMPL_API void ImGui_ImplSDL2_WaitForEvent(); IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event); diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index b023497c424a..cff003425b7a 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -18,6 +18,7 @@ #define WIN32_LEAN_AND_MEAN #endif #include +#include // isinf #include #include @@ -384,6 +385,20 @@ void ImGui_ImplWin32_NewFrame() ImGui_ImplWin32_UpdateGamepads(); } +void ImGui_ImplWin32_WaitForEvent() +{ + if (!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_EnablePowerSavingMode)) + return; + + BOOL window_is_hidden = !IsWindowVisible(g_hWnd) || IsIconic(g_hWnd); + double waiting_time = window_is_hidden ? INFINITE : ImGui::GetEventWaitingTime(); + if (waiting_time > 0.0) + { + DWORD waiting_time_ms = isinf(waiting_time) ? INFINITE : (DWORD)(1000.0 * waiting_time); + ::MsgWaitForMultipleObjectsEx(0, NULL, waiting_time_ms, QS_ALLINPUT, MWMO_INPUTAVAILABLE|MWMO_ALERTABLE); + } +} + // Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E @@ -410,6 +425,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; ImGuiIO& io = ImGui::GetIO(); + + io.FrameCountSinceLastInput = 0; + switch (msg) { case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 13402e350b4e..7d9998a2e4b1 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -18,6 +18,7 @@ IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplWin32_WaitForEvent(); // Win32 message handler your application need to call. // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. diff --git a/docs/TODO.txt b/docs/TODO.txt index 8b2357b3b01f..c325a7e9ba8f 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -403,7 +403,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - inputs/io: clarify/standardize/expose repeat rate and repeat delays (#1808) - inputs/scrolling: support for smooth scrolling (#2462, #2569) - - misc: idle: expose "woken up" boolean (set by inputs) and/or animation time (for cursor blink) for backend to be able stop refreshing easily. - misc: idle: if cursor blink if the _only_ visible animation, core imgui could rewrite vertex alpha to avoid CPU pass on ImGui:: calls. - misc: idle: if cursor blink if the _only_ visible animation, could even expose a dirty rectangle that optionally can be leverage by some app to render in a smaller viewport, getting rid of much pixel shading cost. - misc: no way to run a root-most GetID() with ImGui:: api since there's always a Debug window in the stack. (mentioned in #2960) @@ -423,12 +422,10 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - demo: window size constraint: square demo is broken when resizing from edges (#1975), would need to rework the callback system to solve this - examples: window minimize, maximize (#583) - - examples: provide a zero frame-rate/idle example. - examples: dx11/dx12: try to use new swapchain blit models (#2970) - backends: move to backends/ folder? - backends: report it better when not able to create texture? - backends: apple: example_apple should be using modern GL3. - - backends: glfw: could go idle when minimized? if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { glfwWaitEvents(); continue; } // issue: DeltaTime will be super high on resume, perhaps provide a way to let impl know (#440) - backends: opengl: rename imgui_impl_opengl2 to impl_opengl_legacy and imgui_impl_opengl3 to imgui_impl_opengl? (#1900) - backends: opengl: could use a single vertex buffer and glBufferSubData for uploads? - backends: opengl: explicitly disable GL_STENCIL_TEST in bindings. diff --git a/examples/example_allegro5/main.cpp b/examples/example_allegro5/main.cpp index f38e92c9ed08..996eb281ceb8 100644 --- a/examples/example_allegro5/main.cpp +++ b/examples/example_allegro5/main.cpp @@ -65,6 +65,7 @@ int main(int, char**) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + ImGui_ImplAllegro5_WaitForEvent(queue); ALLEGRO_EVENT ev; while (al_get_next_event(queue, &ev)) { @@ -107,6 +108,7 @@ int main(int, char**) ImGui::Text("counter = %d", counter); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Text("Frames since last input: %d", ImGui::GetIO().FrameCountSinceLastInput); ImGui::End(); } diff --git a/examples/example_glfw_opengl3/main.cpp b/examples/example_glfw_opengl3/main.cpp index edccc4e2f401..b983628b4938 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -167,6 +167,7 @@ int main(int, char**) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + ImGui_ImplGlfw_WaitForEvent(); glfwPollEvents(); // Start the Dear ImGui frame @@ -198,6 +199,7 @@ int main(int, char**) ImGui::Text("counter = %d", counter); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Text("Frames since last input: %d", ImGui::GetIO().FrameCountSinceLastInput); ImGui::End(); } diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp index c9b2d91cbb61..1f0394345133 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -164,6 +164,7 @@ int main(int, char**) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + ImGui_ImplSDL2_WaitForEvent(); SDL_Event event; while (SDL_PollEvent(&event)) { @@ -203,6 +204,7 @@ int main(int, char**) ImGui::Text("counter = %d", counter); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Text("Frames since last input: %d", ImGui::GetIO().FrameCountSinceLastInput); ImGui::End(); } diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index d6378d804101..debc3da25843 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -95,6 +95,8 @@ int main(int, char**) ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop + MSG msg; + ZeroMemory(&msg, sizeof(msg)); bool done = false; while (!done) { @@ -103,11 +105,12 @@ int main(int, char**) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - MSG msg; + ImGui_ImplWin32_WaitForEvent(); while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); + if (msg.message == WM_QUIT) done = true; } @@ -143,6 +146,7 @@ int main(int, char**) ImGui::Text("counter = %d", counter); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Text("Frames since last input: %d", ImGui::GetIO().FrameCountSinceLastInput); ImGui::End(); } diff --git a/imgui.cpp b/imgui.cpp index 2667d8859f97..0ea12e00ef05 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4012,6 +4012,23 @@ ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() return key_mod_flags; } +double ImGui::GetEventWaitingTime() +{ + ImGuiContext& g = *GImGui; + + if ((g.IO.ConfigFlags & ImGuiConfigFlags_EnablePowerSavingMode) && g.IO.FrameCountSinceLastInput > 2) + return ImMax(0.0, g.MaxWaitBeforeNextFrame); + + return 0.0; +} + +void ImGui::SetMaxWaitBeforeNextFrame(double time) +{ + ImGuiContext& g = *GImGui; + + g.MaxWaitBeforeNextFrame = ImMin(g.MaxWaitBeforeNextFrame, time); +} + void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); @@ -4039,6 +4056,7 @@ void ImGui::NewFrame() g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; g.MenusIdSubmittedThisFrame.resize(0); + g.MaxWaitBeforeNextFrame = INFINITY; // Calculate frame-rate for the user, as a purely luxurious feature g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; @@ -4647,6 +4665,7 @@ void ImGui::EndFrame() // End frame g.WithinFrameScope = false; g.FrameCountEnded = g.FrameCount; + g.IO.FrameCountSinceLastInput++; // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); diff --git a/imgui.h b/imgui.h index 8e75f542cddc..c98b9af9717c 100644 --- a/imgui.h +++ b/imgui.h @@ -281,6 +281,15 @@ namespace ImGui IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData(). IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. + // Power saving mode + // Disabled by default; enabled by setting ImGuiConfigFlags_EnablePowerSavingMode in ImGuiIO.ConfigFlags. + // Requires platform binding support. + // When enabled and supported, ImGui will wait for input events before starting new frames, instead of continuously polling, thereby helping to reduce power consumption. + // It will wake up periodically if a widget is animating (e.g. blinking InputText cursor). You can control this maximum wake-up timeout using SetMaxWaitBeforeNextFrame(), for example when your application is playing an animation. + // This wake-up/timeout event is disabled, and ImGui will wait for an input event, as long as the window is known, for sure, to be hidden. This depends on the platform binding, and does not work in all cases (e.g. if the window is in a logical/system 'visible' state, but currently sitting behind another, non-transparent window). + IMGUI_API double GetEventWaitingTime(); // in seconds; note that it can be zero (in which case you might want to peek/poll) or infinity (in which case you may have to use a non-timeout event waiting method). + IMGUI_API void SetMaxWaitBeforeNextFrame(double time); // in seconds + // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. @@ -1435,6 +1444,7 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the backend. ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. + ImGuiConfigFlags_EnablePowerSavingMode = 1 << 6, // Instruct imgui to help save power by not starting new frames when there are no user inputs or if the window is known not to be visible (both features require support in the platform binding). Use SetMaxWaitBeforeNextFrame() to let your application request a maximum wait before the next frame, which helps enforce a minimum frame rate (e.g. when playing an animation). // [BETA] Docking ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. @@ -1961,6 +1971,7 @@ struct ImGuiIO float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. + int FrameCountSinceLastInput; // How many frames since the last input event; a value of 0 indicates that the current frame was triggered by an input. IMGUI_API ImGuiIO(); }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 114e53f1b9ee..a408be1615c3 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -455,6 +455,9 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility."); + ImGui::CheckboxFlags("io.ConfigFlags: EnablePowerSavingMode", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_EnablePowerSavingMode); + ImGui::SameLine(); HelpMarker("Enable power saving mode, reducing the frame rate automatically when idle."); + ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable); ImGui::SameLine(); HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) @@ -1610,6 +1613,8 @@ static void ShowDemoWindowWidgets() { static bool animate = true; ImGui::Checkbox("Animate", &animate); + if (animate) + ImGui::SetMaxWaitBeforeNextFrame(1.0 / 30.0); // = 30fps static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); @@ -5767,6 +5772,7 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard) ImGui::Text(" NavNoCaptureKeyboard"); if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); + if (io.ConfigFlags & ImGuiConfigFlags_EnablePowerSavingMode) ImGui::Text(" EnablePowerSavingMode"); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) ImGui::Text(" DockingEnable"); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(" ViewportsEnable"); if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) ImGui::Text(" DpiEnableScaleViewports"); diff --git a/imgui_internal.h b/imgui_internal.h index 33cf71c24f29..b72a4f213478 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1563,6 +1563,9 @@ struct ImGuiContext ImGuiPlatformMonitor FallbackMonitor; // Virtual monitor used as fallback if backend doesn't provide monitor information. int ViewportFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter + // Power saving mode + double MaxWaitBeforeNextFrame; // How much time, in seconds, can we wait for events before starting the next frame + // Gamepad/keyboard Navigation ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' ImGuiID NavId; // Focused item for navigation diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1fa841a30828..6e532a163782 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4615,8 +4615,28 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + if (cursor_screen_rect.Overlaps(clip_rect)) + { + if (g.IO.ConfigInputTextCursorBlink) + { + double time_to_transition; + + if (state->CursorAnim <= 0.0f) + time_to_transition = 0.80f - state->CursorAnim; + else if (ImFmod(state->CursorAnim, 1.20f) <= 0.80f) + time_to_transition = 0.80f - ImFmod(state->CursorAnim, 1.20f); + else + time_to_transition = 1.20f - ImFmod(state->CursorAnim, 1.20f); + + // Make sure the next frame starts after the transition. + time_to_transition += 0.05f; + + SetMaxWaitBeforeNextFrame(time_to_transition); + } + + if (cursor_is_visible) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + } // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) if (!is_readonly) From e0a7a138e85685512d73d86b8b51d5e86980cd48 Mon Sep 17 00:00:00 2001 From: Core Date: Mon, 26 Apr 2021 22:36:43 +1000 Subject: [PATCH 2/2] Fixed bad merge --- examples/example_win32_directx11/main.cpp | 1 - imgui.h | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index debc3da25843..19eff53dcc84 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -110,7 +110,6 @@ int main(int, char**) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - if (msg.message == WM_QUIT) done = true; } diff --git a/imgui.h b/imgui.h index c98b9af9717c..f3f9f5024edd 100644 --- a/imgui.h +++ b/imgui.h @@ -1444,11 +1444,12 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the backend. ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. - ImGuiConfigFlags_EnablePowerSavingMode = 1 << 6, // Instruct imgui to help save power by not starting new frames when there are no user inputs or if the window is known not to be visible (both features require support in the platform binding). Use SetMaxWaitBeforeNextFrame() to let your application request a maximum wait before the next frame, which helps enforce a minimum frame rate (e.g. when playing an animation). // [BETA] Docking ImGuiConfigFlags_DockingEnable = 1 << 6, // Docking enable flags. + ImGuiConfigFlags_EnablePowerSavingMode = 1 << 7, // Instruct imgui to help save power by not starting new frames when there are no user inputs or if the window is known not to be visible (both features require support in the platform binding). Use SetMaxWaitBeforeNextFrame() to let your application request a maximum wait before the next frame, which helps enforce a minimum frame rate (e.g. when playing an animation). + // [BETA] Viewports // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiBackendFlags_PlatformHasViewports + ImGuiBackendFlags_RendererHasViewports set by the respective backends)