Rysowanie okna aplikacji, tylko wtedy gdy okno nie jest zminimalizowane

0

Witam,

ponieważ mam aplikacje która rysuje pewne rzeczy i pobiera trochę CPU chcę ją zatrzymywać w momencie gdy okno nie jest całkowicie widoczne.
Obecnie wykrywam czy okno jest widoczne przez WM_SIZE -> SIZE_MAXIMIZED / SIZE_RESTORED / SIZE_MINIMIZED
Jednak, gdy otwieram aplikacje i myszką klikam na inną na przykład notatnik w trybie maximized to ta moja aplikacja wciąż działa..

Korzystając z WM_SETFOCUS oraz WM_KILLFOCUS mogę ją wtedy zatrzymywać, ale.. nie chcę tego robić gdy aplikacja jest widoczna w tle, tylko gdy jest całkowicie niewidoczna.
Czy ktoś może mi poradzić jak to zrobić? Których metod użyć?

1

Jak chodzi o stany okna (normalne, minimalne, maksymalne), to zawsze możesz odczytać bieżący status lub zrobić zdarzenie na zmianę statusu.

Jak chodzi o wychwycenie całkowitego przykrycia Twojego programu czymś innym, to AFAIK nie ma takiej możliwości.

Ewentualnie (prawdopodobnie będzie to zawodne, ale spróbować nie zaszkodzi) można zrobić takie obejście, że w momencie uruchomienia programu i w każdym zdarzeniu zmieniającym położenie i wielkość okna. zrobić print screen okna i zapamiętać małe fragmenty, np. narożniki lub krawędzie. Potem co sekundę ponawiać czynność i porównywać te fragmenty z zapamiętanym. Jeżeli te miejsca są podobne, to znaczy, że Twój program jest widoczny i ma pracować z pełną prędkością.

Można próbować coś odczytywać z listy procesów Windows lub tablicy uchwytów okien i kontrolek, dla każdego procesu odczytać PID lub HWND okna, jednak nie wiem, czy można odczytać faktyczną kolejność namalowania okien i kontrolek w Windows. Gdyby była taka możliwość, to można byłoby stwierdzić, czy okno namalowane w dalszej kolejności, czyli bardziej na wierzchu, zasłania Twój program.

2

Dość dziwne zachowanie aplikacji, jeśli okno jest przykryte to i tak nie pobiera tyle samo procka co gdy jest widoczne bo nic faktycznie nie jest rysowane na ekranie tylko w pamięci. User nadal może chcieć podgląd na Alt+Tab i w miniaturce na pasku zadań.

Moim zdaniem najlepiej jeśli samemu pozwolisz userowi na pauzowanie / wznawianie animacji.

Jeśli jednak się upierasz przy Twoim pomyśle to moim zdaniem najlepszym rozwiązaniem będzie cykliczne listowanie wszystkich widocznych okien w systemie i sprawdzenie czy prostokąty na siebie nachodzą.
GetNextWindow według dokumentacji zwraca okna w kolejności Z-Order więc pierwsze wylistowane jest na samym wierzchu https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getnextwindow?redirectedfrom=MSDN
Plus: możesz wybrać w jakim stopniu okno może być zasłonięte i np reagować przy 90%, minus: nie będzie to wykrywało przypadków gdy inne okna nad Twoją aplikacją są półprzezroczyste / mają materiał typu acrylic / mica.

3

Jak milion lat temu jechałem Petzolda, a były to czasy że oszczędzało sie CPU / RAM, gdzieś od WM_PAINT docierało sie do prostokąta podlegajacemu malowaniu (nie zasłonietego)
https://learn.microsoft.com/en-us/windows/win32/gdi/wm-paint

Ale nic nie pamiętam w szczegółach.

6

@obscurity (a także @ZrobieDobrze) - to już nie są czasy Petzolda (swoją droga - genialna i bardzo obszerna książka, mam do dzisiaj) i stacking window manager, czyli mówiąc jaśniej - nie jest tak, że okno jest zakryte i potem się odsłania. Aktualnie windows działa na zasadzie composing window manager, czyli z grubsza można powiedzieć, że każda aplikacja się czuje, jakby była cały czas na wierzchu pulpitu i w pełni widoczna. Można to zobaczyć chociażby po tym, że jak najedziesz myszką na ikonę okna na pasku zadań, które jest schowane pod innymi, to ta miniaturka pokazuje aktualny stan (np. bieżącą pozycję filmu na YT) - https://en.m.wikipedia.org/wiki/Desktop_Window_Manager

Także nie ufalbym obecnie działaniu w oparciu o WM_PAINT czy innym InvalidateRect, bo wyniki ich działania mogą być mylące. Dawno się nie bawiłem grzebaniem na poziomie WinAPI, ale z tego co kojarzę to mechanizmy z czasów Win95, wprawdzie działają, ale mogą dawać dziwne efekty - np. że okno jest cały czas na widoku/odsłonięte.

3

Jak chodzi o wychwycenie całkowitego przykrycia Twojego programu czymś innym, to AFAIK nie ma takiej możliwości.

Jest jest, tylko trzeba się trochę napocić. Nie wiem tylko jak to zaafektuje performance, może wcale, ale jeśli coś sam autor sam rysuje to może mieć wpływ. Anyway, są dwa sposoby do wypróbowania.

  1. GetForegroundWindow zwróci Ci handle'a to okna na wierzchu. Sprawdzasz czy ten handle to nie jest shell, desktop czy Twoje okno hWnd =! GetDesktopWindow() && hWnd != GetShellWindow() && hWnd != GetActiveWindow() wtedy wiesz, że jakieś inne okno jest na wierzchu. Wtedy za pomocą GetWindowRect sprawdzasz czy zajmuje całą przestrzeń. Maksylaną przestrzeń pobierasz GetWindowRect(GetDesktopWindow(), &rc);

  2. Jest jeszcze SHQueryUserNotificationState, które zaraportuje Ci QUNS_PRESENTATION_MODE co oznacza, że jakaś aplikacja działa w fullscreenie. Ale nie wiem czy dotyczy to również zmaksymalizowanej aplikacji.

Wydaje mi się, że pierwszy sposó w połączeniu z WM_KILLFOCUS ma szansę zadziałać względnie dobrze, bo jak masz handle to okna na wierzchu to powinieneś móc również sprawdzić czy przykrywa ono Twoje okno nawet jeśli nie jest zmaksymalizowane, używasz do tego GetWindowPlacement który oprócz rozmiaru zwróci Ci koordynaty okna.

1 użytkowników online, w tym zalogowanych: 0, gości: 1