@@ -78,15 +78,16 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*);
7878
7979struct ImGui_ImplWin32_Data
8080{
81+ INT64 Time;
82+ INT64 TicksPerSecond;
8183 HWND hWnd;
8284 HWND MouseHwnd;
8385 bool MouseTracked;
84- int MouseButtonsDown;
85- INT64 Time;
86- INT64 TicksPerSecond;
87- ImGuiMouseCursor LastMouseCursor;
8886 bool HasGamepad;
8987 bool WantUpdateHasGamepad;
88+ int MouseButtonsDown;
89+ int MouseX, MouseY; // against spurious WM_MOUSEMOVE events
90+ ImGuiMouseCursor LastMouseCursor;
9091
9192#ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
9293 HMODULE XInputDLL;
@@ -129,7 +130,10 @@ bool ImGui_ImplWin32_Init(void* hwnd)
129130 bd->WantUpdateHasGamepad = true ;
130131 bd->TicksPerSecond = perf_frequency;
131132 bd->Time = perf_counter;
133+ // bd->LastMouseCursor = ImGuiMouseCursor_Arrow; //windows default is the arrow, no need to reset it
132134 bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
135+ bd->MouseX = -1 ;
136+ bd->MouseY = -1 ;
133137
134138 // Set platform dependent data in viewport
135139 ImGui::GetMainViewport ()->PlatformHandleRaw = (void *)hwnd;
@@ -180,8 +184,15 @@ static bool ImGui_ImplWin32_UpdateMouseCursor()
180184 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
181185 return false ;
182186
183- ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor ();
184- if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor )
187+ ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData ();
188+
189+ ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor ();
190+ if (bd->LastMouseCursor == mouse_cursor)
191+ return false ;
192+
193+ bd->LastMouseCursor = mouse_cursor;
194+
195+ if (mouse_cursor == ImGuiMouseCursor_None /* || io.MouseDrawCursor*/ )
185196 {
186197 // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
187198 ::SetCursor (NULL );
@@ -190,7 +201,7 @@ static bool ImGui_ImplWin32_UpdateMouseCursor()
190201 {
191202 // Show OS mouse cursor
192203 LPTSTR win32_cursor = IDC_ARROW;
193- switch (imgui_cursor )
204+ switch (mouse_cursor )
194205 {
195206 case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break ;
196207 case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break ;
@@ -328,20 +339,59 @@ static void ImGui_ImplWin32_UpdateGamepads()
328339#endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD
329340}
330341
331- void ImGui_ImplWin32_NewFrame ()
342+ bool ImGui_ImplWin32_NewFrame (bool poll_only )
332343{
333344 ImGuiIO& io = ImGui::GetIO ();
334345 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData ();
335346 IM_ASSERT (bd != NULL && " Did you call ImGui_ImplWin32_Init()?" );
336347
348+ INT64 current_time;
349+
350+
351+
352+
353+ for (;;)
354+ {
355+ ::QueryPerformanceCounter ((LARGE_INTEGER*)¤t_time);
356+
357+ double next_refresh = !poll_only ? io.NextRefresh : 0.0 ;
358+
359+ double cur_delta = double (current_time - bd->Time ) / bd->TicksPerSecond ;
360+ if (cur_delta <= next_refresh)
361+ {
362+ double ms_to_wait_double = (next_refresh - cur_delta) * 1000 .0f ;
363+ unsigned int ms_to_wait = ms_to_wait_double >= MAXDWORD ? INFINITE : unsigned int (ms_to_wait_double);
364+ if (ms_to_wait)
365+ MsgWaitForMultipleObjectsEx (0 , nullptr , ms_to_wait, QS_ALLEVENTS, 0 );
366+ }
367+
368+
369+ MSG msg;
370+ while (::PeekMessage (&msg, NULL , 0U , 0U , PM_REMOVE))
371+ {
372+ if (msg.message != WM_QUIT)
373+ {
374+ ::TranslateMessage (&msg);
375+ ::DispatchMessage (&msg);
376+ continue ;
377+ }
378+ return false ;
379+ }
380+
381+
382+ if (cur_delta <= next_refresh)
383+ continue ;
384+
385+ break ;
386+ }
387+
388+
337389 // Setup display size (every frame to accommodate for window resizing)
338390 RECT rect = { 0 , 0 , 0 , 0 };
339391 ::GetClientRect (bd->hWnd, &rect);
340392 io.DisplaySize = ImVec2 ((float )(rect.right - rect.left ), (float )(rect.bottom - rect.top ));
341393
342394 // Setup time step
343- INT64 current_time = 0 ;
344- ::QueryPerformanceCounter ((LARGE_INTEGER*)¤t_time);
345395 io.DeltaTime = (float )(current_time - bd->Time ) / bd->TicksPerSecond ;
346396 bd->Time = current_time;
347397
@@ -352,15 +402,12 @@ void ImGui_ImplWin32_NewFrame()
352402 ImGui_ImplWin32_ProcessKeyEventsWorkarounds ();
353403
354404 // Update OS mouse cursor with the cursor requested by imgui
355- ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor ();
356- if (bd->LastMouseCursor != mouse_cursor)
357- {
358- bd->LastMouseCursor = mouse_cursor;
359- ImGui_ImplWin32_UpdateMouseCursor ();
360- }
361-
405+ ImGui_ImplWin32_UpdateMouseCursor ();
406+
362407 // Update game controllers (if enabled and available)
363408 ImGui_ImplWin32_UpdateGamepads ();
409+
410+ return true ;
364411}
365412
366413// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255)
@@ -504,33 +551,57 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
504551 if (ImGui::GetCurrentContext () == NULL )
505552 return 0 ;
506553
554+ const char * key_refresh_reason;
507555 ImGuiIO& io = ImGui::GetIO ();
508556 ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData ();
509557
510558 switch (msg)
511559 {
512560 case WM_MOUSEMOVE:
561+
513562 // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events
563+ if (!bd->MouseHwnd ) // mouse entered client area
564+ bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
565+
514566 bd->MouseHwnd = hwnd;
515567 if (!bd->MouseTracked )
516568 {
517569 TRACKMOUSEEVENT tme = { sizeof (tme), TME_LEAVE, hwnd, 0 };
518570 ::TrackMouseEvent (&tme);
519571 bd->MouseTracked = true ;
520572 }
521- io.AddMousePosEvent ((float )GET_X_LPARAM (lParam), (float )GET_Y_LPARAM (lParam));
573+
574+ {
575+ int m_x = GET_X_LPARAM (lParam), m_y = GET_Y_LPARAM (lParam);
576+ if (bd->MouseX != m_x || bd->MouseY != m_y) // spurious WM_MOUSEMOVE events are a real thing. don't act on them
577+ {
578+ bd->MouseX = m_x, bd->MouseY = m_y;
579+
580+ io.SetNextRefresh (0 , " mouse move" ); // return 0;
581+
582+ io.AddMousePosEvent ((float )m_x, (float )m_y);
583+ }
584+ }
522585 break ;
523586 case WM_MOUSELEAVE:
524587 if (bd->MouseHwnd == hwnd)
525588 bd->MouseHwnd = NULL ;
526589 bd->MouseTracked = false ;
590+ // bd->LastMouseCursor = ImGuiMouseCursor_COUNT;
591+ bd->MouseX = -1 , bd->MouseY = -1 ;
527592 io.AddMousePosEvent (-FLT_MAX, -FLT_MAX);
528593 break ;
529- case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
530- case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
531- case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
532- case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
594+ case WM_LBUTTONDOWN: key_refresh_reason = " mouse lbuttonup down" ; goto md;
595+ case WM_LBUTTONDBLCLK: key_refresh_reason = " mouse lbuttonup dblclk" ; goto md;
596+ case WM_RBUTTONDOWN: key_refresh_reason = " mouse rbuttonup down" ; goto md;
597+ case WM_RBUTTONDBLCLK: key_refresh_reason = " mouse rbuttonup dblclk" ; goto md;
598+ case WM_MBUTTONDOWN: key_refresh_reason = " mouse mbuttonup donw" ; goto md;
599+ case WM_MBUTTONDBLCLK: key_refresh_reason = " mouse mbuttonup dblclk" ; goto md;
600+ case WM_XBUTTONDOWN: key_refresh_reason = " mouse xbuttonup down" ; goto md;
601+ case WM_XBUTTONDBLCLK: key_refresh_reason = " mouse xbuttonup dblclk" ;
602+ md:
533603 {
604+ io.SetNextRefresh (0 , key_refresh_reason);
534605 int button = 0 ;
535606 if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0 ; }
536607 if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1 ; }
@@ -542,11 +613,13 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
542613 io.AddMouseButtonEvent (button, true );
543614 return 0 ;
544615 }
545- case WM_LBUTTONUP:
546- case WM_RBUTTONUP:
547- case WM_MBUTTONUP:
548- case WM_XBUTTONUP:
616+ case WM_LBUTTONUP: key_refresh_reason = " mouse lbutton up" ; goto mu;
617+ case WM_RBUTTONUP:key_refresh_reason = " mouse rbutton up" ; goto mu;
618+ case WM_MBUTTONUP:key_refresh_reason = " mouse mbutton up" ; goto mu;
619+ case WM_XBUTTONUP:key_refresh_reason = " mouse xbutton up" ;
620+ mu:
549621 {
622+ io.SetNextRefresh (0 , key_refresh_reason);
550623 int button = 0 ;
551624 if (msg == WM_LBUTTONUP) { button = 0 ; }
552625 if (msg == WM_RBUTTONUP) { button = 1 ; }
@@ -559,19 +632,27 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
559632 return 0 ;
560633 }
561634 case WM_MOUSEWHEEL:
635+ io.SetNextRefresh (0 , " wheel up" );
562636 io.AddMouseWheelEvent (0 .0f , (float )GET_WHEEL_DELTA_WPARAM (wParam) / (float )WHEEL_DELTA);
563637 return 0 ;
564638 case WM_MOUSEHWHEEL:
639+ io.SetNextRefresh (0 , " wheel down" );
565640 io.AddMouseWheelEvent ((float )GET_WHEEL_DELTA_WPARAM (wParam) / (float )WHEEL_DELTA, 0 .0f );
566641 return 0 ;
567642 case WM_KEYDOWN:
643+ key_refresh_reason = " key down" ; goto l;
568644 case WM_KEYUP:
645+ key_refresh_reason = " key up" ; goto l;
569646 case WM_SYSKEYDOWN:
647+ key_refresh_reason = " syskey down" ; goto l;
570648 case WM_SYSKEYUP:
571- {
572- const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN);
649+ key_refresh_reason = " key up" ;
650+ l:
651+ {
652+ const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN);
573653 if (wParam < 256 )
574654 {
655+ io.SetNextRefresh (0 , key_refresh_reason);
575656 // Submit modifiers
576657 ImGui_ImplWin32_UpdateKeyModifiers ();
577658
0 commit comments