I'm trying to make an OpenGL program with as many windows as I want. I know it's possible. I don't care if I have to use something other than SDL2, but I've tried GLFW3 and get the same results.
The problem is not that I can't create multiple windows or can't render to both of them. I can. The problem is no matter how I try to do it, whether I use a single thread or multiple, the frame rate is locked to a divisor of my monitor's refresh rate. What I mean is, if I have two windows rendering on the main thread and updated using SDL_GL_SwapWindow(window) I get 72fps for both of them (My monitor has a refresh rate of 144hz and 144/2=72hz). This is expected for a single thread because of v-sync. Each call to SDL_GL_SwapWindow is syncing the window to the refresh rate and 1/144 + 1/144 is 1/72. That's not the problem. The problem is I have tried using separate threads for each window and even then it runs at 72fps even though they should be rendering/swapping the buffers at the same time.
Example 1:
In this example, I have both windows rendering and swapping buffers on the main thread. This does what you would expect on a monitor with a refresh rate of 144hz. They render at 72fps.
#include <iostream>
#include <SDL2/SDL.h>
#undef main
#include <glad/glad.h>
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* w1 = SDL_CreateWindow("w1", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL);
SDL_Window* w2 = SDL_CreateWindow("w2", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 700, SDL_WINDOW_OPENGL);
SDL_GLContext c1 = SDL_GL_CreateContext(w1);
SDL_GLContext c2 = SDL_GL_CreateContext(w2);
SDL_GL_MakeCurrent(w1, c1);
gladLoadGL();
int r = 0, b = 0;
bool running = true;
while (running) {
uint64_t startTime = SDL_GetPerformanceCounter();
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = false;
} else if (event.type == SDL_WINDOWEVENT) {
if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
SDL_GL_DeleteContext(c2);
SDL_DestroyWindow(w2);
}
}
}
SDL_GL_MakeCurrent(w1, c1);
glClearColor(r / 255.0f, 0, 0, 1);
r = (r + 1) % 256;
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(w1);
SDL_GL_MakeCurrent(w2, c2);
glClearColor(0, 0, b / 255.0f, 1);
b = (b + 1) % 256;
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(w2);
double deltaTime = (SDL_GetPerformanceCounter() - startTime) / (double)SDL_GetPerformanceFrequency();
std::cout << 1.0 / deltaTime << " fps" << std::endl;
}
SDL_GL_DeleteContext(c1);
SDL_DestroyWindow(w1);
SDL_Quit();
return 0;
}
Example 2:
In this example, I have both windows rendering and swapping buffers on their own threads. This doesn't do what I expect because it behaves the exact same as having them both on the same thread like in example 1.
#include <iostream>
#include <thread>
#include <SDL2/SDL.h>
#undef main
#include <glad/glad.h>
void RenderThread(bool& running, SDL_Window* window) {
SDL_GLContext context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, context);
gladLoadGL();
int r = 0;
while (running) {
uint64_t startTime = SDL_GetPerformanceCounter();
glClearColor(r / 255.0f, 0, 0, 1);
r = (r + 1) % 256;
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(window);
double deltaTime = (SDL_GetPerformanceCounter() - startTime) / (double)SDL_GetPerformanceFrequency();
std::cout << 1.0 / deltaTime << " fps" << std::endl;
}
SDL_GL_DeleteContext(context);
}
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* w1 = SDL_CreateWindow("w1", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL);
SDL_Window* w2 = SDL_CreateWindow("w2", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 700, SDL_WINDOW_OPENGL);
bool running = true, r1 = true, r2 = true;
std::thread t1(RenderThread, std::ref(r1), w1);
std::thread t2(RenderThread, std::ref(r2), w2);
while (running) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = false;
r1 = false;
t1.join();
} else if (event.type == SDL_WINDOWEVENT) {
if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
r2 = false;
t2.join();
SDL_DestroyWindow(w2);
}
}
}
}
SDL_DestroyWindow(w1);
SDL_Quit();
return 0;
}
I'm fine with using another library for the windows. If my problem can be fixed with a different library please tell me which one.
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.
In the event handling loop of SDL2, calling the method SDL_GetMouseState(&x,&y); or using event.motion.x and event.motion.y for the relative mouse coordinates makes SDL2 responsiveness VERY SLUGGISH. Whats weird is SDL_GetMouseState()is alot faster than event.motion.x and y, however they are both unbearably bad. Is there any other way of getting the mouse pos? You can even try this. I setup a simple text program in SDL2 to test something responsive such as scrolling, where you offset the y values. Try with and without vsync and with and without getting the mouse pos this way. I am currently using linux mint.
Code: (You will need arial.ttf in your folder where the project is if using codeblocks like i am)
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_image.h>
#include <string>
using std::string;
using std::to_string;
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
SDL_Window *window = SDL_CreateWindow("Test Program", 0, 30, 1280, 720, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
SDL_Event event;
SDL_Point mousePos = {0,0};
///Fps vars
int fpsCounter, fpsStart, fpsEnd;
fpsStart = SDL_GetTicks();
fpsEnd = SDL_GetTicks();
fpsCounter = 0;
TTF_Font *fpsFont = TTF_OpenFont("arial.ttf", 30);
SDL_Surface *fpsSurface = TTF_RenderText_Blended(fpsFont, "FPS: ", {0,0,0});
SDL_Texture *fpsTexture = SDL_CreateTextureFromSurface(renderer, fpsSurface);
SDL_FreeSurface(fpsSurface);
int textW, textH, yVal;
yVal = 50;
SDL_QueryTexture(fpsTexture, NULL, NULL, &textW, &textH);
SDL_Rect fpsRect = {1000, yVal, textW, textH};
bool running = true;
while (running)
{
while ( SDL_PollEvent(&event) )
{
if (event.type == SDL_QUIT)
{
running = false;
break;
}
else if (event.type == SDL_MOUSEMOTION){
int x,y;
SDL_GetMouseState(&x,&y);
mousePos = {x,y};
break;
}
else if (event.type == SDL_MOUSEWHEEL){
if (event.wheel.y > 0){ ///Scrolling up here
yVal -= 50;
fpsRect.y = yVal;
break;
}
if (event.wheel.y < 0){ ///Scrolling down here
yVal += 50;
fpsRect.y = yVal;
break;
}
}
}
SDL_SetRenderDrawColor(renderer, 255,255,255,255);
SDL_RenderClear(renderer);
//Update every 0.5s (500ms)
fpsEnd = SDL_GetTicks();
fpsCounter += 2;
if ( (fpsEnd-fpsStart) > 500 ){
fpsStart = SDL_GetTicks();
SDL_DestroyTexture(fpsTexture);
///Change text
string newText = ("FPS: " + to_string(fpsCounter));
fpsSurface = TTF_RenderText_Blended(fpsFont, newText.c_str(), {0,0,0});
fpsTexture = SDL_CreateTextureFromSurface(renderer, fpsSurface);
SDL_FreeSurface(fpsSurface);
SDL_QueryTexture(fpsTexture, NULL, NULL, &textW, &textH);
fpsRect = {1000, yVal, textW, textH};
fpsCounter = 0;
}
SDL_RenderCopy(renderer, fpsTexture, NULL, &fpsRect);
SDL_RenderPresent(renderer);
}
SDL_DestroyTexture(fpsTexture);
TTF_CloseFont(fpsFont);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
TTF_Quit();
SDL_Quit();
return 0;
}
I don't agree that turning off VSYNC is a solution. It shouldn't be like this with VSYNC on regardless. It isn't like this in windows for some reason, it works fine there. Capping the FPS with SDL_Delay() to the refresh rate of the display also gives me the same problem.
The problem is that you "break" every time you get an event. So, your event queue is full all the time as you only process one event per draw.
Replace "break" with "continue" and process all events.
I know this question has been asked before but most of the time answer was just to add delay or an event loop. However I have added an event loop and the window ist not showing. Only the console. I am running this program in Visual Studio 2019.
#include <iostream>
#include "GL/glew.h"
#define SDL_MAIN_HANDLED
#include "SDL.h"
int main() {
SDL_Window* window;
SDL_Init(SDL_INIT_EVERYTHING);
//fenster erstellen
window = SDL_CreateWindow("C++ OpenGL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_OPENGL);
//opengl context setzen
SDL_GLContext glContext = SDL_GL_CreateContext(window);
bool close = false;
while (!close) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
close = true;
}
}
if (close) {
break;
}
}
return 0;
}
You need to include SDL_MainReady as you are not using SDL_main.
See here
So you code would be adjusted like
int main() {
SDL_Window* window;
SDL_SetMainReady();
SDL_Init(SDL_INIT_EVERYTHING);
...
return 0;
}
In my project, I have a decreasing timer progress bar PNG image on display. This image and its background can be found at the below link:
https://s3.eksiup.com/df7dd38f781.png
My goal is to make the bar decrease starting from the upper green side to the lower red side. My code for this is the following:
void EntityTimer::OnRender(SDL_Renderer *ren)
{
SDL_Rect currRect,dstRect;
double rem=0.0;
memcpy(&currRect,&origRect,sizeof (SDL_Rect));
memcpy(&dstRect,&origRect,sizeof (SDL_Rect));
if (timerObjPtr->remainingDuration>timerObjPtr->durationInMilisec)
timerObjPtr->durationInMilisec=timerObjPtr->remainingDuration;
rem=((double)timerObjPtr->remainingDuration/timerObjPtr->durationInMilisec);
currRect.h=(origRect.h)*rem;
currRect.h = currRect.h;
dstRect.x=0;
dstRect.y=0;
dstRect.h=currRect.h;
SDL_RenderCopy(ren,timerTexture,&dstRect,&currRect);
}
Since this image is static, I tried to do it by manipulating this texture's height parameter with "currRect.h=(origRect.h)*rem;" line. But this makes the progress bar to decrease from the red side to the green side (the opposite of what I want).
I tried to correct it but made it worse by mirroring the PNG image on the progress bar area and decreasing it from bottom to top again.
Any help is appreciated so that the bar decreases from top (green) to bottom (red).
Well that's kinda expected since you're drawing from the same point (x = 0, y = 0) but using a smaller size. You need to update the destine y position:
dstRect.y = origRect.h - origRect.h * rem;
EDIT: It was the other way around
dstRect.y = origRect.h * rem
(assuming rem goes from 0 to 1)
Here is an example of what you want, you can use this image to test https://i.imgur.com/faKKShU.png
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include <thread>
#define HEIGHT 600
#define WIDTH 800
using namespace std;
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("Red", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
bool quit = false;
SDL_Event event;
SDL_Texture *red_part = IMG_LoadTexture(renderer, "red_image.png");
float multiplier = 0;
while (!quit) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
}
int y_pos = HEIGHT * multiplier;
SDL_RenderClear(renderer);
SDL_Rect copy_rect{0, y_pos, 800, 600};
SDL_RenderCopy(renderer, red_part, nullptr, ©_rect);
SDL_RenderPresent(renderer);
multiplier += 0.01;
if (multiplier >= 1.)
multiplier = 1.;
std::this_thread::sleep_for(std::chrono::milliseconds{33});
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}
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?