At first my code set up the SDL environment, and proceeded to update the OpenGL context, without performing any SDL_Event processing whatsoever. This causes the window, as long as it was open, to appear to Windows to be unresponsive. The window flickers a bit. The titlebar would get "(Not Responding)" appended to it, and upon clicking inside the window it becomes grayed out, as Windows does this by default on non responsive windows. However in this state (even as and after it becomes grayed out), the OpenGL display continues to update and animate, and here's the kicker, it even does so while the window is being dragged. Clearly in this case the application isn't handling events from windows correctly, causing windows to think that it is in a hanged state. But there is clear evidence that the opengl continues to render.
Now I make one single modification to the code, which is these three lines placed in an appropriate spot inside the loop (which also does the OpenGL draw):
SDL_Event event;
if (SDL_PollEvent(&event) && event.type == SDL_QUIT)
break;
All this is doing is flushing the message queue using SDL.
Now the behavior is that Windows no longer thinks it is "Not Responding" and it does not get grayed out. No flicker. Everything seems to run swimmingly. But once I click and drag the title bar to drag the window, rendering gets blocked. I haven't debugged it to be sure, but I suspect that SDL_PollEvent blocks for the duration of the window drag.
Is there a way around this? This is interesting because part of the behavior exhibited by failing to handle events is proof that what I want is possible in theory.
Update: I found this thread: http://www.gamedev.net/topic/488074-win32-message-pump-and-opengl---rendering-pauses-while-draggingresizing/
The verdict seems to be that it comes down to certain choices that Microsoft made for us... It basically gets stuck in DefWindowProc() till the mouse is released. It would get very messy to hack a fix for this and I might be able to do a work around by rendering in another thread. But I don't even want to begin to think about juggling an OpenGL context from multiple threads, if that's even something that's possible.
Some workaround that works for me - add event filter for SDL_WINDOWEVENT_SIZE_CHANGED event and do additional SetViewport and draw frame.
int SDLApp::eventFilter(void* pthis, const SDL_Event *event)
{
if (event->type == SDL_WINDOWEVENT &&
event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
{
SDLApp* app = (SDLApp*)pthis;
// Note: NULL rectangle is the entire window
SDL_RenderSetViewport(app->renderer_, NULL);
app->DrawFrame();
}
return 1;
}
...
SDL_SetEventFilter((SDL_EventFilter)SDLApp::eventFilter, this);
This question is old, but the solution I'm using doesn't seem to be mentioned anywhere else, so here it is.
I got my inspiration from this answer, and it doesn't use additional threads.
#include <SDL.h>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <SDL_syswm.h>
#define SIZE_MOVE_TIMER_ID 1
bool sizeMoveTimerRunning = false;
int eventWatch(void*, SDL_Event* event) {
if (event->type == SDL_SYSWMEVENT) {
const auto& winMessage = event->syswm.msg->msg.win;
if (winMessage.msg == WM_ENTERSIZEMOVE) {
// the user started dragging, so create the timer (with the minimum timeout)
// if you have vsync enabled, then this shouldn't render unnecessarily
sizeMoveTimerRunning = SetTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID, USER_TIMER_MINIMUM, nullptr);
}
else if (winMessage.msg == WM_TIMER) {
if (winMessage.wParam == SIZE_MOVE_TIMER_ID) {
// call your render function
render();
}
}
}
return 0;
}
// rendering function
void render() {
/* do your rendering here */
}
// event loop - call this function after setting up your window to start the event loop
void eventLoop() {
SDL_AddEventWatch(eventWatch, nullptr); // register the event watch function
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); // we need the native Windows events, so we can listen to WM_ENTERSIZEMOVE and WM_TIMER
while (true) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (sizeMoveTimerRunning) {
// modal drag/size loop ended, so kill the timer
KillTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID);
sizeMoveTimerRunning = false;
}
/* handle the events here */
}
render();
}
}
Of course, if your rendering function needs to keep additional state (e.g. if you're using OOP), use the void* parameter of eventWatch(void*, SDL_Event*) to pass the state.
I had a similar problem in which it would freeze video playback when the window was dragged or resized. The solution I found was to spawn a separate thread for rendering and use the main thread for input.
Example:
DWORD RenderThread(SDL_Window* window)
{
//Rendering stuff here...
}
int main()
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window* window = SDL_CreateWindow("Title Here",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, h, w, SDL_WINDOW_RESIZABLE);
HANDLE hRenderThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RenderThread, window, 0, NULL);
SDL_Event event;
while (1)
{
SDL_PollEvent(&event);
switch (event.type)
{
//Event handling here...
}
}
}
Keep in mind that you MUST create the window in the thread that does event handling. If not it won't work. You can create the window in your event handling thread then pass that window pointer to your rendering thread.
I propose you created 2 threads:
Thread 1: loops calling SDL_PollEvent() (without rendering anything)
Thread 2: does OpenGL rendering (without calling SDL_PollEvent())
This way, your OpenGL context would be manipulated from a single thread. The whole solution has a minimum impact the architecture of your application.
Many windows procedures run a separate message loop until a certain event occurs, so you shouldn't rely on your main loop to do the drawing. If possible, application logic and rendering should always be handled in a separate thread.
Your main thread (that only handles message processing) doesn't need GL context at all, so you wouldn't need to worry about sharing.
Related
This is my code:
#include <iostream>
#include <SDL2/SDL.h>
int main(int argc, const char * argv[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *_window;
_window = SDL_CreateWindow("Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 500, SDL_WINDOW_RESIZABLE);
SDL_Delay(20000);
SDL_DestroyWindow(_window);
SDL_Quit();
return 0;
}
Im working in Xcode. I've downloaded SDL2 and imported the library to the projects build phases. I've tested that the SDL2 works correctly.
The problem is that window never shows up. I just get a "spinning-mac-wheel" and then the program quits after the delay. I've made sure that the window is not hidden behind somewhere.
Ideas?
You have to give the system a chance to have it's event loop run.
easiest is to poll for events yourself:
SDL_Event e;
bool quit = false;
while (!quit){
while (SDL_PollEvent(&e)){
if (e.type == SDL_QUIT){
quit = true;
}
if (e.type == SDL_KEYDOWN){
quit = true;
}
if (e.type == SDL_MOUSEBUTTONDOWN){
quit = true;
}
}
}
instead of the wait loop
--- Addendum
Since this answer is still helping people maybe it's nice if I also add a bit more info on why this works instead of just posting the solution.
When on the Mac (same for Windows actually) a program starts, it starts with just the 'main thread'. This is the thread which is used to set up UI stuff. The 'main thead' differs from other threads in that it comes with an event handling system. This system catches events like mouse moves, key presses, button clicks and then queues these and lets your code respond to it. All the UI things on Mac (and Windows) rely on this event pump being there and running. This is the reason why if you do anything UI related in your code you need to make sure you are not on a different thread.
Now, in your code you initialise the window and the UI, but then you do a SDL_Delay. This just blocks the thread and halts it for 20 seconds so nothing is done. And since you do that on the main thread, even the handling of the queue with the events is blocked. So on the Mac that shows as the spinning macwheel.
So the solution I posted actually keeps on polling for events and handles them. This way you are effectively also 'idling', but the moment events are posted (like mouse clicks and keys) the thread will wake up again and stuff will be processed.
You have to load a bitmap image, or display something on the window, for Xcode to start displaying the window.
#include <SDL2/SDL.h>
#include <iostream>
using namespace std;
int main() {
SDL_Window * window = nullptr;
SDL_Surface * window_surface = nullptr;
SDL_Surface * image_surface = nullptr;
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
window_surface = SDL_GetWindowSurface(window);
image_surface = SDL_LoadBMP("image.bmp");
SDL_BlitSurface(image_surface, NULL, window_surface, NULL);
SDL_UpdateWindowSurface(window);
SDL_Delay(5000);
SDL_DestroyWindow(window);
SDL_FreeSurface(image_surface);
SDL_Quit();
}
You need to initialize SDL with SDL_Init(SDL_INIT_VIDEO) before creating the window.
Please remove the sdl_delay() and replace it with the below mentioned code. I don't have any reason for it but I tried on my own and it works
bool isquit = false;
SDL_Event event;
while (!isquit) {
if (SDL_PollEvent( & event)) {
if (event.type == SDL_QUIT) {
isquit = true;
}
}
}
I have this very strange issue. I'm trying to get a window hierarchy to be replicated. So on creating the 1st level dialog, I'm start the instance of the 2nd level dialog.
I've done this in many different ways, but it always shows up as the 2nd level being below the 1st level and then usually a zorder inversion happens (they flip positions). Occasionally, the inversion doesn't happen, but if I click on the owner, the owned immediately jumps to the top of the zorder.
Here are the main parts of a small example to show this happening:
const unsigned short WMA_DIALOGACTION = WM_APP+1;
// Button event handler for the 0th level
void CdialogcallingdialogsDlg::OnBnClickedDlgLvl1()
{
CDlgLvl1 x(this);
x.DoModal();
}
BEGIN_MESSAGE_MAP(CDlgLvl1, CDialogEx)
ON_WM_WINDOWPOSCHANGED()
ON_MESSAGE(WMA_DIALOGACTION, OnDialogAction)
END_MESSAGE_MAP()
void CDlgLvl1::OnWindowPosChanged(WINDOWPOS* lpwndpos)
{
if (!m_shownDlg) {
m_shownDlg = true;
PostMessage(WMA_DIALOGACTION);
}
}
// Level 1 dialog opening up level 2 dialog
LRESULT CDlgLvl1::OnDialogAction(WPARAM wParam, LPARAM lParam)
{
ShowWindow(SW_SHOW);
CDlgLvl2 x(this);
x.DoModal();
return LRESULT();
}
BEGIN_MESSAGE_MAP(CDlgLvl2, CDialogEx)
ON_WM_WINDOWPOSCHANGING()
END_MESSAGE_MAP()
// Level 2 dialog offseting its position
void CDlgLvl2::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
ASSERT(lpwndpos->hwnd == m_hWnd);
// Offset dialog to see the problem of dlg2 showing up below dlg1
if (!(lpwndpos->flags & SWP_NOMOVE)) {
lpwndpos->x += 10;
lpwndpos->y += 10;
}
}
In the example, you click on the button in the main dialog. That then starts up CDlgLvl1 which then starts up CDlgLvl2. The dialogs are the default dialogs except for the message handling that is shown here and a button on the main application dialog. If you look at it carefully, you can see the inversion.
What am I doing wrong? Perhaps there is a better way to do this?
In case it makes a difference, the issue is more pronounced under Windows 10 and doesn't seem to be visible on Windows 8.1.
A copy of the solution can be pulled from my git repo here:
https://github.com/Ma-XX-oN/dialog-calling-dialogs.git
I've just added some bitmaps on the dialogs to really show the issue, but I've not tested on my 8.1 box yet.
I did a recording of how it pops up and here is frame 0, 2, and 3 of that recording:
Frame 0
Frame 2
Frame 3
As you can see, LVL1 appears over LVL2 in Frame 2, and then flips position in Frame 3.
Full video can be found here.
Using this example project, I've not been able to replicate LVL1 staying overtop of LVL2, but I believe that the behaviour of the zorder inversion not happening is some sort of race condition.
The problem is caused when windows "transition animation" is enabled. WM_WINDOWPOSCHANGED is being sent before the animation is finished.
To fix this problem, you can simply disable the transition for the dialog:
BOOL CDlgLvl2::OnInitDialog()
{
BOOL res = CDialogEx::OnInitDialog();
BOOL attrib = TRUE;
DwmSetWindowAttribute(m_hWnd, DWMWA_TRANSITIONS_FORCEDISABLED, &attrib, sizeof(attrib));
return res;
}
If you don't want to disable the transition, you have to wait until this transition is finished. I don't know how to detect it or how to determine the transition time. It seems to be 250 milliseconds. SystemParametersInfo(SPI_SETMENUSHOWDELAY...) gives a value of 400 milliseconds which seems a bit too long.
Assuming we know the time, use SetTimer to run the function after transition is over:
BOOL CDlgLvl2::OnInitDialog()
{
BOOL res = CDialogEx::OnInitDialog();
ANIMATIONINFO info = { sizeof info };
SystemParametersInfo(SPI_GETANIMATION, sizeof(ANIMATIONINFO), &info, 0);
if (info.iMinAnimate)
SetTimer(1, 250, nullptr);
else
SetTimer(1, 1, nullptr);
return res;
}
void CDlgLvl2::OnTimer(UINT_PTR nIDEvent)
{
CDialogEx::OnTimer(nIDEvent);
if(nIDEvent == 1)
{
KillTimer(nIDEvent);
CDlgLvl2(this).DoModal();//note, PostMessage is not needed in SetTimer
}
}
Maybe the problem is caused because the 1st level dialog creates the 2nd one before it has a chance to display itself. And yes, this can vary from system to system. There's no really a fix, but I would suggest a workaround, employing a timer. Below is some code.
Header file for CDlgLvl1:
class CDlgLvl1 : public CDialogEx
{
.
.
.
protected:
UINT_PTR nIDTimer = 0; // Add this
};
Source file for CDlgLvl1:
BEGIN_MESSAGE_MAP(CDlgLvl1, CDialogEx)
.
.
ON_MESSAGE(WMA_DIALOGACTION, OnDialogAction)
ON_WM_TIMER()
END_MESSAGE_MAP()
BOOL CDlgLvl1::OnInitDialog()
{
CDialogEx::OnInitDialog();
nIDTimer = SetTimer(1, 250, NULL);
return TRUE;
}
void CDlgLvl1::OnTimer(UINT_PTR nIDEvent)
{
if (nIDTimer && nIDEvent == nIDTimer)
{
KillTimer(nIDTimer);
nIDTimer = 0;
PostMessage(WMA_DIALOGACTION);
return;
}
CDialogEx::OnTimer(nIDEvent);
}
LRESULT CDlgLvl1::OnDialogAction(WPARAM wParam, LPARAM lParam)
{
CDlgLvl2 x(this);
x.DoModal();
return 0;
}
The mechanism you provided to prevent the 2nd window being displayed multiple times (the m_shownDlg variable) has been replaced by the nIDTimer check.
Please experiment with the timer's elapse value. The one I suggest (250 - 1/4 sec) is OK for most systems and imperceptible to to the user.
I wrote this in the SO editor, no actual test in VS (so it may contain some few syntax errors - pls fix them if so).
Note: You do not need to override OnWindowPosChanging() if you only want to set the position of the 2nd dialog. It's relative to its parent, so you can simply set the X Pos and Y Pos properties of the dialog's resource.
I tried your project in Visual Studio 2019:
I ran it in DEBUG mode and it works fine. The third dialogue showed up as a child of the second dialog (that is, with the correct ZORDER). The same is true for RELEASE build.
See: https://www.dropbox.com/s/8f5z5ltq3vfc10r/Test.mp4?dl=0
Update
If one of my classes I had a timer and I did this:
void CChristianLifeMinistryEditorDlg::OnTimer(UINT_PTR nIDEvent)
{
READYSTATE eState = READYSTATE_UNINITIALIZED;
if (nIDEvent == PRINT_PREVIEW_TIMER)
{
eState = m_pPrintHtmlPreview->GetReadyState();
if (eState == READYSTATE_COMPLETE)
{
KillTimer(m_uPreviewTimer);
PostMessage(WM_COMMAND,
MAKELONG(IDC_BUTTON_PRINT_PREVIEW2, BN_CLICKED));
}
}
CResizingDialog::OnTimer(nIDEvent);
}
You could adapt the principle and then just simulate pressing the button to display the second next dialog. Might work.
The problem occurs when
SDL_RestoreWindow(gameWindow);
is called on a minimised window. The window does not re-appear.
I've made a little dummy program simulating the sort of SDL calls that my main program is calling, and the problem reproduces. Here is the code:
#include <SDL.h>
int main(int argn, char **argv)
{
// The windows
SDL_Window *gameWindow;
// Initialise
SDL_Init(SDL_INIT_EVERYTHING);
// Create window
gameWindow = SDL_CreateWindow(
"Game Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640,
480,
SDL_WINDOW_OPENGL
);
// Minimise/ hide or whatever?
SDL_MinimizeWindow(gameWindow);
// Wait
SDL_Delay(1000);
// Attempt to bring back the window
SDL_RestoreWindow(gameWindow);
// program would run here
SDL_Delay(1000);
// quit out
SDL_DestroyWindow(gameWindow);
// Clean up
SDL_Quit();
return 0;
}
Any ideas as to why this gameWindow is not re-appearing?
Is SDL_Restore not designed to restore a minimised window?
Also FYI This is running on Ubuntu 13.10 and compiling with gcc 4.8.1 and Gnome metacity 2.34.13
Short answer
Make sure you call one of the functions that let SDL process events: SDL_PollEvent() or SDL_WaitEvent() or SDL_PumpEvents().
Longer answer
It is possible that the reason why the window can't be restored is because it does not have SDL_WINDOW_MINIMIZED flag assigned to it.
Here is the source code of the SDL_RestoreWindow() function:
void
SDL_RestoreWindow(SDL_Window * window)
{
CHECK_WINDOW_MAGIC(window,);
if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
return;
}
if (_this->RestoreWindow) {
_this->RestoreWindow(_this, window);
}
}
If neither SDL_WINDOW_MAXIMIZED nor SDL_WINDOW_MINIMIZED are set, the _this->RestoreWindow() will not be called (and the window will not be restored).
Normally when a window is being minimized the SDL_WINDOW_MINIMIZED flag is set by SDL in SDL_SendWindowEvent() function:
case SDL_WINDOWEVENT_MINIMIZED:
if (window->flags & SDL_WINDOW_MINIMIZED) {
return 0;
}
window->flags &= ~SDL_WINDOW_MAXIMIZED;
window->flags |= SDL_WINDOW_MINIMIZED;
SDL_OnWindowMinimized(window);
break;
This will happen when SDL processes events queue. Processing of the events should be triggered by your application as described in the documentation:
Internally, SDL stores all the events waiting to be handled in an
event queue. Using functions like SDL_PollEvent(), SDL_PeepEvents()
and SDL_WaitEvent() you can observe and handle waiting input events.
If you do not want to receive and handle input events, you can call SDL_PumpEvents() function to let SDL process the events:
This function updates the event queue and internal input device state.
Often the need for calls to SDL_PumpEvents() is hidden from the user since SDL_PollEvent() and SDL_WaitEvent() implicitly call SDL_PumpEvents(). However, if you are not polling or waiting for events (e.g. you are filtering them), then you must call SDL_PumpEvents() to force an event queue update.
I am having some issues with GLFW's window creation. I am wanting to have a program capable of toggling between windowed and fullscreen mode. To do this in GLFW 2.7.8 one must first destroy the active window, then create a new one. I read that version 3.0 has support for multiple windows, but it is still in development.
I have provided my own function to handle keyboard input. Using the initial 400 by 400 window, the program functions as expected; it will enter fullscreen on f or F, will exit when the escape key is pressed, and will complain when anything else is pressed.
However, when fullscreen mode is entered, the window becomes unresponsive with regards to my provided keyboard function. It will continue to run through the loop in main() and will respond to something like the glfwGetKey(GLFW_KEY_ESC) test. Regardless of if I have the mouse cursor enabled or not, the cursor does not appear.
Again, the fullscreen window is created and the KeyboardCallback function returns back into the main loop. I wish to understand why the fullscreen window is not working with my keyboard function, and why it is not displaying properly.
I am not drawing anything to the window, as I am trying to get some experience with various window abstraction libraries specifically. Drawing a simple triangle did nothing to solve he problem, and the fullscreen window remains black.
My code is as follows:
#include <stdio.h>
#include <stdlib.h>
// glfw32 - 2.7.8
#include <GL\glfw.h>
#pragma comment (lib, "GLFW.lib")
#pragma comment (lib, "opengl32.lib")
using namespace std;
// constants and globals
const int WINDOW_WIDTH=400, WINDOW_HEIGHT=400;
static bool fullscreen=false;
// function declarations
void GLFWCALL KeyboardCallback(int key, int action);
void FatalError(const char* msg) {
fprintf(stderr, "ERROR: Failure in \"%s\"\n", msg);
exit(1);
}
int main() {
// ititialize GLFW
glfwInit();
glfwEnable(GLFW_MOUSE_CURSOR);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
// initial window, 400x400 with 32-bit depth buffer in windowed mode
glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 0,0,0,0, 32, 0, GLFW_WINDOW);
glfwSetWindowTitle("Simple Title");
glfwSetWindowPos(0, 0);
// set custom keyboard callback
glfwSetKeyCallback(KeyboardCallback);
while (true) { // loop until exit
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers();
// debug
//printf("Looping...\n");
if ( glfwGetKey(GLFW_KEY_ESC) ) {break;}
}
glfwTerminate();
printf("\nHave a nice day\n");
return 0;
}
void GLFWCALL KeyboardCallback(int key, int action) {
//printf("In keyboard function\n");
if (action) { // if key DOWN,
switch(key) { // do something...
case 'f':
case 'F': {
fullscreen = !fullscreen;
printf("Toggle Fullscreen: %s\n", (fullscreen ? "On" : "Off"));
glfwCloseWindow();
if (! glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 0,0,0,0, 32, 0,
fullscreen ? GLFW_FULLSCREEN : GLFW_WINDOW)) {
FatalError("toggle fullscreen");
}
glfwSetWindowTitle("New Title");
break;
}
case GLFW_KEY_ESC: {
printf("\nGoodbye cruel world...\n");
glfwTerminate();
exit(0);
}
default: {
printf("Key not implemented: %c\n", key);
break;
}
}
}
printf("Exiting keyboard function\n");
}
I tried James' approach from here but to no effect. Are there GLFW flags I am forgetting to set?
EDIT------- 5/20
I had a thought. Perhaps my callback was being unregistered when the window is destroyed. Turns out that re-registering my function when the new window is created made the window responsive.
While this solves my original problem, I now face a new one. I cannot render to the new window properly. I can insert glClear( GL_COLOR_BUFFER_BIT ); into my main loop, which will successfully set a background colour for the new window. For some reason it does not interact with my arrays and buffers to draw the image.
Code with attempted drawing
EDIT------- 5/21
I converted my code to GLFW 3.0.0. The window management is much cleaner (albeit more complex) and I do not have the rendering issue. Probably because I explicitly have to state the current context to use.
I would still like to solve the rendering issue, both out of curiosity and for if/when I return to 2.7.
Right now the event queue of GLFW is not pumped. You must either set the GLFW_AUTO_POLL_EVENTS option so that glfwSwapBuffers pumps the event queue, or you call glfwPollEvents at the start of a main event loop iteration.
I'm creating a Qt/C++ application using QML for some parts. Under windows I would like to make use of the translucent windowing using ExtendFrameIntoClientArea as seen in this snippet from my window class.
#ifdef Q_WS_WIN
if ( QSysInfo::windowsVersion() == QSysInfo::WV_VISTA ||
QSysInfo::windowsVersion() == QSysInfo::WV_WINDOWS7 )
{
EnableBlurBehindWidget(this, true);
ExtendFrameIntoClientArea(this);
}
#else
The code is working nicely with one exception. If the transparent window system is turned off the background becomes black, and as part of my UI is transparent it becomes dark as well. The same thing happens when logging in to a remote computer running the application, even if the transparent windowing system is re-initialized immediately the background stays black until the above code is executed again. This is demonstrated in this image: Comparison of failed rendering (in background) and correct (in front).
The problem is to find a signal to connect to re-initializing the transparent window, or better yet to detect when windows are drawn transparently and draw the UI accordingly. Any alternative solutions are also welcome.
After digging around in both Qt and MSDN Aero documentation I came up with a two step solution. By overriding the winEvent method of my main window, I was able to receive the signal which is triggered every time the translucent window system is enabled or disabled.
#define WM_DWMCOMPOSITIONCHANGED 0x031E
bool MainWindow::winEvent(MSG *message, long *result) {
if ( message->message == WM_DWMCOMPOSITIONCHANGED ) {
// window manager signaled change in composition
return true;
}
return false;
}
That took me quite close, but it didn't tell me whether DWM was currently drawing transparent windows or not. By using dwmapi.dll I was able to find a method which does exactly that, and it can be accessed like below:
// QtDwmApi.cpp
extern "C"
{
typedef HRESULT (WINAPI *t_DwmIsCompositionEnabled)(BOOL *pfEnabled);
}
bool DwmIsCompositionEnabled() {
HMODULE shell;
shell = LoadLibrary(L"dwmapi.dll");
if (shell) {
BOOL enabled;
t_DwmIsCompositionEnabled is_composition_enabled = \
reinterpret_cast<t_DwmIsCompositionEnabled>(
GetProcAddress (shell, "DwmIsCompositionEnabled")
);
is_composition_enabled(&enabled);
FreeLibrary (shell);
if ( enabled ) {
return true;
} else {
return false;
}
}
return false;
}
My implementation is now able to react to changes in Aero and draw the GUI accordingly. When logging in over remote desktop the window is drawn using transparency as well where available.
The function should be written as follows to avoid the GPA failure
// QtDwmApi.cpp
extern "C"
{
typedef HRESULT (WINAPI *t_DwmIsCompositionEnabled)(BOOL *pfEnabled);
}
bool DwmIsCompositionEnabled() {
HMODULE shell;
BOOL enabled=false;
shell = LoadLibrary(L"dwmapi.dll");
if (shell) {
t_DwmIsCompositionEnabled is_composition_enabled = \
reinterpret_cast<t_DwmIsCompositionEnabled>(
GetProcAddress (shell, "DwmIsCompositionEnabled")
);
if (is_composition_enabled)
is_composition_enabled(&enabled);
FreeLibrary (shell);
}
return enabled;
}