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.
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;
}
}
}
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'm working on a GLFW application and I noticed a problem when resizing the window.
While the user is scaling the window, the glfwPollEvents() function waits until the user finishes. This means that, during the scaling of the window, the render function isn't called and horrible artifacts are created.
I've worked around this by calling the render function from the main loop as well as the window resize callback:
#include <GLFW/glfw3.h>
static void render(GLFWwindow* window) {
glfwMakeContextCurrent(window);
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
}
static void window_size_callback(GLFWwindow* window, int width, int height) {
render(window);
glViewport(0, 0, width, height);
}
int main(void) {
if (!glfwInit()) {
return -1;
}
GLFWwindow* window = glfwCreateWindow(640, 480, "Terrain chunk render", NULL, NULL);
if (!window) {
glfwTerminate();
return -1;
}
glfwSetWindowSizeCallback(window, window_size_callback);
while (!glfwWindowShouldClose(window)) {
render(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
However, this means that the render function isn't called when the user is holding down the mouse button after having scaled the window without actually moving it. Is there an efficient way of working around this and having the render function called more consistently?
Use two threads, one for polling events and one for rendering. You also might want to use glfwWaitEvents intstead of glfwPollEvents to block the event thread until events are available.
You should move the glfwSetWindowSizeCallback(window, window_size_callback); into your while loop so that the render function is called while you are re-sizing the window. I had the same problem a long time ago.
Edit: It's not recommended to set callbacks in the loop. The frames just need to be redrawn while the window is being resized.
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.
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.