diff --git a/docs/TODO.txt b/docs/TODO.txt index 07f6676bb971..35529a06e1ef 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -342,7 +342,6 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - inputs: add mouse cursor for unavailable/no? IDC_NO/SDL_SYSTEM_CURSOR_NO. - 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 back-end 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: make the ImGuiCond values linear (non-power-of-two). internal storage for ImGuiWindow can use integers to combine into flags (Why?) @@ -363,9 +362,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - demo: add virtual scrolling example? - demo: demonstrate Plot offset - examples: window minimize, maximize (#583) - - examples: provide a zero frame-rate/idle example. - examples: apple: example_apple should be using modern GL3. - - examples: 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) - examples: opengl: rename imgui_impl_opengl2 to impl_opengl_legacy and imgui_impl_opengl3 to imgui_impl_opengl? (#1900) - examples: opengl: could use a single vertex buffer and glBufferSubData for uploads? - examples: opengl: explicitly disable GL_STENCIL_TEST in bindings. diff --git a/examples/example_allegro5/main.cpp b/examples/example_allegro5/main.cpp index 59e19370cea8..52cd521a4d76 100644 --- a/examples/example_allegro5/main.cpp +++ b/examples/example_allegro5/main.cpp @@ -63,6 +63,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)) { @@ -105,6 +106,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 4833d1b05500..63056ea365e8 100644 --- a/examples/example_glfw_opengl3/main.cpp +++ b/examples/example_glfw_opengl3/main.cpp @@ -126,6 +126,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 @@ -157,6 +158,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 19d34e9ad5d0..ace4bd8c480b 100644 --- a/examples/example_sdl_opengl3/main.cpp +++ b/examples/example_sdl_opengl3/main.cpp @@ -120,6 +120,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)) { @@ -159,6 +160,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 0a8f09b2c43f..1a6b3edfef8b 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -80,18 +80,22 @@ int main(int, char**) // Main loop MSG msg; ZeroMemory(&msg, sizeof(msg)); - while (msg.message != WM_QUIT) + bool done = false; + while (!done) { // Poll and handle messages (inputs, window resize, etc.) // 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. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + ImGui_ImplWin32_WaitForEvent(); + while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - continue; + + if (msg.message == WM_QUIT) + done = true; } // Start the Dear ImGui frame @@ -123,6 +127,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/imgui_impl_allegro5.cpp b/examples/imgui_impl_allegro5.cpp index 525ad5c976e1..4a3477625ae0 100644 --- a/examples/imgui_impl_allegro5.cpp +++ b/examples/imgui_impl_allegro5.cpp @@ -32,6 +32,7 @@ #include // uint64_t #include // memcpy +#include // isinf #include "imgui.h" #include "imgui_impl_allegro5.h" @@ -317,6 +318,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. @@ -325,6 +344,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/examples/imgui_impl_allegro5.h b/examples/imgui_impl_allegro5.h index bc995a5ace22..432eeec54799 100644 --- a/examples/imgui_impl_allegro5.h +++ b/examples/imgui_impl_allegro5.h @@ -16,12 +16,14 @@ #pragma once 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 ImGui state. diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index a9e72c6cbb32..54b95e7c21fe 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -37,6 +37,8 @@ #include "imgui.h" #include "imgui_impl_glfw.h" +#include // isinf + // GLFW #include #ifdef _WIN32 @@ -66,6 +68,7 @@ static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; // Chain GLFW callbacks: 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; @@ -84,6 +87,9 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti if (g_PrevUserCallbackMousebutton != NULL) 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; } @@ -94,16 +100,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) g_PrevUserCallbackKey(window, key, scancode, action, mods); ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; + if (action == GLFW_PRESS) io.KeysDown[key] = true; if (action == GLFW_RELEASE) @@ -122,6 +143,7 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) g_PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(); + io.FrameCountSinceLastInput = 0; io.AddInputCharacter(c); } @@ -185,6 +207,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw { 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); } @@ -213,6 +236,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/examples/imgui_impl_glfw.h b/examples/imgui_impl_glfw.h index ccbe840d4471..9fb911540424 100644 --- a/examples/imgui_impl_glfw.h +++ b/examples/imgui_impl_glfw.h @@ -24,6 +24,7 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool in IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(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(); // InitXXX function with 'install_callbacks=true': install GLFW callbacks. They will call user's previously installed callbacks, if any. // InitXXX function with 'install_callbacks=false': do not install GLFW callbacks. You will need to call them yourself from your own GLFW callbacks. diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index 426775810170..48d7cf9db2b8 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -50,6 +50,8 @@ #include "TargetConditionals.h" #endif +#include // isinf + #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) @@ -73,6 +75,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. @@ -81,6 +103,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/examples/imgui_impl_sdl.h b/examples/imgui_impl_sdl.h index 376e622c7fd1..3b6ceefed4c1 100644 --- a/examples/imgui_impl_sdl.h +++ b/examples/imgui_impl_sdl.h @@ -24,4 +24,5 @@ IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window); IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(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/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 8d46d942c96b..bae51df5b075 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -14,6 +14,7 @@ #endif #include #include +#include // isinf #include // CHANGELOG @@ -236,6 +237,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 @@ -257,6 +272,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/examples/imgui_impl_win32.h b/examples/imgui_impl_win32.h index 761618605cba..0098ad204e49 100644 --- a/examples/imgui_impl_win32.h +++ b/examples/imgui_impl_win32.h @@ -12,6 +12,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(); // Handler for Win32 messages, update mouse/keyboard data. // You may or not need this for your implementation, but it can serve as reference for handling inputs. diff --git a/imgui.cpp b/imgui.cpp index 93b15ddb94a0..6fe59e7ed142 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3684,6 +3684,23 @@ static void NewFrameSanityChecks() g.IO.ConfigWindowsResizeFromEdges = false; } +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() ?"); @@ -3724,6 +3741,7 @@ void ImGui::NewFrame() g.FrameCount += 1; g.TooltipOverrideCount = 0; g.WindowsActiveCount = 0; + g.MaxWaitBeforeNextFrame = INFINITY; // Setup current font and draw list shared data g.IO.Fonts->Locked = true; @@ -4213,6 +4231,7 @@ void ImGui::EndFrame() // End frame g.FrameScopeActive = 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 9697c80db755..2dee98fabde2 100644 --- a/imgui.h +++ b/imgui.h @@ -223,6 +223,15 @@ namespace ImGui IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can get call GetDrawData() to obtain it and run your rendering function. (Obsolete: this used to call io.RenderDrawListsFn(). Nowadays, we allow and prefer calling your render function yourself.) 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 (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. @@ -1005,6 +1014,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 back-end. ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. Use if the back-end 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). // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core Dear ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. @@ -1462,6 +1472,7 @@ struct ImGuiIO float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform back-end). 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 ec1ada820a13..bfe05975a8ef 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -347,6 +347,8 @@ void ImGui::ShowDemoWindow(bool* p_open) } ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); ImGui::SameLine(); HelpMarker("Instruct back-end 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::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); HelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); @@ -1069,6 +1071,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)); @@ -3076,6 +3080,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.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); if (io.ConfigInputTextCursorBlink) ImGui::Text("io.ConfigInputTextCursorBlink"); diff --git a/imgui_internal.h b/imgui_internal.h index 6d3931039e89..0931ff8fb8d0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -924,6 +924,9 @@ struct ImGuiContext ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + // Power saving mode + double MaxWaitBeforeNextFrame; // How much time, in seconds, can we wait for events before starting the next frame + // Navigation data (for gamepad/keyboard) 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 bcd051e57726..7910cc139fb8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4076,8 +4076,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)