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?
Related
I am new to C++ and am currently trying to implement namespaces.
Here, you can see, I have a namespace, GameEngine_CORE, where all the behind-the-scenes work is done. I have the namespace SandBox, which is where the user actually would write their game.
I need SandBox to be able to access some of the variables in GameEngine_CORE, which seems to work fine (I have put a list of using GameEngine_CORE::... to save the user's time).
However, I also need GameEngine_CORE to be able to call the start() and update() function in SandBox, but upon compilation I get an error every time I try and call something from SandBox, eg: SandBox::start();
'SandBox' is not a class or namespace name
I am coding in Visual Studio, and compiling in Debug mode for Windows x64.
#pragma once
#include "SDL.h"
#undef main
#include <iostream>
#include <math.h>
#include <vector>
#include <stdio.h> // Temp
#include "Vec2.h"
#include "Log.h"
#include "Time.h"
#include "Boundary.h"
#include "Ray.h"
#include "Particle.h"
#include "Polygon.h"
#include "Path.h"
#include "Circle.h"
namespace GameEngine_CORE {
// Renderer
SDL_Renderer* renderer;
SDL_Window* window;
bool isRunning;
bool fullscreen;
// Mouse
int mouseX;
int mouseY;
Uint32 mouse;
// Keyboard
Vec2 arrowInp;
// Collision Stack
std::vector<Boundary> collisionStack;
// Log
Log logger;
// Physics
Time timer;
Time clockTime;
float deltaTime;
Vec2 gravity = Vec2(0, -9.8f);
void handleEvents();
void render();
void createWindow();
void destroyWindow();
// For the SandBox Program
void SandBox::start();
void SandBox::update();
int main() { // Entry Point
double m_LastClock = 0.0;
clockTime.StartTimer(); // Start Counter
SandBox::start(); // For user
while (isRunning) {
double m_CurrentClock;
m_CurrentClock = clockTime.GetTimer(); // Get Counter
deltaTime = (float)(m_CurrentClock - m_LastClock);
if (deltaTime > 0.15f) {
deltaTime = 0.15f;
}
handleEvents();
render();
m_LastClock = m_CurrentClock;
}
destroyWindow();
return 0;
}
// Initialise the window to draw to
void createWindow() {
// Set window size and type
fullscreen = true;
Uint32 flags = 0;
flags = SDL_WINDOW_RESIZABLE;
if (fullscreen) {
flags = flags | SDL_WINDOW_MAXIMIZED;
}
if (SDL_Init(SDL_INIT_EVERYTHING) == 0) {
logger.Message("Subsystems Initialised");
window = SDL_CreateWindow("2D Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 300, 300, flags);
if (window) {
logger.Message("Window Created");
// Minimum window size
SDL_SetWindowMinimumSize(window, 1000, 1000);
}
// Create Renderer for window
renderer = SDL_CreateRenderer(window, -1, 0);
if (renderer) {
SDL_SetRenderDrawColor(renderer, 121, 121, 121, 255);
logger.Message("Renderer Created");
// Set how to blend alphas and colours
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
isRunning = true;
}
}
}
void destroyWindow() {
// Frees memory associated with renderer and window
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window); //error here
SDL_Quit();
}
// Handles SDL events
void handleEvents() {
// Check for quit
SDL_Event event;
SDL_PollEvent(&event);
// Check we have latest inputs
SDL_PumpEvents();
// Reset inputs
arrowInp.x = 0; arrowInp.y = 0;
// If we get quit event, stop running and free up memory
switch (event.type) {
case SDL_QUIT:
isRunning = false;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_LEFT: arrowInp.x = -1; break;
case SDLK_RIGHT: arrowInp.x = 1; break;
}
switch (event.key.keysym.sym)
{
case SDLK_UP: arrowInp.y = -1; break;
case SDLK_DOWN: arrowInp.y = 1; break;
}
break;
default:
break;
}
}
// Render Function
void render() {
// Set back ground colour and clear renderer every frame
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SandBox::update();
SDL_RenderPresent(renderer); // Draw to the screen
}
}
using GameEngine_CORE::createWindow;
using GameEngine_CORE::deltaTime;
using GameEngine_CORE::mouse;
using GameEngine_CORE::mouseX;
using GameEngine_CORE::mouseY;
using GameEngine_CORE::renderer;
using GameEngine_CORE::arrowInp;
using GameEngine_CORE::collisionStack;
namespace SandBox { // Where the user of the engine would write code
// Drawing Vars
Particle particle = Particle(50, 359.99f, Vec2(900, 700));
Boundary boundaries[] = { Boundary(Vec2(800, 0), Vec2(800, 1000)), Boundary(Vec2(300, 300), Vec2(400, 700)), Boundary(Vec2(300, 700), Vec2(800, 800)) };
GameEngine::Polygon square = GameEngine::Polygon(Vec2(800, 400), 45, Vec2(400, 200), 4);
GameEngine::Polygon poly1 = GameEngine::Polygon(Vec2(200, 400), Vec2(200, 200), 6);
GameEngine::Polygon ellipse = GameEngine::Polygon(Vec2(1200, 600), Vec2(200, 400), 64);
Path path = Path(Vec2(500, 100));
Circle circle = Circle(100, Vec2(800, 500));
GameEngine::Polygon player = GameEngine::Polygon(Vec2(700, 400), 0, Vec2(60, 60), 16);
float moveSpeed;
Vec2 velocity;
float rotator = 0;
// Functions
static void collision();
static void start() { // Called when the program starts
createWindow();
moveSpeed = 1000.0f;
path.addPoints(std::vector<Vec2> { Vec2(200, 100), Vec2(200, 800), Vec2(350, 800), Vec2(700, 650), Vec2(400, 400), Vec2(200, 100)});
}
static void update() { // Repeats every frame
printf("%f secs \n", deltaTime);
// Get the mouse' current state
mouse = SDL_GetMouseState(&mouseX, &mouseY);
particle.setPos(Vec2((float)mouseX, (float)mouseY));
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); // Draw Boundaries
rotator += 0.01f;
poly1.setRot(rotator);
// Player
player.Show(renderer);
square.Show(renderer);
poly1.Show(renderer);
ellipse.Show(renderer);
path.Show(renderer);
collision();
}
static void collision() {
// Clears stack
collisionStack.clear();
// Adds colliders to stack
square.makeCollider(collisionStack);
poly1.makeCollider(collisionStack);
ellipse.makeCollider(collisionStack);
path.makeCollider(collisionStack);
player.makeCollider(collisionStack);
// Draw Particles
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 100);
particle.collide(renderer, collisionStack);
}
static void movement() {
Vec2 velocity = arrowInp * moveSpeed;
}
}
I don't imagine anyone needs to see the header files, but I can provide them if requested. I also declare my start() and update() functions in GameEngine_CORE, which I'm not sure is correct, however I was getting complaints from Visual Studio that it could not find the function definitions otherwise.
I am creating a program, and I have a rectangle. Basically, I am creating a custom window inside the SDL2 window, which is a Rect, with another Rect being its toolbar. I am struggling for hours trying to figure how to detect how deep is the mouse within the Rect, but I am failing every time. The most obvious equation for me was int deep = mousePos.x - x, but it flickers between two positions every time I move my mouse. I then have tried a LOT of other calculations, but none of them worked. Either they flickered between two positions with descending values, or they were completely static and didn't move, or always moved a lot in a specific direction. I have visually represented the calculations, which were mostly correct, but the flickering between two positions is always ruining it. Thanks for any help. I am providing source code, too.
SOURCE:
//
// main.cpp
// Open
//
// Created by Fildom on 28.12.2021.
//
// Library includes
#include <SDL2/SDL.h>
#include <stdio.h>
bool isdown = false;
// Screen rendering helper
void on_render(SDL_Window* window, SDL_Renderer* renderer);
// Concatenation (probably not spelt correctly but idrc) for easier use
const char * concat(const char * one, const char * two) {
char * buffer = new char[strlen(one) + strlen(two) + 1];
strcpy(buffer, one);
strcat(buffer, two);
return buffer;
}
// Main method, required for performing application run
int main(int argc, const char * argv[]) {
SDL_Renderer *renderer = NULL; // Initialize the renderer
SDL_Event event = { 0 }; // Create a null event
SDL_Window *win = NULL; // Initialize a window
int exit = 0; // If exit is 1, win closes
// Window pre-modifiers
const char * appName = "test";
// SDL VIDEO mode initialization and error check
if(SDL_Init(SDL_INIT_VIDEO) == -1) {
printf("SDL_Init() failed with \"%s.\"", SDL_GetError());
return 1;
}
// Create the window and load it into a previously defined variable
win = SDL_CreateWindow(concat(appName, " - Initialization in progress"), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
// Window creation was unsuccessfull
if(!win) {
printf("SDL_CreateWindow() failed with \"%s.\"", SDL_GetError());
return -1;
}
// Creating renderer
renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
// If renderer failed to load...
if(!renderer) {
printf("SDL_CreateRenderer() failed with \"%s.\"", SDL_GetError());
return -1;
}
// Everything has gone OK, thus the window can be renamed
SDL_SetWindowTitle(win, appName);
// Game loop, as said previously, false = 0, true = 1.
// while !exit |
// while not exit <- |
// while exit is 0 (false) <-
while (!exit) {
// Event loop
if (SDL_WaitEvent(&event)) {
// Event types
switch(event.type) {
case SDL_QUIT:
exit = 1; // Exit = 1, thus app is being exitted
break;
case SDL_KEYDOWN:
if(event.key.keysym.sym == SDLK_ESCAPE) exit = 1; // If ESC is pressed
break;
case SDL_MOUSEBUTTONUP:
isdown = false;
break;
case SDL_MOUSEBUTTONDOWN:
isdown = true;
break;
case SDL_MOUSEMOTION:
break;
case SDL_WINDOWEVENT:
switch(event.window.event) {
case SDL_WINDOWEVENT_CLOSE: // macOS and/or other OSes rely on right click + Quit to fully exit out of an application. This makes it easier by just hitting the close button.
exit = 1;
break;
}
break;
default: break;
}
}
// Render the screen
on_render(win, renderer);
// Swap buffers to display
SDL_RenderPresent(renderer);
}
// Cleanup
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
class Window {
public:
int x, y, w, h;
SDL_Color winc, wintc;
bool draggable;
int titleh;
Window(int wx, int wy, int ww, int wh, SDL_Color window_color = {255, 255, 255, 255}, SDL_Color window_title_color = {200, 200, 200, 255}) {
x = wx;
y = wy;
w = ww;
h = wh;
winc = window_color;
wintc = window_title_color;
draggable = true;
titleh = 50;
}
int tx, ty = 0;
void Render(SDL_Renderer* renderer) {
SDL_Rect _t;
_t.x = x;
_t.y = y;
_t.w = w;
_t.h = h;
SDL_Rect title;
title.x = x;
title.y = y;
title.w = w;
title.h = titleh;
SDL_SetRenderDrawColor(renderer, winc.r, winc.g, winc.b, winc.a);
SDL_RenderFillRect(renderer, &_t);
SDL_SetRenderDrawColor(renderer, wintc.r, wintc.g, wintc.b, wintc.a);
SDL_RenderFillRect(renderer, &title);
int mx, my;
SDL_PumpEvents();
SDL_GetMouseState(&mx, &my);
SDL_Point ms;
ms.x = mx;
ms.y = my;
if (SDL_PointInRect(&ms, &title) and isdown) {
x = mx - tx;
y = my - ty;
tx = x;
ty = y;
}
}
};
Window test1 = Window(200, 100, 300, 200);
void on_render(SDL_Window* window, SDL_Renderer* renderer) {
SDL_Rect wind = { 0 };
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_GetWindowSize(window, &wind.w, &wind.h);
test1.Render(renderer);
}
I ended up doing it in a different way. Instead of using mousePosition.x and y, I used relative X and Y which worked out perfectly.
code for that is
mousePosition.relX and mousePosition.relY;
The problem with my code is that I am making a pong game in SDL 2.0 in c++. I did everything until creating the movement. When the player paddle moves, it leaves behind a trail in the same color as the paddle. I watched some videos on YouTube, but when they do the movement it's nice and clear and for me to fix this but I need to recolor the background every time the player moves, which makes it being all flashy and if I hold the button I don't see the paddle at all.
#include<iostream>
#include<SDL2/SDL.h>
#include<SDL2/SDL_image.h>
#include<windows.h>
#define width 800
#define height 600
using namespace std;
bool run = true;
class Player{
private:
SDL_Window* window = SDL_CreateWindow("Pong!", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_RESIZABLE);
SDL_Surface* Screen = SDL_GetWindowSurface(window);
Uint32 screen_color = SDL_MapRGB(Screen->format, 0, 0, 0);
Uint32 In_game_RGB = SDL_MapRGB(Screen->format, 255, 255, 255);
SDL_Rect Pl;
SDL_Rect AI;
SDL_Rect Ball;
SDL_Rect ClearP;
SDL_Rect ClearAI;
public:
Player(){
//Player parameters
Pl.x = 60;Pl.y = 225;Pl.w = 25;Pl.h = 200;
//AI parameters
AI.x = 720;AI.y = 225;AI.w = 25;AI.h = 200;
//Ball parameters
Ball.x = width/2;Ball.y = height/2+10;Ball.w = 25;Ball.h = 25;
//Recoloring parameters
ClearP.x = 0;ClearP.y = 0; ClearP.w = 375;ClearP.h = height;
ClearAI.x = 425;ClearAI.y = 0;ClearAI.w = 375;ClearAI.h = height;
//Make the screen color black
SDL_FillRect(Screen, NULL, screen_color);
}
void scrUpdate(){
SDL_UpdateWindowSurface(window);
}
void drawPlayer(){
SDL_FillRect(Screen, &Pl, In_game_RGB);
}
void drawComputer(){
SDL_FillRect(Screen, &AI, In_game_RGB);
}
void ball(){
SDL_FillRect(Screen, &Ball, In_game_RGB);
}
void Movement(){
if(GetAsyncKeyState(VK_DOWN)){
Pl.y += 2;
SDL_FillRect(Screen,&ClearP,screen_color);
}
if(GetAsyncKeyState(VK_UP)){
SDL_FillRect(Screen,&ClearP,screen_color);
Pl.y -= 2;
}
}
};
void EventCheck(){
SDL_Event event;
if(SDL_PollEvent(&event)){
if(event.type == SDL_QUIT){
run = false;
}
}
}
int main( int argc, char *argv[] )
{
SDL_Init(SDL_INIT_EVERYTHING);
Player Play;
//Player Computer();
while(run){
Play.scrUpdate();
Play.drawPlayer();
Play.drawComputer();
Play.ball();
Play.Movement();
EventCheck();
}
SDL_Quit();
return EXIT_SUCCESS;
}
It would help to show some code or an example of what you have been doing, or a link to one of the videos you have been watching:
Youtube tutorial
but I suggest taking a look at:
screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE); and SDL_Flip(screen) as those have to do with screen buffering and drawing.
Another possibilty is that you are running an outdated version of SDL, or an incompatible one with your current system.
To be able to give a more complete and proper answer, I'd highly suggest adding more information about your code, screenshots of results and your version of SDL and operating system.
Also, you said it was flashy when you hold the paddle. I think it must be that you are performing your logic to move the paddle and you redraw the paddle once it's still. If you are redrawing the entire screen constantly, consider double buffering.
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.
(See "Edit 2" below for the solution.)
I need to create SDL surfaces from scratch, instead of loading them from a file. Unfortunately, SDL_BlitSurface() seems to render all colors as black when used with the surface generated through SDL_CreateRGBSurface(). This is my code:
int main(int argc, char** argv)
{
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE);
SDL_Surface* layer = SDL_CreateRGBSurface(SDL_HWSURFACE, 100, 100,
screen->format->BitsPerPixel,
screen->format->Rmask,
screen->format->Gmask,
screen->format->Bmask,
screen->format->Amask
);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = 100;
rect.h = 100;
Uint32 blue = SDL_MapRGB(screen->format, 0, 0, 255);
SDL_FillRect(layer, &rect, blue);
SDL_BlitSurface(screen, NULL, layer, NULL);
SDL_Flip(screen);
SDL_Delay(3000);
return 0;
}
What I get is a black screen, instead of a 100x100 blue rectangle. What I could find by Googling doesn't seem to help me, as those questions either apply to 8bit surfaces (and setting palettes — my bpp is 32 here) or are left unanswered.
So, I would like to know how should I properly blit a generated surface onto a SDL screen.
Edit: I see it was an error in the parameter ordering. The line in question should read
SDL_BlitSurface(layer, NULL, screen, NULL);
Still, I am having trouble to achieve the same effect in my more complex C++ program. I will post the relevant parts of the code here:
main.cpp:
int main(int argc, char** argv)
{
SDLScreen screen(1024, 700, "Hello, SDL!");
SDL_Event event;
SDLMenu menu;
bool shouldQuit = false;
menu.setBounds(200, 100, 200, 600);
menu.setFontName("NK211.otf");
menu.setFontSize(36);
menu.setEffect(sdlteShadowText);
menu.addItem("New game");
menu.addItem("Load game");
menu.addItem("Save game");
menu.addItem("Exit");
menu.render();
while (!shouldQuit)
{
menu.draw(screen.getSurface());
SDL_Flip(screen.getSurface());
SDL_Delay(10);
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
shouldQuit = true;
}
else if (event.type == SDL_KEYUP)
{
if (event.key.keysym.sym == SDLK_q)
{
shouldQuit = true;
}
}
}
}
}
SDLMenu.cpp:
void
SDLMenu::setSelectionColorRGB(int r, int g, int b)
{
SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
selectionColor = SDL_MapRGB(info->vfmt, r, g, b);
}
void
SDLMenu::render()
{
SDLText* current = NULL;
SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
if (!items->empty())
{
current = getItemAt(currentItem);
selectionRect = getItemRect(current);
setSelectionColorRGB(0,0,255);
selectionCanvas = SDL_CreateRGBSurface(SDL_HWSURFACE,
selectionRect->w, selectionRect->h,
info->vfmt->BitsPerPixel,
info->vfmt->Rmask,
info->vfmt->Gmask,
info->vfmt->Bmask,
info->vfmt->Amask);
SDL_FillRect(selectionCanvas, selectionRect, selectionColor);
SDL_SaveBMP(selectionCanvas, "selection.bmp"); // debug
}
for (list<SDLText*>::iterator i = items->begin();
i != items->end(); i++)
{
(*i)->render();
}
}
void
SDLMenu::draw(SDL_Surface* canvas)
{
int currentY = bounds.y;
if (selectionCanvas != NULL)
{
SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);
}
for (list<SDLText*>::iterator i = items->begin();
i != items->end(); i++)
{
(*i)->draw(bounds.x, currentY, canvas);
currentY += fontSize + itemGap;
}
}
SDLScreen.cpp:
SDLScreen::SDLScreen(int w, int h, string t, int d)
: width(w), height(h), depth(d), title(t)
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_WM_SetCaption(title.c_str(), NULL);
refresh();
}
void
SDLScreen::refresh()
{
screen = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE);
}
The selection rectangle for the active menu item should be blue, but it shows up in black. The file selection.bmp is also all black.
Edit 2: I found out what created the problem. The selectionRect was set relative to the screen, while the selectionCanvas had the width and height of a particular menu item. So, the filling was done out of bounds of the selectionCanvas. Adding separate SDL_Rect for filling solved the problem.
SDL_Rect fillRect;
fillRect.x = 0;
fillRect.y = 0;
fillRect.w = selectionRect->w;
fillRect.h = selectionRect->h;
SDL_FillRect(selectionCanvas, &fillRect, selectionColor);
// and later...
SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);
You inverted source and destination. To blit on screen, it should be
SDL_BlitSurface(layer, NULL, screen, NULL);
doc for SDL_BlitSurface