SDL desktop resolution detection in Linux [duplicate] - c++

This question already has an answer here:
How can I get the screen resolution using SDL2?
(1 answer)
Closed 6 years ago.
I have got some reports that for some Linux users, especially those who use SteamOS, my game opens in wrong resolution. The game tries to detect the current desktop resolution and create a borderless fullscreen window by using that resolution.
For example, the resolution of the SteamOS is usually 1920x1080, but the SDL reports it to be something like 4096x2160! Thus when the game starts, players see only lower-left portion of the game area.
My function for detecting screen resolution is the following:
bool View::checkDisplaySize() {
int display_count = 0;
int display_index = 0;
int mode_index = 0;
SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
if ((display_count = SDL_GetNumVideoDisplays()) < 1) {
printf("SDL_GetNumVideoDisplays returned: %i", display_count);
return false;
}
if (SDL_GetDisplayMode(display_index, mode_index, &mode) != 0) {
printf("SDL_GetDisplayMode failed: %s", SDL_GetError());
return false;
}
m_display.w = mode.w;
m_display.h = mode.h;
return true;
}
I then use the information which is stored in m_display struct to enter fullscreen. Window creation and going to fullscreen are in separate functions, because players who use some other Linux distro than SteamOS have also an option to enter to windowed mode during the game:
window = SDL_CreateWindow("Game", 0, 0, m_display.w, m_display.h, window_flags);
...
SDL_SetWindowBordered(window, SDL_FALSE);
SDL_SetWindowPosition(window, 0, 0);
SDL_SetWindowSize(window, m_display.w, m_display.h);
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
For me this has worked without problems with all the Linux computers I have tested. I haven't been able to reproduce the problem in my own test environment.
My questions are:
Is this a problem in SDL implementation in Linux or am I doing something wrong?
And, if indeed I am to blame here:
Is this correct way to query the screen resolution?
If not, should I use some other method to query resolution (which is more reliable)?

The documentation for SDL_GetDisplayMode has an enlightening remark:
The display modes are sorted in this priority:
width -> largest to smallest
height -> largest to smallest
...
That means that what you are actually querying seems to be the largest supported resolution for the display, rather than the actual resolution.
You'll probably want to use SDL_GetCurrentDisplayMode or SDL_GetDesktopDisplayMode to get the currently active display mode for the display.

Related

SDL_Texture renders black after resize unless it is redrawn

I've got a bit of a nasty bug for you folks. (Yes, it's probably my bug and not SDL's.) I have been in the process of writing a modern C++ wrapper for SDL and everything appears to be working as intended. However, my Texture class has a strange bug: if it is redrawn after a resize, it looks fine, but if it is not, it becomes entirely black. Here is what that looks like:
Before the resize
After the resize
I can't exactly post just one part of the code here, so here is the entire folder (hosted on GitLab): SDL wrapper
Here is a small program that reproduces the error using this library:
#include "sdl_wrapper/context.hh"
#include "sdl_wrapper/video/context.hh"
#include "sdl_wrapper/render/renderer.hh"
#include "sdl_wrapper/render/texture.hh"
#include "sdl_wrapper/colors.hh"
#include "SDL2/SDL_events.h"
int main()
{
sdl::Context sdlContext;
sdl::video::Context videoContext = sdlContext.initVideo();
sdl::video::Window window = videoContext.createWindow("test", 0, 0, 800, 600).resizable().build();
int width = 800;
int height = 600;
sdl::render::Renderer renderer = window.createRenderer().targetTexture().build();
sdl::render::Texture example(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 400, 300);
renderer.setTarget(example);
renderer.setDrawColor(sdl::colors::Blue);
renderer.clear();
renderer.resetTarget();
bool run = true;
while (run)
{
SDL_Event e;
while (SDL_PollEvent(&e) != 0)
{
if (e.type == SDL_QUIT)
{
run = false;
break;
}
}
renderer.setDrawColor({0x44, 0x44, 0x44, 0xff});
renderer.clear();
renderer.copy(example, std::nullopt, {{width / 4, height / 4, example.getWidth(), example.getHeight()}});
renderer.present();
}
}
To reproduce, simply run this program and resize the window. There will be a blue square that becomes black after the resize.
I would greatly appreciate it if someone could point me in the right direction here. I would really like to avoid redrawing on every resize (feel free to argue with me on that point if I am misguided).
SDL doesn't promise to keep target textures data. There are cases, especially with d3d or mobile, where data is lost due to some big state change. Changing window size may sound not that big, but on some hardware/driver configurations is causes problems, I suppose that's the reason why SDL detects resize and drops all renderer data. You get SDL_RENDER_TARGETS_RESET event when you need to update your render textures.
That shouldn't happen with e.g. opengl renderer implementation (that may sound great but reasons behind it are not so great); on windows, SDL2 defaults to direct3d, which could be modified by issuing SDL_SetHint or setting envvars.

How to make window always on top in SFML?

I am trying to make a program with SFML who's window stays always on top. How can I achieve that with SFML? I've searched all around but to no avail.
"Program" is a small red dot in the middle of a screen that would imitate a crosshair and I need it to be on top of everything because a real game would be in the background (game does not have crosshair, only sighting).
Only other idea I have, is to use SFML's method getSystemHandle() which would give me OS-specific handle of a window. I am using Ubuntu 16.04 with Gnome and X and I am not quite sure how to code that functionality after I obtain the handle.
It's not possible with the current version of SFML, but since you only need it for X so far, you can just implement it yourself using a snippet from this old/rejected pull request.
void WindowImplX11::setTopmost(bool topmost)
{
static Atom wmStateAbove = XInternAtom(m_display, "_NET_WM_STATE_ABOVE", 1);
static Atom wmNetWmState = XInternAtom(m_display, "_NET_WM_STATE", 1);
if (wmStateAbove)
{
XClientMessageEvent emsg;
memset(&emsg, 0, sizeof(emsg));
emsg.type = ClientMessage;
emsg.window = m_window;
emsg.message_type = wmNetWmState;
emsg.format = 32;
emsg.data.l[0] = topmost;
emsg.data.l[1] = wmStateAbove;
XSendEvent(m_display, RootWindow(m_display, m_screen), false, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&emsg);
}
}
You'll have to retrieve m_display, m_window etc. on your own and/or reimplement the pull request into your source version.

SDL2 How to position a window on a second monitor?

I am using SDL_SetWindowPosition to position my window. Can I use this function to position my window on another monitor?
UPDATE
Using SDL_GetDisplayBounds will not return the correct monitor positions when the text size is changed in Windows 10. Any ideas how to fix this?
SDL2 uses a global screen space coordinate system. Each display device has its own bounds inside this coordinate space. The following example places a window on a second display device:
// enumerate displays
int displays = SDL_GetNumVideoDisplays();
assert( displays > 1 ); // assume we have secondary monitor
// get display bounds for all displays
vector< SDL_Rect > displayBounds;
for( int i = 0; i < displays; i++ ) {
displayBounds.push_back( SDL_Rect() );
SDL_GetDisplayBounds( i, &displayBounds.back() );
}
// window of dimensions 500 * 500 offset 100 pixels on secondary monitor
int x = displayBounds[ 1 ].x + 100;
int y = displayBounds[ 1 ].y + 100;
int w = 500;
int h = 500;
// so now x and y are on secondary display
SDL_Window * window = SDL_CreateWindow( "title", x, y, w, h, FLAGS... );
Looking at the definition of SDL_WINDOWPOS_CENTERED in SDL_video.h we see it is defined as
#define SDL_WINDOWPOS_CENTERED SDL_WINDOWPOS_CENTERED_DISPLAY(0)
so we could also use the macro SDL_WINDOWPOS_CENTERED_DISPLAY( n ) where n is the display index.
Update for Windows 10 - DPI scaling issue
It seems like there is indeed a bug with SDL2 and changing the DPI scale in Windows (i.e. text scale).
Here are two bug reports relevant to the problem. They are both still apparently unresolved.
https://bugzilla.libsdl.org/show_bug.cgi?id=3433
https://bugzilla.libsdl.org/show_bug.cgi?id=2713
Potential Solution
I am sure that the OP could use the WIN32 api to determine the dpi scale, for scale != 100%, and then correct the bounds by that.
DPI scaling issue ("will not return the correct monitor positions when the text size is changed")
It's a known issue with SDL2 (I encountered it in those versions: 2.0.6, 2.0.7, 2.0.8, probably the older versions have this issue as well).
Solutions:
1) Use manifest file and set there:
<dpiAware>True/PM</dpiAware>
(you need to include the manifest file to your app distribution)
2) Try SetProcessDPIAware().
Yes, you can use SetWindowPosition, if you know the boundaries of the second monitor.
You can use the function SDL_GetDisplayBounds(int displayIndex,SDL_Rect* rect) to get them.

SDL2 does not draw image using texture

I have been attempting to get a simple SDL2 program up to display an image and then exit. I have this code:
/* compile with `gcc -lSDL2 -o main main.c` */
#include <SDL2/SDL.h>
#include <SDL2/SDL_video.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
#include <SDL2/SDL_timer.h>
int main(void){
SDL_Init(SDL_INIT_VIDEO);
SDL_Window * w = SDL_CreateWindow("Hi", 0, 0, 640, 480, 0);
SDL_Renderer * r = SDL_CreateRenderer(w, -1, 0);
SDL_Surface * s = SDL_LoadBMP("../test.bmp");
SDL_Texture * t = SDL_CreateTextureFromSurface(r, s);
SDL_FreeSurface(s);
SDL_RenderClear(r);
SDL_RenderCopy(r, t, 0, 0);
SDL_RenderPresent(r);
SDL_Delay(2000);
SDL_DestroyTexture(t);
SDL_DestroyRenderer(r);
SDL_DestroyWindow(w);
SDL_Quit();
}
I am aware that I have omitted the normal checks that each function succeeds - they all do succeed, they were removed for ease of reading. I am also aware I have used 0 rather than null pointers or the correct enum values, this also is not the cause of the issue (as the same issue occurs when I correctly structure the program, this was a quick test case drawn up to test the simplest case)
The intention is that a window appear and shows the image (which is definitely at that directory), wait for a couple of seconds and exit. The result, however, is that the window appears correctly but the window is filled with black.
An extra note SDL_ShowSimpleMessageBox() appears to work correctly. I don't know how this relates to the rest of the framework though.
I'll just clear this, you wanted to make a texture, do it directly to ease control, plus this gives you better control over the image, try using this code, fully tested, and working, all you wanted was for the window to show the image and close within 2 seconds right?. If the image doesn't load then it's your image's location.
/* compile with `gcc -lSDL2 -o main main.c` */
#include <SDL.h>
#include <SDL_image.h>
#include <iostream> //I included it since I used cout
int main(int argc, char* argv[]){
bool off = false;
int time;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window * w = SDL_CreateWindow("Hi", 0, 0, 640, 480, SDL_WINDOW_SHOWN);
SDL_Renderer * r = SDL_CreateRenderer(w, -1, SDL_RENDERER_ACCELERATED);
SDL_Texture * s = NULL;
s = IMG_LoadTexture(r, "../test.bmp"); // LOADS A TEXTURE DIRECTLY FROM THE IMAGE
if (s == NULL)
{
cout << "FAILED TO FIND THE IMAGE" << endl; //we did this to check if IMG_LoadTexture found the image, if it showed this message in the cmd window (the black system-like one) then it means that the image can't be found
}
SDL_Rect s_rect; // CREATES THE IMAGE'S SPECS
s_rect.x = 100; // just like the window, the x and y values determine it's displacement from the origin which is the top left of your window
s_rect.y = 100;
s_rect.w = 640; //width of the texture
s_rect.h = 480; // height of the texture
time = SDL_GetTicks(); //Gets the current time
while (time + 2000 < SDL_GetTicks()) //adds 2 seconds to the past time you got and waits for the present time to surpass that
{
SDL_RenderClear(r);
SDL_RenderCopy(r, s, NULL, &s_rect); // THE NULL IS THE AREA YOU COULD USE TO CROP THE IMAGE
SDL_RenderPresent(r);
}
SDL_DestroyTexture(s);
SDL_DestroyRenderer(r);
SDL_DestroyWindow(w);
return 0; //returns 0, closes the program.
}
if you wanted to see a close button on the window and want it to take effect then create an event then add it to the while area to check if it's equal to SDL_Quit();, I didn't include it since you wanted it to immediately close within 2 seconds, thus, rendering the close button useless.
HAPPY CODING :D
When using SDL_RENDERER_SOFTWARE for the renderer flags this worked. Also it worked on a different machine. Guess there must be something screwed up with my configuration, although I'm not sure what it is because I'm still getting no errors shown. Ah well, mark as solved.
I believe this to be (not 100% sure, but fairly sure), due to this line of code:
SDL_Renderer * r = SDL_CreateRenderer(w, -1, 0);
According to the SDL wiki article SDL_CreateRenderer, the parameters required for the arguments that you are passing in are as follows:
SDL_Window* window
int index
Uint32 flags
You are passing in the pointer to the window correctly, as well as the index correctly, but the lack of a flag signifies to SDL that SDL should use the default renderer.
Most systems have a default setting for applications for which renderer should be used, and this can be modified on a application by application basis. If no default setting is provided for a specific application, the render look up immediately checks the default render settings list. The SDL wiki briefly refers to this list by the following line at the bottom of the remarks section:
"Note that providing no flags gives priority to available SDL_RENDERER_ACCELERATED renderers."
What's not explained here in the wiki is that the "renderers" the wiki is referring to comes from the default renderer list.
This leads me to believe that you have either changed a setting somewhere in the course of history of your computer, or elsewhere in you visual studio settings that is resulting in no list to be found. Therefore you must explicitly inform SDL which renderer to use because of your machine settings. Otherwise using an argument of 0 should work just fine. In the end this doesn't hurt as it's better to be explicit in your code rather than implicit if at all possible.
(That said, all of my deductions are based off of the fact that I am assuming that everything you said that works, works. If this is not true, then the issue could be because of a vast variety of reasons due to the lack of error checking.)

Win7 64bits Professional. GDI doesn't work normally occasionally until resize the window

In our project, we use GDI to draw waveform and any other graphics, however, we meet a special problem, the situations are:
The waveform stop drawing, just like not response.
The buttons in toolbar also stop updating its background while mouse hots it or clicks it, however, it is very mystical, the button could respond event and open the relevant dialog.
The dialog opened from toolbar could draw normally, any figures could work well.
While resize the window, double click the title of application, anything works normally again.
So, I hope to know whether there is any bug or driven problem in Win7 64bits Pro version which have been reported.
The similar problems in stack-overflow see this link:
What could cause redraw issues on 64-bit vista but not in 32-bit in .NET WInForms?
The code of drawing waveform and graphics, and
void CZMultiPatientView::UpdateDisplay(int ThisSecondCount, int ThisMillisecondCount)
{
CClientDC dc(this);
CDC *pDC = &dc;
//
// Draw waveforms
//
DrawPatientViewWaveforms(pDC);
//
// Update parameters
//
if ((GetElapsedMilliseconds(mLastAlarmMillisecondCount, ThisMillisecondCount) >= 500) || (mForceRedraw))
{
//
// !!! SHOULD NOT DO THIS IN HERE !!!
//
if (mSetupParameterDialogDisplayed)
{
//mParameterDataDialog.UpdateCurrentParameterValues() ;
m_pParamDlg->SendMessage(WM_UPDATE_VALUE); // Kevin
}
}
if ((GetElapsedSeconds(mLastAlarmsecondCount, ThisSecondCount) >= 1) || (mForceRedraw))
{
//Update messages (once a second)
UpdateMessages(pDC);
mLastAlarmsecondCount=ThisSecondCount;
}
if (GetElapsedMilliseconds(mLastDrawParametersCount, ThisMillisecondCount) >= 200 || (mForceRedraw))
{
PostMessage(APP_UPDATE_VIEWS, (WPARAM)ThisMillisecondCount, (LPARAM)mForceRedraw);
mLastDrawParametersCount = ThisMillisecondCount;
}
//
// Update alarms
//
if (GetElapsedMilliseconds(mLastAlarmMillisecondCount, ThisMillisecondCount) >= 500)
{
mLastAlarmMillisecondCount = ThisMillisecondCount ;
DoSoundAlarmTone();
}
mForceRedraw = 0 ;
}