SDL - Get native Screen Resolution - c++

My code:
window.cpp
Window::Window(int w, int h, const char *title, const char *icon)
{
height = h;
width = w;
if(SDL_Init( SDL_INIT_EVERYTHING ) == 0)
{
SDL_WM_SetCaption(title, NULL);
SDL_WM_SetIcon(SDL_LoadBMP(icon),NULL);
screen = SDL_SetVideoMode(width, height, 32,
SDL_SWSURFACE | SDL_RESIZABLE | SDL_DOUBLEBUF);
if(screen == NULL)
{
running = false;
return;
}
fullscreen = false;
}
else
running = false;
return;
}
Window::Window()
{
const SDL_VideoInfo* info = SDL_GetVideoInfo();
screenWidth = info->current_w;
screenHeight = info->current_h;
Window(640, 480, "Flatgu game", "rsc/img/icon.bmp");
}
window.h
class Window
{
public:
Window();
~Window();
int getWidth() {return width;}
int getHeight() {return height;}
bool isFullscreen() {return fullscreen;}
void toggleFullscreen();
private:
Window(int w, int h, const char *title, const char *icon);
bool fullscreen, running;
int height, width, screenWidth, screenHeight;
SDL_Surface *screen;
};
It compiles fine, but then, after compiling, I'm getting this ugly error:
What's the reason of my problem? Why do I get so weird numbers?
My aim is to store original screen resolution for further use (like toggling to fullscreen), and I have to do this before calling SDL_SetVideoMode(). That's why it is in the constructor.

You have a problem with calling SDL Video Functions before actually initializing SDL.
SDL_Init( SDL_INIT_EVERYTHING )
has to be called before
SDL_GetVideoInfo();
In your case you call SDL_GetVideoInfo(); first
const SDL_VideoInfo* info = SDL_GetVideoInfo(); //<-- calls SDL_GetVideoInfo();
screenWidth = info->current_w;
screenHeight = info->current_h;
Window(640, 480, "Flatgu game", "rsc/img/icon.bmp"); //<-- initializes SDL
So the solution is simple; make the call SDL_Init( SDL_INIT_EVERYTHING ) immediately at the start of your program, then you can call SDL_GetVideoInfo(); as much as you like.
You will have to restructure your class Window slightly.

To get the best video mode call SDL_GetVideoInfo before setting up the video (before calling SDL_SetVideoMode).
But you still have to initialize the video subsystem before calling it (SDL_Init(SDL_INIT_VIDEO)).

I know this is old, but there's a big mistake in the code.
Window(640, 480, "Flatgu game", "rsc/img/icon.bmp");
creates a nameless instance of a Window, so the instance that calls it will still have uninitialized variables. It looks like you were trying to use delegating constructors, but in that case the call to the other constructor must be in the member initializer list.
See this page.

Related

SDL_RenderCopy not doing anything

I'm calling SDL_RenderCopy and it gets called and returns normally but doesn't draw anything to the window. Edited to make the question and code clearer. I'm thinking I might be trying to use something beyond its scope and hence it can't be called but this doesn't produce any error so I'm not sure. Here's the simple picture I refer to https://commons.wikimedia.org/wiki/Category:PNG_chess_pieces/Standard_transparent#/media/File:Chess_kdt60.png
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
// Recreation of the problem. Doesnt draw anything onto the white screen.
class King{
public:
King(SDL_Renderer *renderer){
SDL_Surface *Piece;
Piece = IMG_Load("Pieces/BK.png"); // I'll attach the picture
king = SDL_CreateTextureFromSurface(renderer, Piece);
SDL_FreeSurface(Piece);
kingRect.h = 100;
kingRect.w = 100;
}
~King(){}
void render(SDL_Renderer *renderer){
SDL_RenderCopy(renderer, king, NULL, &kingRect); // 99% sure the problem is this
}
private:
SDL_Texture *king;
SDL_Rect kingRect;
};
class Game {
public:
Game(const char *title, int sidelength){
isRunning = true;
if(SDL_Init(SDL_INIT_EVERYTHING) != 0) isRunning = false;
window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, sidelength, sidelength, SDL_WINDOW_OPENGL);
if(window == NULL) isRunning = false;
renderer = SDL_CreateRenderer(window, -1, 0);
if(!renderer) isRunning = false;
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
}
~Game(){}
void handleEvents(){
//Handles Events. I know this works.
}
}
void update(){};
void render(){
SDL_RenderClear(renderer);
BK.render(renderer);
SDL_RenderPresent(renderer);
}
void clean(){
//Cleans up after. I know this works.
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
}
bool running(){return(isRunning);}
King BK{renderer};
private:
bool isRunning{true};
SDL_Window *window;
SDL_Renderer *renderer;
};
Game *game = nullptr;
int main(int argc, const char *argv[]){
game = new Game("Testing Window", 800);
while(game->running()){
game->handleEvents();
game->update();
game->render();
}
game->clean();
return(0);
}
King BK{renderer}; field gets initialised before your Game::Game finishes and gets a chance to assign a renderer, so it gets NULL instead. NULL is not a valid renderer and can't create textures. If you would have checked for error you would have got Invalid renderer message. Also decent compiler with enabled warnings will tell something like warning: 'Game::renderer' is used uninitialized in this function [-Wuninitialized]; consider enabling better warning levels in your compiler.
Second thing is that you never called IMG_Init with required image formats you intend to load.
Third thing is that code is misformatted and wouldn't compile without modifications. I suggest testing code that you post as MCCVE for still being compilable and reproducing your problem (as MCCVE implies).

Switching Between windowed and full screen in OpenGL/GLFW 3.2

I am in the process of learning OpenGL on Linux but I can't get mode switching working (windowed to full screen and back).
The window appears to be going into full screen but but not looking correct. To switch modes a new window is being created and old one destroyed.
void OpenGLWindow::FullScreen(bool fullScreen, int width, int height)
{
GLFWwindow *oldHandle = m_window;
m_fullscreen = fullScreen;
m_width = width;
m_height = height;
m_window = glfwCreateWindow(width, height, m_caption.c_str(),
fullScreen ? m_monitor : NULL, m_window);
if (m_window == NULL)
{
glfwTerminate();
throw std::runtime_error("Failed to recreate window.");
}
glfwDestroyWindow(oldHandle);
m_camera->Invalidate();
// Use entire window for rendering.
glViewport(0, 0, width, height);
glfwMakeContextCurrent(m_window);
glfwSwapInterval(1);
if (m_keyboardHandler) SetKeyboardHandler(m_keyboardHandler);
}
Initial Window
Full Screen (incorrect)
Return to Windowed
Updates to Question
I have updated the code to use your code and getting the same issue. On your suggestion I am now updating the camera, but again no avail :(
void OpenGLCamera::Invalidate()
{
RecalculateProjection(m_perspProjInfo->Width(), m_perspProjInfo->Height());
m_recalculateViewMatrix = true;
m_recalculatePerspectiveMatrix = true;
m_recalculateProjectionMatrix = true;
}
void OpenGLCamera::RecalculateProjection(int width, int height)
{
float aspectRatio = float(width) / height;
float frustumYScale = cotangent(degreesToRadians(
m_perspProjInfo->FieldOfView() / 2));
float frustumXScale = frustumYScale;
if (width > height)
{
// Shrink the x scale in eye-coordinate space, so that when geometry is
// projected to ndc-space, it is widened out to become square.
m_projectionMatrix[0][0] = frustumXScale / aspectRatio;
m_projectionMatrix[1][1] = frustumYScale;
}
else {
// Shrink the y scale in eye-coordinate space, so that when geometry is
// projected to ndc-space, it is widened out to become square.
m_projectionMatrix[0][0] = frustumXScale;
m_projectionMatrix[1][1] = frustumYScale * aspectRatio;
}
}
Rabbid : When I resize:
Rabbid : When I go to full screen:
In the following, I'll describe a small but handy class, which deals with resizing a GLFW window and handles switch fullscreen window on and off.
All the used GLFW functions are well documented in the GLFW documentation.
#include <GL/gl.h>
#include <GLFW/glfw3.h>
#include <array>
#include <stdexcept>
class OpenGLWindow
{
private:
std::array< int, 2 > _wndPos {0, 0};
std::array< int, 2 > _wndSize {0, 0};
std::array< int, 2 > _vpSize {0, 0};
bool _updateViewport = true;
GLFWwindow * _wnd = nullptr;
GLFWmonitor * _monitor = nullptr;
void Resize( int cx, int cy );
public:
void Init( int width, int height );
static void CallbackResize(GLFWwindow* window, int cx, int cy);
void MainLoop ( void );
bool IsFullscreen( void );
void SetFullScreen( bool fullscreen );
};
When creating the window, then the user function pointer (glfwSetWindowUserPointer) is set to the window management class. And the resize callback is set by glfwSetWindowSizeCallback. After the window is created its current size and position can be get by glfwGetWindowPos and glfwGetWindowSize.
void OpenGLWindow::Init( int width, int height )
{
_wnd = glfwCreateWindow( width, height, "OGL window", nullptr, nullptr );
if ( _wnd == nullptr )
{
glfwTerminate();
throw std::runtime_error( "error initializing window" );
}
glfwMakeContextCurrent( _wnd );
glfwSetWindowUserPointer( _wnd, this );
glfwSetWindowSizeCallback( _wnd, OpenGLWindow::CallbackResize );
_monitor = glfwGetPrimaryMonitor();
glfwGetWindowSize( _wnd, &_wndSize[0], &_wndSize[1] );
glfwGetWindowPos( _wnd, &_wndPos[0], &_wndPos[1] );
_updateViewport = true;
}
When the resize notification occurs, then the pointer to the window management class can be get by glfwGetWindowUserPointer:
static void OpenGLWindow::CallbackResize(GLFWwindow* window, int cx, int cy)
{
void *ptr = glfwGetWindowUserPointer( window );
if ( OpenGLWindow *wndPtr = static_cast<OpenGLWindow*>( ptr ) )
wndPtr->Resize( cx, cy );
}
Any change of the window size is notified and the new window size is stored (glfwGetWindowSize):
void OpenGLWindow::Resize( int cx, int cy )
{
_updateViewport = true;
}
When the window size has changed, then the viewport has to be suited to the window size (glViewport). This can be done in the main loop of the application:
void OpenGLWindow::MainLoop ( void )
{
while (!glfwWindowShouldClose(_wnd))
{
if ( _updateViewport )
{
glfwGetFramebufferSize( _wnd, &_vpSize[0], &_vpSize[1] );
glViewport( 0, 0, _vpSize[0], _vpSize[1] );
_updateViewport = false;
}
// ..... render the scene
glfwSwapBuffers(_wnd);
glfwPollEvents();
}
}
If the current window is in full screen mode, can be achieved by asking for the monitor that the window uses for full screen mode (glfwGetWindowMonitor):
bool OpenGLWindow::IsFullscreen( void )
{
return glfwGetWindowMonitor( _wnd ) != nullptr;
}
To switch the full screen mode on and off, glfwSetWindowMonitor has to be called, either with the monitor for the full screen mode, or with nullptr:
void OpenGLWindow::SetFullScreen( bool fullscreen )
{
if ( IsFullscreen() == fullscreen )
return;
if ( fullscreen )
{
// backup window position and window size
glfwGetWindowPos( _wnd, &_wndPos[0], &_wndPos[1] );
glfwGetWindowSize( _wnd, &_wndSize[0], &_wndSize[1] );
// get resolution of monitor
const GLFWvidmode * mode = glfwGetVideoMode(_monitor);
// switch to full screen
glfwSetWindowMonitor( _wnd, _monitor, 0, 0, mode->width, mode->height, 0 );
}
else
{
// restore last window size and position
glfwSetWindowMonitor( _wnd, nullptr, _wndPos[0], _wndPos[1], _wndSize[0], _wndSize[1], 0 );
}
_updateViewport = true;
}
I recommend you to not create a new Window with glfwCreateWindow when you just want to switch between windowed and fullscreen. Use glfwSetWindowMonitor instead.
When you create a window with fullscreen enabled, you have to pass arguments which are compatible with a video mode on the monitor. You can get the standard video mode on the primary monitor like this:
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
and to switch to fullscreen:
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
Just pass a nullptr-mode and your own values of course:
glfwSetWindowMonitor(window, nullptr, 0, 0, windowWidth, windowHeight, windowRefreshRate);
And don't forget to resize the viewport and update the camera.
Are you resizing the viewport and updating the camera when the user resizes the window?
There are a couple of issues with your code:
Assuming that glfwCreateWindow will set the resolution to width * height in fullscreen mode is not correct. The GLFW documentation states (emphasis mine):
For full screen windows, the specified size becomes the resolution of the window's desired video mode. As long as a full screen window is not iconified, the supported video mode most closely matching the desired video mode is set for the specified monitor.
Assuming that the window size is specified in "pixels" is not correct either.Quoting the relevant part of the documentation again:
While the size of a window is measured in screen coordinates, OpenGL works with pixels. The size you pass into glViewport, for example, should be in pixels. On some machines screen coordinates and pixels are the same, but on others they will not be. There is a second set of functions to retrieve the size, in pixels, of the framebuffer of a window.
Issues 1 and 2 can be solved by simply calling glfwGetFramebufferSize after the window was created. This leaves us with issue 3:
You call glViewport without having a current GL context -
resulting in undefined behavior, and especially in not setting the viewport at all. Now that is actually an interesting one, because the initial viewport for the new context will be the full new window, so that your mistakes 1 and 2 have no direct effect. They still might have some effect later if your code relies on m_width and m_height containing useful values, though.

SDL2: How to keep aspect ratio when resizing the window

I am trying to create a SDL window which keeps its aspect ratio when resize event happens. If user widens the window, the height is increased and vice versa. I catch the SDL_WINDOWEVENT_RESIZED event, calculate new width or height which maintains the aspect ratio and then call SDL_SetWindowSize() with calculated values.
The problem is that calling the SDL_SetWindowSize() function inside the event polling loop does nothing on the screen. SDL does update the window size variables (calling SDL_GetWindowSize() in my main loop returns the updated window dimensions). However, the actual window is not updated.
The only way I can get this to work is to call constantly SDL_SetWindowSize() in the main loop, but I think that is the wrong way of doing things. The code below illustrates my problem. Is there a better and cleaner way to get this to work?
I am using SDL 2.0.3 and 64-bit Ubuntu Linux with GNOME desktop.
#include <SDL2/SDL.h>
static const float ASPECT_RATIO = 16.f/9.f;
SDL_Window* window;
SDL_Renderer* renderer;
uint32_t windowID;
SDL_Rect screen;
bool done = false;
bool resizeDone = false;
void handle_events()
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_WINDOWEVENT:
if(e.window.windowID == windowID) {
switch(e.window.event) {
case SDL_WINDOWEVENT_RESIZED: {
int width = e.window.data1;
int height = e.window.data2;
float aspectRatio = (float)width/(float)height;
if(aspectRatio != ASPECT_RATIO) {
if(aspectRatio > ASPECT_RATIO) {
height = (1.f / ASPECT_RATIO) * width;
}
else {
width = ASPECT_RATIO * height;
}
printf("Setting window size to %d, %d, aspect ratio: %f\n",
width, height, (float)width/(float)height);
}
screen.w = width;
screen.h = height;
SDL_SetWindowSize(window, width, height); // <-- does not work
resizeDone = true;
break;
}
}
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
}
void run() {
while(!done) {
//SDL_SetWindowSize(window, screen.w, screen.h); // <-- works
handle_events();
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
if(resizeDone) {
int w, h;
SDL_GetWindowSize(window, &w, &h);
printf("SDL_GetWindowSize: %d, %d\n", w, h);
resizeDone = false;
}
}
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
uint32_t window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
windowID = SDL_GetWindowID(window);
renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
run();
SDL_Quit();
return 0;
}
Some window managers seems to ignore resize requests made while WM itself resizes window (e.g. while mouse button held). On contrary, SDL_GetWindowSize returns cached values, which in that specific case sometimes happens to be wrong.
I see no platform-independent way to achieve that, other than constantly calling SDL_SetWindowSize on each frame, just in case. It could be achieved using platform-specific APIs, though (like SDL_GetWindowSysWMInfo and then using Xlib).
On macOS, I have solved it like this:
cocoa.m:
#import <Cocoa/Cocoa.h>
void SetWindowRatio(void *window) {
NSWindow *win = (__bridge NSWindow*) window;
win.aspectRatio = NSMakeSize( 1280, 720 );
}
main.cpp:
#include <SDL.h>
#include <SDL_syswm.h>
extern "C" void SetWindowRatio(void *window);
// and later..
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(sdl.window, &wmInfo);
SetWindowRatio(wmInfo.info.cocoa.window);
Perhaps something similar could be done on Linux, only access different part of wmInfo.info. and call the native function?

Mac OS OpenGL - problems with CPU usage

Since the Mavericks update(I'm on the 10.10 now) the debug window is showing this message :
The function ‘CGContextErase’ is obsolete and will be removed
in an upcoming update. Unfortunately, this application, or a library
it uses, is using this obsolete function, and is thereby contributing
to an overall degradation of system performance.
I'm creating an OpenGL (SDL) application, which written in C++ and now I have issues with CPU's memory because of my application(it uses 100% powers of my MacBook's Intel i5 processor).
So, maybe it is because all of this CGContextErase function.
How do I solve it?
Well, maybe there are mistakes in my code :
//I'm using the SDL2
#include <SDL2/SDL.h>
class sWindow {
public:
SDL_Window *win;
SDL_Surface *winSur;
SDL_Event e;
void createWindow(char*,int,int,int,int,Uint32);
void update();
void render();
void close();
SDL_Rect WIN_RECT;
char WIN_TITLE = NULL;
int WIN_ID = -1;
};
SDL_Rect newRect(int x, int y, int w, int h) {
SDL_Rect returnRect;
returnRect.x = x;
returnRect.y = y;
returnRect.w = w;
returnRect.h = h;
return returnRect;
}
//The window, where the content(surface) will be rendered.
sWindow win1;
//Window's construct
void sWindow::createWindow(char* title, int x, int y, int w, int h, Uint32 flags) {
win = SDL_CreateWindow(title, x, y, w, h, flags);
winSur = SDL_GetWindowSurface(win);
WIN_RECT = newRect(x, y, w, h);
WIN_ID = SDL_GetWindowID(win);
}
//The logic and render actions...
void sWindow::update() {
}
//Window's destructor
void sWindow::close() {
SDL_DestroyWindow(win);
SDL_FreeSurface(winSur);
}
//Main loop control
bool quit = false;
//Initilize the OpenGL and other libs(SDL2)
bool inited() {
bool result = true;
if (SDL_INIT_VIDEO <= 0) {
result = false;
printf("SDL_INIT_VIDEO Failed");
}
return result;
}
//Main loop...
int main(int argc, char* argv[]) {
if (inited()) {
win1.createWindow((char*)"SpaceCode", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 380, 280, SDL_WINDOW_SHOWN);
while (!quit) {
while (SDL_PollEvent(&win1.e) != 0) {
if (win1.e.type == SDL_QUIT) {
quit = true;
}
}
}
}
win1.close();
SDL_Quit();
return 0;
}
This code would always result in high CPU use as there is no framerate cap in place therefore it will just try to process it as fast as possible.
See various posts below:
http://lazyfoo.net/SDL_tutorials/lesson14/
https://gamedev.stackexchange.com/questions/79490/should-i-cap-the-frame-rate-in-sdl
Plus many more searching on Google.
I had this problem. It was caused by an outdated Wacom Tablet driver. If you have such a driver installed I'd recommend removing it, then reinstalling a more recent driver. That did the trick for me.

Modifying pointer leads to SIGSEGV signal

this is my first experience,so don't be picky.
It's SIGSEGV problem, and it appears only in class linking.
The problem within "SDL_surface *ScreenSurface" pointer.
Here goes the code...
Screen header
class screen
{
public:
screen();
SDLclass_Window *MainWindow=NULL;
SDL_Surface *ScreenSurface=NULL; //this is the problem pointer to the struct that cause error
//Those pointer are't NULL, see below
virtual ~screen();
protected:
private:
const int SCREEN_WIDTH = 1280;
const int SCREEN_HEIGHT = 726;
bool init();
};
Screen code
screen::screen()
{
if(!init())
{
std::cout<<"ERROR at INIT";
SDL_Delay( 4000 );
}
else
{
bool quit=0;
SDL_Event e;
while (!quit)
{
SDL_UpdateWindowSurface(MainWindow);
//Looking for events
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
}
}
}
this->~screen();
}
bool screen::init() //Initialization
{
bool succses=1;
if (SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
std::cout<< "SDL could not initialize! SDL_Error:"<< SDL_GetError()<<std::endl;
succses=0;
return succses;
}
MainWindow= SDL_CreateWindow( "Arachy: WIP Version", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN ); //Setting parameters to SDL_window
if (MainWindow==NULL)
{
std::cout<<"can't create window"<<SDL_GetError()<<std::endl;
succses=0;
return succses;
}
else
{
//Get window surface
ScreenSurface = SDL_GetWindowSurface( MainWindow );// telling ScreenSurface that it our main screen surface
}
return succses;
}
screen::~screen()
{
SDL_Quit();
}
Activity header
(another class that uses SDL_Surface which defined above in class Screen)
class activity
{
public:
activity();
virtual ~activity();
protected:
private:
screen mainScreen;
void load();
};
Activity code (I'm trying to modify SDL_Surface with SDL_FillRect() after that i got SIGSEGV signal during debug
activity::activity()
{
SDL_FillRect(mainScreen.ScreenSurface,NULL,SDL_MapRGB(mainScreen.ScreenSurface->format,255,000000,255));
}
Please , help me to find out...
Thank you
The problem is twofold:
A) It's likely that the destructor of screen is called twice, causing SDL_DestroyWindow() and SDL_FreeSurface() to be called on invalid, non-NULL pointers.
If you absolutely, positively want to call your destructor twice, you should at least reset these pointers to NULL after destroying the objects they point to.
But in most cases you should simply, you know, not explicitly call the destructor...
B) As set now, activity's constructor body will never be called until the main event loop is over.