Why does my window only get shown after I use SDL_PollEvent()? - c

I'm using SDL2 in C and wanted to show something to the screen without the need for taking in inputs. But when I ran the code to present a black screen the window would not open. I proceeded to do what I knew would make it pop up and added a SDL_Event variable and used SDL_PollEvent(). I would like to know why I had to do this, and if there would be a way for me to use a SDL_Window without polling any events.
(For example: something like an animation).
The code in question is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
int main()
{
if (SDL_Init(SDL_INIT_VIDEO))
{
fprintf(stderr, "Error while Initalizing SDL2: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Window *window = SDL_CreateWindow("Connect Four", 100, 100, 500, 500, SDL_WINDOW_SHOWN);
if (!window)
{
fprintf(stderr, "Error while Initalizing window: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer)
{
SDL_DestroyWindow(window);
fprintf(stderr, "Error while Initalizing renderer: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Event events; //without these two lines
SDL_PollEvent(&events); //the window will not open on screen
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}

It may be confusing because some SDL examples (including SDL doc wiki) uses "draw&delay" code without event processing, but that is not a reliable way to display things on screen. That examples are overly simplified and was written long time ago when it kind of worked, but only for simplest things, as 3 seconds is probably not long enough for window manager to kill unresponsive program, or for user to notice window image collapses if it is minimised or covered by other windows.
Short answer to your question is no, your program have to communicate with window manager (side note - it may not be necessary on some operating/graphics systems); you have to get events and react to special window events (e.g. your window becoming visible or user requesting resize). In SDL, this is done with SDL_PumpEvents call, which internally does this communication and generates event queue that you can inspect later, so you need to call that frequently, either directly or indirectly via SDL_PollEvent or SDL_WaitEvent. If you don't, window manager probably will not be nice to you - depending on window manager, you may get "application not responding, let's kill it" dialog, greyed out window or, as in your case, no window at all (that particular case is because you've presented your rendering result before "your window is now visible" event was received, so your image is discarded; that's why processing events before rendering changed how things go).
But even so, processing events once is not correct either. What you have is still optimistic "let's hope nothing breaking will happen in 3 seconds". If you do animation, process events on every frame; but if you have static display code - it may be good idea to treat it like it is animated too. Basically you need rendering loop, with event processing at start, and redraw code to re-generate your image when you have to. This "have to" may be either unconditional (full redraw on every iteration), or, if you really don't want to (e.g. for processing cost reasons - most GUI programs don't do full redraw when idle) you still have to redraw when window manager says your previous image is no longer valid - SDL notifies you of that via SDL_WindowEvent of SDL_WINDOWEVENT_EXPOSED type. In that case you may want to use blocking SDL_WaitEvent to avoid unnecessary iterations.

Related

WM_MOUSEWHEEL doesn't seem to be _always_ generated, what gives?

First off, what I wanted to accomplish: intercepting (via a low level mouse hook) WM_MOUSEHWHEEL messages to go back and forward in Windows' file explorer history, or at least to the parent window if access to the history weren't possible.
That was what started it all, not that I had any idea if it'd be possible or not, specially with a touchpad, but that was my intention. So I started looking at the more usual WM_MOUSEWHEEL (vertical scrolling), only to find out that those messages aren't being generated much of the time!
As per the MSDN docs on WM_MOUSEWHEEL:
Sent to the focus window when the mouse wheel is rotated. The DefWindowProc function propagates the message to the window's parent. There should be no internal forwarding of the message, since DefWindowProc propagates it up the parent chain until it finds a window that processes it.
Okay, looks good, so when the mouse wheel is rotated that message is sent to the window that has the focus. Let's see if that's the case with the help of Spy++ from the Visual Studio pack of utilities, made a recording of it and uploaded it to YouTube, embedding doesn't seem to be allowed in this Stack Exchange: https://youtu.be/YJvjw_BPJf0
Summing it up though, there's no WM_MOUSEWHEEL being generated for all of the programs tested. Explorer was the first one I tried to look at, focusing on the different sub-windows a window had and made sure it was scrollable.
I could see some related messages being received by the scrollbar section of it, but that's it, no sign of that message anywhere (however, VM_VSCROLL was posted to one of the windows of the scrollbar).
Next up was Notepad3, nada, its SysListView32 doesn't see anything and the parent window gets some notification, activation messages and the like but nothing scrolling related.
Everything's SysListView32 received some LVS_SCROLL messages but it didn't act on them (its message loop is probably looking for the darn WM_MOUSEWHEELs instead).
Finally a browser, nothing at all, the "view" didn't register anything at all while the parent window captured the mouse by the look of the events.
NOTE: For those of you who see the video, note that while Spy++ was on top it didn't have focus during all tests, it was merely pinned so I could see the messages.
I almost gave up, starting from the default example Win32 application they give you in Visual Studio I added a hook procedure and if that message was received it'd print a message box, just in case it was the shell the one getting the messages somehow.
There was my surprise when I ran the code directly in VS that I see message boxes when I scroll through the code. WTF?!
Fire Spy++ again (also 64-bit because it was the 2022 Preview I was running) and let's see, there they are!
https://youtu.be/ekrWKP7HiTE
The code editor sub-window is receiving those messages whenever I scroll vertically with the trackpad using two fingers. So, what is going on?
I mean, I even tried to run that sample code elevated to be at a higher integrity level and see if that'd make those messages appear, but to no avail.
The code is as simple as it gets, most of it was autogenerated as a sample Win32 application, but the hook and procedure are as follows:
// The callback procedure:
LRESULT CALLBACK mouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == 0) {
if (wParam == WM_MOUSEWHEEL)
MessageBox(NULL, L"We got it!", L"Yay!", MB_OK);
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
...
// Setting the mouse hook (at the application's entry point):
HHOOK mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, hInstance, 0);
if (!mouseHook) {
LPWSTR errorMessage = NULL;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, (LPWSTR)&errorMessage, 0, NULL) == 0) {
MessageBox(NULL, L"Couldn't format the error message...", L"Error", MB_OK | MB_ICONERROR);
}
MessageBox(NULL, errorMessage, L"Damn", MB_OK | MB_ICONERROR);
LocalFree(errorMessage);
return FALSE;
}
Although I tagged it as C, it really applies to any language that runs in Windows and can access low level mouse events, be it managed or not.
PS. By the way, although the intention I mentioned at the beginning was the motivation that started it all, I reckon it's impossible to do. If vertical scroll messages aren't always generated even in scrollable controls, I don't see horizontal ones being generated when there's nothing to scroll. It'd need to be implemented in the File Explorer itself most likely.

Determine if window is visible with High CPU

Background
I am trying to screenshot a specific window, however the OS where I need to accomplish this has very high CPU usage and sometimes my application determines this window as visible however in the screenshot it is either not shown or half drawn.
My Understanding
Since the CPU usage is very high it will not be able to provide enough resources to the process which opens this specific window. Therefore the check for window visibility succeeds it may just mean that the window was initiated for creation but not drawn yet (given the lack of resources).
My Code
HWND hWnd = FindWindowExA(0, 0, NULL, programdata->caption);
if (hWnd)
{
if (IsWindowVisible(hWnd))
{
RECT rect = { 0 };
GetWindowRect(hWnd, &rect);
// if window size also matches requirement then
TakeScreenshot();
Question
Is there any better way that I can determine if the window was fully drawn and visible to the human eye?
if the window was fully drawn and visible to the human eye?
To take screenshot you don't need the window to be visible. You only have to copy from its context. So just do GetWindowDC() and use it.
If you really need to check if it's "visible to human eye", you must also ensure that the window is at the top of Z-order. Otherwise, it could be overlayed by some other window.
If you want to check if window was fully repainted, you can test its GetUpdateRect() value.

How to send GtkWidgets to the back or front?

I wonder if there is a way to "Send to back" or "Bring to front" a GtkWidget dynamically. ( I know, it can be done by changing the creating order of GtkWidget )
example : (GTK+ 2.0, Cent OS 7.0, C)
#include <gtk/gtk.h>
int main( int argc, char *argv[] )
{
GtkWidget *window, *button1, *button2, *fixed;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
button1 = gtk_button_new_with_label("A button");
button2 = gtk_button_new_with_label("B button");
fixed = gtk_fixed_new();
gtk_fixed_put( (GtkFixed*)fixed,button2, 30, 30 );
gtk_fixed_put( (GtkFixed*)fixed,button1, 50, 50 );
gtk_container_add(GTK_CONTAINER(window), fixed);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
this program will display : (the first picture)
Are there some commands that can make the "B Button" be in front of "A Button". ( look likes this following picture --> the second picture )
No, there aren't "functions" like those in a GtkFixed.
From the Gtk 2 documentation:
Description
The GtkFixed widget is a container which can place child widgets at
fixed positions and with fixed sizes, given in pixels. GtkFixed
performs no automatic layout management.
For most applications, you should not use this container! It keeps you
from having to learn about the other GTK+ containers, but it results
in broken applications. With GtkFixed, the following things will
result in truncated text, overlapping widgets, and other display bugs:
Themes, which may change widget sizes.
Fonts other than the one you used to write the app will of course change the size of widgets containing text; keep in mind that users
may use a larger font because of difficulty reading the default, or
they may be using Windows or the framebuffer port of GTK+, where
different fonts are available.
Translation of text into other languages changes its size. Also, display of non-English text will use a different font in many cases.
In addition, the fixed widget can't properly be mirrored in
right-to-left languages such as Hebrew and Arabic. i.e. normally GTK+
will flip the interface to put labels to the right of the thing they
label, but it can't do that with GtkFixed. So your application will
not be usable in right-to-left languages.
Finally, fixed positioning makes it kind of annoying to add/remove GUI
elements, since you have to reposition all the other elements. This is
a long-term maintenance problem for your application.
If you know none of these things are an issue for your application,
and prefer the simplicity of GtkFixed, by all means use the widget.
But you should be aware of the tradeoffs.
The order by which they are added is the order they will keep in the virtual Z axis. You can control this by removing and readding them or other similar approaches.

Win32, and the window style

So I have a window, and I have coded it so that during run-time it can enter and exit full-screen mode. Entering full-screen works, but exiting places the window tile bar in reverse order.
Exit full screen code:
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 640, 480, NULL);
InvalidateRect(hWnd, NULL, TRUE);
Picture of the result: https://www.dropbox.com/s/p15eltz7b2hxx4y/window.png?dl=0
I tried using GWL_STYLE instead of GWL_EXSTYLE but that works even worse, with the window being visible but clicking anything on the window will act like the window is not there and the click on whatever is behind it...
Thanks!
Philip
Just a thought, couldn't you get the window style (with GetWindowLongPtr), store it as a member variable in you class and then use this as the style to reset in SetWindowLongPtr?
Following is uncheck code (this is assuming you are using C++),
MainWnd::OnFullScreen(...)
{
m_oldStyle = GetWindowLongPtr(GWL_EXSTYLE, m_hwd);
/*
what ever other code is necessary
*/
}
MainWnd::OnExitFullScreen(...)
{
SetWindowLongPtr(m_hwn, GWL_EXSTYLE, m_oldStyle);
/*
and other code as needed
*/
}
I've made two assumptions here:
(1) that you will have two variables, one to contain the old style (m_oldStyle) and one to hold the handle to the window (m_hwd). Note if you are doing strict SDK style coding then the handle will be passed to you as part of WndProc. If you are using MFC there should be member function in the class you derived you main window from. In other cases you are on your own.
(2) the second assumption is that SetWindowLongPtr is called prior to any change of screen type. I believe that SetWindowLongPtr is called during window construction, but it has been several years since I've done serious windows programming using Microsoft frameworks (now I tend to used QT's framework).

capture mouse with Xlib

I want to write a simple Xlib program changing the mouse behavior (to give an example, invert vertical movement). I have a problem with capturing the events.
I would like the code to
capture changes in the controllers position (I move mouse upward, MotionEvent)
calculate new cursor position (new_x -= difference_x)
set new cursor position ( move pointer down, XWarpPointer, prevent event generation here)
The code below should capture a motion event every time the mouse is moved, but it generates the event only when the pointer moves from one window to another... How to capture all the movement events?
#include "X11/Xlib.h"
#include "stdio.h"
int main(int argc, char *argv[])
{
Display *display;
Window root_window;
XEvent event;
display = XOpenDisplay(0);
root_window = XRootWindow(display, 0);
XSelectInput(display, root_window, PointerMotionMask );
while(1) {
XNextEvent( display, &event );
switch( event.type ) {
case MotionNotify:
printf("x %d y %d\n", event.xmotion.x, event.xmotion.y );
break;
}
}
return 0;
}
Related:
X11: How do I REALLY grab the mouse pointer?
When your program receives mouse events, it receives a copy of the events; copies are also sent to other programs that are listening for those events (see XSelectInput(3)). You cannot override this without using XGrabPointer(3) to take exclusive ownership of the mouse, which will prevent other programs from receiving any mouse events. In short, you can't actually do what you are trying to do.
Note also that if a client has specified PointerMotion in its do-not-propagate mask for one of its windows, you will not receive any pointer motion events within its window (again, unless you do a grab).
If you want to change the behavior of the mouse when it is being moved, I suggest you to play with the input properties instead of trying to do the processing in your program.
xinput --list
xinput --list-props 'USB Optical Mouse'
xinput --set-prop 'USB Optical Mouse' 'Evdev Axis Inversion' 1 0
xinput --set-prop 'USB Optical Mouse' 'Evdev Axes Swap' 1
There's also the 'Coordinate Transformation Matrix' property but for some reason it's not working for me right now.
You don't need to call the xinput program yourself: you can use Xlib calls (look at xinput's source code).

Resources