I feel like I'm misunderstanding a big concept here in c++
(Can't buy any books.)
I couldn't find anything else on this, sorry if something hidden in an obscure part of the internet turns up.
I've been trying to work with SDL lately when I found a new interesting way of creating an application wrapper for sdl. But when separating it into a separate cpp and header file... stuff happens.
C++:
//
// AppSdl.cpp
// sdlgaim
//
// Created by Home on 28/4/14.
// Copyright (c) 2014 hyperum. All rights reserved.
//
#include "AppSdl.h"
app_sdl::app_sdl() :
_running(false)
{
}
app_sdl::~app_sdl()
{
destroy();
}
int app_sdl::init(int width, int height, const char *title)
{
// Initialize the SDL library.
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
{
fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
return APP_FAILED;
}
win = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
// Success.
return APP_OK;
}
void app_sdl::destroy()
{
if (win)
{
SDL_DestroyWindow(win);
SDL_DestroyRenderer(renderer);
SDL_Quit();
}
}
int app_sdl::run(int width, int height, const char *title)
{
// Initialize application.
int state = init(width, height, title);
if (state != APP_OK) return state;
// Enter to the SDL event loop.
SDL_Event ev;
_running = true;
while (SDL_WaitEvent(&ev))
{
onEvent(&ev);
Render();
if (_running == false)
{
break;
}
}
// Success.
return APP_OK;
}
void app_sdl::onEvent(SDL_Event* ev)
{
switch (ev->type)
{
case SDL_QUIT:
_running = false;
break;
case SDL_KEYDOWN:
{
switch (ev->key.keysym.sym)
{
case SDLK_ESCAPE:
_running = false;
break;
}
}
}
}
void app_sdl::Render()
{
SDL_Rect r;
int w,h;
SDL_GetWindowSize(win, &w, &h);
r.w = 200;
r.h = 200;
r.x = w/2-(r.w/2);
r.y = h/2-(r.h/2);
//
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0, 0xff);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0, 0xff);
SDL_RenderFillRect(renderer, &r);
SDL_RenderPresent(renderer);
}
Header:
//
// AppSdl.h
// sdlgaim
//
// Created by Home on 28/4/14.
// Copyright (c) 2014 hyperum. All rights reserved.
//
#ifndef __sdlgaim__AppSdl__
#define __sdlgaim__AppSdl__
#include <iostream>
#include <SDL2/SDL.h>
struct app_sdl
{
app_sdl();
~app_sdl();
// Application state (just convenience instead of 0, 1, ...).
enum APP_STATE
{
APP_OK = 0,
APP_FAILED = 1
};
// Destroy application, called by destructor, don't call manually.
void destroy();
int init(int width, int height, const char *title);
// Run application, called by your code.
int run(int width, int height, const char *title);
// Called to process SDL event.
void onEvent(SDL_Event* ev);
// Called to render content into buffer.
void Render();
// Whether the application is in event loop.
bool _running;
SDL_Window *win;
SDL_Renderer *renderer;
};
#endif /* defined(__sdlgaim__AppSdl__) */
Now, you see, when I include the cpp file and do this:
app_sdl app;
return app.run(640, 480, APPTITLE); in my main integer, everything runs fine. But when I include the HEADER file instead, this happens:
Undefined symbols for architecture x86_64:
"app_sdl::run(int, int, char const*)", referenced from:
_main in main.o
"app_sdl::app_sdl()", referenced from:
_main in main.o
"app_sdl::~app_sdl()", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Anyone knows what's happening here? And what I should do?
As you may know, including a cpp file is not the right thing to do even if it seems to work around this problem.
Error like yours occurs when you forget to add the source file to the compile command.
I've never used xcode, but quick googling resulted in this page which suggests:
examine which symbols are missing
target->build phases->compile source
add the missing source files if they are not listed
command+b to compile the source code again
Related
Just wondering , anyone got that issue:
I'm using Visual Studio Community and after adding class and moving SDL_Init(SDL_INIT_VIDEO) from main function to Someclass.cpp my Window is not appearing anymore. SDL_Init returns 1.
I thought it's just my code related issue but I copied tutor code and that's not working as well in my environment.(his eclipse works as it should) It used to work properly when SDL_Init(SDL_INIT_VIDEO) was called from main.cpp.
I was trying to add additional dependencies in Project/Settings/Linker/Input/Additional Dependencies
but no success:( Maybe I was doing something wrong.
SDL Window still doesn't appear.
This is the function which initialize SDL video:
SDL_Window* Screen::Init(uint32_t w, uint32_t h, uint32_t mag){
if (SDL_Init(SDL_INIT_VIDEO))
{
std::cout << "Error SDL_Init Failed" << SDL_GetError() << std::endl;
return nullptr;
}
mWidth = w;
mHeight = h;
moptrWindow = SDL_CreateWindow("Arcade", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, mWidth * mag, mHeight * mag, 0);
if (moptrWindow)
{
mnoptrWindowSurface = SDL_GetWindowSurface(moptrWindow);
SDL_PixelFormat* pixelFormat = mnoptrWindowSurface->format;
Color::InitColorFormat(pixelFormat);
mClearColor = Color::Black();
mBackBuffer.Init(pixelFormat->format, mWidth, mHeight);
mBackBuffer.Clear(mClearColor);
}
return moptrWindow;
}
The main function from where Init is called:
int main(int argc, const char* argv[]){
Screen theScreen;
theScreen.Init(SCREEN_WIDTH, SCREEN_HEIGHT, MAGNIFICATION);
theScreen.Draw(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, Color::Yellow());
theScreen.SwapScreens();
SDL_Event sdlEvent;
bool running = true;
while (running)
{
while (SDL_PollEvent(&sdlEvent))
{
switch (sdlEvent.type)
{
case SDL_QUIT:
running = false;
break;
}
}
}
return 0;
}
Output is:
1
C:\Users\MyPC\projects\drawing-line\Debug\drawing-line.exe (process 13032) exited with code 0.
Press any key to close this window . . .
If anyone wants to try my code there is link to google drive:
that's whole project
Thank you.
Luke
Eventually I resolved the problem. I changed all settings to X64 and got rid of const in: int main(int argc,const char* argv[]). Now works as it should.Took me while:)
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).
I've been following LazyFoo's SDL tutorials (and also adding my own organization and coding style). When I got to his animation tutorial I decided to make a separate class to store the variables and methods related to the animation algorithm, rather than having global variables. He uses an array of SDL_Rects to define the boundaries of different sprites on a sprite sheet, so I used an SDL_Rect pointer to store the array in my custom class. When I compiled everything I didn't see an animation, when I compiled the original source code I did. When I started debugging things, I found that when I was rendering the sprites, the rects were actually full of garbage, even though when I initialize them the rects are just fine. I've tried to simplify the problem so many times, but every approach I take to recreate the bug in a simpler environment actually works as expected! So with that in mind I apologize for the large amount of code, because I can't seem to reduce the problem.
texture.h
#ifndef TEXTURE_H
#define TEXTURE_H
#include <SDL2/SDL.h>
#include <string>
class Animation {
public:
Animation(SDL_Renderer* renderer);
~Animation();
void load(std::string path, int frames, SDL_Rect* clips),
free(),
render(int x, int y),
next_frame();
private:
SDL_Renderer* _renderer=NULL;
SDL_Rect* _clips=NULL;
SDL_Texture* _texture=NULL;
int _frame=0, _frames=0, _width=0, _height=0;
};
#endif
texture.cpp
#include <stdio.h>
#include <SDL2/SDL_image.h>
#include "texture.h"
#include "error.h"
Animation::Animation(SDL_Renderer* renderer) {
_renderer = renderer;
}
Animation::~Animation() {
free();
_renderer = NULL;
}
void Animation::load(std::string path, int frames, SDL_Rect* clips) {
free();
SDL_Texture* texture = NULL;
SDL_Surface* surface = IMG_Load(path.c_str());
if (!surface)
throw ErrorIMG("Could not load image "+path);
SDL_SetColorKey(surface, SDL_TRUE,
SDL_MapRGB(surface->format, 0, 0xFF, 0xFF));
texture = SDL_CreateTextureFromSurface(_renderer, surface);
if (!texture)
throw ErrorSDL("Could not create texture from image "+path);
_width = surface->w;
_height = surface->h;
SDL_FreeSurface(surface);
_frames = frames;
_clips = clips;
printf("clips[%d]: w: %d h: %d\n", 0, _clips[0].w, _clips[0].h);
}
void Animation::free() {
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = NULL;
_clips = NULL;
_frames = 0;
_frame = 0;
_width = 0;
_height = 0;
}
}
void Animation::render(int x, int y) {
SDL_Rect crect = _clips[_frame/4];
printf("in render (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
SDL_Rect render_space = {x, y, crect.w, crect.h};
SDL_RenderCopy(_renderer, _texture, &_clips[_frame], &render_space);
}
void Animation::next_frame() {
SDL_Rect crect = _clips[_frame/4];
printf("in next frame (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
++_frame;
if (_frame/4 >= _frames)
_frame = 0;
}
game.h
#ifndef GAME_H
#define GAME_H
#include "texture.h"
class Game {
public:
Game();
~Game();
void main();
private:
void load_media();
SDL_Window* _window=NULL;
SDL_Renderer* _renderer=NULL;
Animation* _anim=NULL;
const int SCREEN_WIDTH=640, SCREEN_HEIGHT=480;
};
#endif
game.cpp
#include <SDL2/SDL_image.h>
#include "game.h"
#include "error.h"
void Game::main() {
load_media();
bool has_quit = false;
SDL_Event event;
while (!has_quit) {
while (SDL_PollEvent(&event))
if (event.type == SDL_QUIT)
has_quit = true;
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(_renderer);
_anim->render(100, 100);
_anim->next_frame();
SDL_RenderPresent(_renderer);
}
}
Game::Game() {
if (SDL_Init(SDL_INIT_VIDEO))
throw ErrorSDL("SDL could not initialize");
_window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!_window)
throw ErrorSDL("Window could not be created");
Uint32 render_flags = SDL_RENDERER_ACCELERATED;
render_flags |= SDL_RENDERER_PRESENTVSYNC;
_renderer = SDL_CreateRenderer(_window, -1, render_flags);
if (!_renderer)
throw ErrorSDL("Renderer could not be created");
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
throw ErrorIMG("SDL_image could not initialize");
}
Game::~Game() {
delete _anim;
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
_renderer = NULL;
_window = NULL;
IMG_Quit();
SDL_Quit();
}
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
for (int i=0; i < nclips; i++) {
clips[i].x = i*64;
clips[i].y = 0;
clips[i].w = 64;
clips[i].h = 164;
}
_anim = new Animation(_renderer);
_anim->load("sheet.png", nclips, &clips[0]);
}
You're storing a pointer to a temporary. The SDL_Rect* clips pointer that you pass to Animation::load is assigned to the member _clips used after the function returns. For this to work correctly, the data that is pointed to needs to live for as long as the Animation class is using it. The problem arises here:
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
...
_anim->load("sheet.png", nclips, &clips[0]);
}
In this piece of code, clips is a local variable. That means it gets destroyed at the end of load_media(), and the memory contents at that location will become garbage.
There are a number of ways you could fix this. A simple one would be to use std::vector<SDL_Rect> instead of SDL_Rect*. std::vector can safely be copied and manages its internals for you. Your new code could look like:
class Animation {
...
std::vector<SDL_Rect> _clips;
...
}
void Animation::load(std::string path, int frames, std::vector<SDL_Rect> clips) {
...
_clips = clips;
...
}
void Game::load_media() {
const int nclips = 4;
std::vector<SDL_Rect> clips;
clips.resize(nclips);
...
_anim->load("sheet.png", nclips, clips);
}
And dont forget to #include <vector>. Documentation for std::vector is here. Note that std::vector has a size() method that can probably replace frames everywhere it appears.
The stack-allocated Game::load_media()::clips array disappears when it goes out of scope. Make a copy in Animation::load() instead of only storing a pointer.
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?
I have recently purchased a new laptop and got a new version of VS from my school. And I'm having a little trouble setting up my libraries.
I created a basic SDL_Window, as well as a SDL_GLContext.
I'm able to include my libraries and run the program, but I can't call functions like glClearColor, or glGetString(GL_VERSION). I get a rather strage warning and an error that I have never seen before, I'm guessing it's related to the 2013 version?
I have tried ignoring all specific default libraries, as well as trying to change the programtype (Multithreaded DLL, those 4).
And I have made sure all the dll-files are in place in my system folder.
What makes me wonder is the fact that glewInit() works, but not glClearColor() etc...
Output:
1>MSVCRTD.lib(cinitexe.obj) : warning LNK4098: defaultlib 'msvcrt.lib'
conflicts with use of other libs; use /NODEFAULTLIB:library
1>Window.obj : error LNK2019: unresolved external symbol
__imp__glClearColor#16 referenced in function "public: void __thiscall Window::initOpenGL(void)" (?initOpenGL#Window##QAEXXZ)
1>C:\Users\Aleksander\documents\visual studio 2013\Projects\OpenGL
Test\Debug\OpenGL Test.exe : fatal error LNK1120: 1 unresolved
externals
Header:
#pragma once
#include <SDL.h> //Tested OK
#include <SDL_image.h> //Tested OK
#include <SDL_mixer.h> //Tested OK
#include <SDL_net.h> //Tested OK
#include <OpenGL\glew.h> //?
#include <freeglut\freeglut.h> //?
#include <gl\GL.h> //?
#include <glm\glm.hpp> //Tested OK
#include <iostream> //Standard OK
#include <Box2D\Box2D.h> //Tested OK
using namespace std;
class Window {
SDL_Window *window;
SDL_GLContext context;
bool quit;
public:
Window();
~Window();
void initSDL();
void initOpenGL();
void run();
};
Source:
#include "Window.h"
Window::Window() {
printf("Starting window...\n");
//Initialize window
quit = false;
initSDL();
initOpenGL();
//Run window
run();
}
Window::~Window() {
printf("Closing window...\n");
//Clear window memory
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
//Close window library
SDL_Quit();
}
void Window::initSDL() {
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
quit = true;
printf("Unable to initialize SDL!\n");
}
else {
//Create window
const char *title = "OpenGL Test";
int pos = SDL_WINDOWPOS_CENTERED;
int w = 800;
int h = 600;
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
window = SDL_CreateWindow(title, pos, pos, w, h, flags);
//Create OpenGL context
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
context = SDL_GL_CreateContext(window);
}
}
void Window::initOpenGL() {
GLenum init = glewInit();
if (init != GLEW_OK) {
quit = true;
printf("Unable to initialize OpenGL!\n");
printf("Error: %s!\n", glewGetErrorString(init));
}
else {
//printf("Vendor: %s\n", glGetString(GL_VENDOR));
//printf("Version: %s\n", glGetString(GL_VERSION));
//printf("Renderer: %s\n", glGetString(GL_RENDERER));
}
}
void Window::run() {
printf("Window started running!\n");
SDL_Event event;
while (!quit) {
SDL_GL_SwapWindow(window);
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
}
}
}