Why isn't my image displaying using SDL2? - c

I just started coding a game using SDL2 in C, but I quickly ran into a problem : my images are not displayed on the window. I already made the following checks:
the window and the renderer are successfully created
my .bmp files are correctly loaded
the SDL_RenderCopy() function is called, and returns no error
the SDL_RenderPresent() function is called and returns no error
I'm not trying to display my images on a 0x0px surface
my image is a .bmp file
Here is some code, I hope this will help (I'm sorry it's quite disorganized):
Edit: I simplified the showed code
TrapAdventures.c (main file):
#include <SDL2/SDL.h>
#include <stdio.h>
#include "HobbesSDL.h"
#include "TrapInit.h"
#include "TrapKeyboard.h"
#define Ellipsis (return 0)
#define Pass ((void)0)
#define NullKeycode ((SDL_Keycode)0)
int main(void){
HobbesBool TrapRunning = HOBBES_TRUE;
HobbesWindow *TrapWindow;
HobbesRenderer *TrapRenderer;
if (TrapInitContext("Trap adventures", &TrapWindow, &TrapRenderer) != 0){
TrapRunning = HOBBES_FALSE;
}
HobbesSprite *TrapPlayer;
if (TrapInitSprite(&TrapPlayer, TrapRenderer) != 0)
TrapRunning = HOBBES_FALSE;
while (TrapRunning){
if (TrapGetKeyState(TrapKeySpace))//Quit program if spacebar is pressed
TrapRunning = HOBBES_FALSE;
SDL_RenderClear(TrapRenderer);
if (HobbesDisplaySprite(TrapPlayer) != 0){
printf("Error while displaying player!\n");
TrapRunning = HOBBES_FALSE;
}
SDL_RenderPresent(TrapRenderer);
SDL_Delay(50);
}
HobbesDestroySprite(TrapPlayer);
SDL_DestroyRenderer(TrapRenderer);
SDL_DestroyWindow(TrapWindow);
HobbesQuit();
printf("End!\n");
return 0;
}
The HobbesSprite type defined in HobbesSDL.h (my own (and small) library, using SDL2):
typedef struct HobbesSprite {
SDL_Renderer *renderer;
SDL_Surface *surface;
SDL_Texture *texture;
HobbesRect *rect;
HobbesHitbox *hitbox;
HobbesBool hidden;
} HobbesSprite;
Some of the functions in HobbesSDL.c that I use in this code:
#include <SDL2/SDL.h>
#include "HobbesSDL.h"
#include <errno.h>
int HobbesInit(void){
int ret = SDL_Init(SDL_INIT_VIDEO);
if (ret != 0)
fprintf(stderr, "Error while initializing SDL2: %s", SDL_GetError());
return ret;
}
int HobbesDisplaySprite(HobbesSprite *sprite){
if (SDL_RenderCopy(sprite->renderer, sprite->texture, NULL, (SDL_Rect *)sprite->rect) != 0 && !sprite->hidden){
fprintf(stderr, "Error while displaying sprite: %s\n", SDL_GetError());
fclose(stderr);
return -1;
}
return 0;
}
void HobbesUpdateWindow(HobbesRenderer *renderer){
SDL_RenderPresent(renderer);
}
TrapInit.c:
#include <SDL2/SDL.h>
#include "HobbesSDL.h"
#include "TrapInit.h"
/*These first four functions will be used to initialize the rendering context*/
int TrapInitSDL(){
return HobbesInit();
}
HobbesWindow *TrapInitWindow(const char name[]){
SDL_Window *window = SDL_CreateWindow(name, 0, 0, 1000, 1000, 0);
if (window == NULL){
fprintf(stderr, "Error while creating window: %s\n", SDL_GetError());
}
return window;
}
HobbesRenderer *TrapInitRenderer(HobbesWindow *window){
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
if (renderer == NULL){
fprintf(stderr, "Error while creating renderer: %s\n", SDL_GetError());
}
return renderer;
}
int TrapInitContext(const char name[], HobbesWindow **wptr, HobbesRenderer **rptr){
//Initializes the rendering context using the above functions
int InitSDLResult = TrapInitSDL();
*wptr = TrapInitWindow(name);
*rptr = TrapInitRenderer(*wptr);
if (InitSDLResult != 0 || *wptr == NULL || *rptr == NULL)
return 1;
return 0;
}
/*The following function will be used to create every sprite needed in the game*/
int TrapInitSprite(HobbesSprite **Player, HobbesRenderer *renderer){
*Player = HobbesCreateSprite(renderer, "Images/Player.bmp", 0, 724);
if (*Player == NULL)
return 1;
return 0;
}
(I'm sorry that my code is so unclear, but I do my best :) )
My OS is MacOS Big Sur 11.6.4, my graphic card Intel Iris Pro Graphics 6200 1536 Mo, my processor is a 3,3 GHz Intel Core i7 four cores.
Finally, even thought I use the gcc command to compile, my compiler is clang (Apple made the choice to use clang by default, but the command is still gcc…).
If you need any other information, just ask in the comments and I'll answer you the best I can.
Do you have any ideas?

I did a lot of tests, and finally found the problem.
The problem was that I tried to convert a HobbesRect * to a SDL_Rect *, but I did it wrong. If you look at the code of HobbesDisplaySprite, I do something like this:
SDL_RenderCopy(sprite->renderer, sprite->texture, NULL, (SDL_Rect *)sprite->rect) //(SDL_Rect *)sprite->rect is the problem
I solved my problem using a far more explicit conversion:
SDL_RenderCopy(sprite->renderer, sprite->texture, NULL, &((SDL_Rect){sprite->rect->x, sprite->rect->y, sprite->rect->w, sprite->rect->h}))
Thank you all for your interest in my problem! I'll try to put less code in my question next time ;) !

Related

SDL_Window does not display after build/compilation, Ubuntu 20.04

Currently using SDL2, and attempting to output a basic SDL Window (Ubuntu 20.04) with the C programming language. Program runs, window does not display.
Would like to preface that I've gotten the SDL_Window to show up once before, but (possibly unrelated) after the successful integration of SDL2_gfx, there seems to be issues with the window displaying.
I've run the code both on the Eclipse IDE and in the main linux command line, same error persists, with both a clean build, and a successful compilation, but the window does not show on the screen.
See code below.
#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
int main(int argc, char **argv) {
SDL_Window *window;
if(SDL_Init(SDL_INIT_VIDEO) < 0){
printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
return 1;
}
window = SDL_CreateWindow("SDL Example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_ALLOW_HIGHDPI);
if(window == NULL){
printf("SDL window failed to initialize: %s\n", SDL_GetError());
return 1;
}
SDL_Event e;
int quit = 0;
while(quit == 0){
while(SDL_PollEvent(&e)){
if(e.type == SDL_QUIT){
quit = 1;
}
if(e.type == SDL_KEYDOWN){
quit = 1;
}
if(e.type == SDL_MOUSEBUTTONDOWN){
quit = 1;
}
}
}
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
Few points/disclaimers: This question has been asked by MacOS users, some suggestions include putting in
if(SDL_Init(SDL_INIT_EVERYTHING) < 0)...
vs
if(SDL_Init(SDL_INIT_VIDEO) < 0)....
This has been attempted but I do not have audio support configured for SDL
Also....
SDL_WINDOW_ALLOW_HIGHDPI
Has been changed to various flags, and this has been unsuccessful.
Any advice/suggestions/solutions?

Should I load a DLL in the source code or just its header file? I want to be able to build the same source on linux

I'm trying to figure out a way to store all my dependent libraries in a sub folder, say /libs, and my executable outside of this folder. From what I can tell, I need to then add this library folder into the system PATH or add some code such as HINSTANCE hDLL = LoadLibrary(".\\libs\\mydll.DLL"); into my source (correct me if I'm wrong here, I'm not very sure about this).
However, the problem is, I also would like to be able to compile the same source code on Linux, without having to modify it every time I change from one OS to another. Is this possible? How can I do that in C? If possible, can you guy show me the most standard way to do it in the industry?
I know having all my DLLs outside together with my executable will solve this problem instantly, but that's just avoiding the problem altogether and I'm interested to learn more about this topic.
For example, here I have a small C code that help me initialize some SDL2 facilities:
#include <stdio.h>
#include <stdlib.h>
#include "SDL2\SDL.h"
#include "SDL2\SDL_messagebox.h"
#include "SDL2\SDL_image.h"
#include "SDL2\SDL_ttf.h"
#define MAX_KEYBOARD_KEYS 350
#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 720
typedef struct {
SDL_Renderer *renderer;
SDL_Window *window;
int keyboard[MAX_KEYBOARD_KEYS];
} App;
App app;
void initSDL() {
/*
Procedure to initiate SDL2, SDL_Image, and SDL_ttf
Log errors and quit the program if failed.
*/
int rendererFlags, windowFlags, imgFlags;
rendererFlags = SDL_RENDERER_ACCELERATED;
windowFlags = 0;
imgFlags = IMG_INIT_PNG | IMG_INIT_JPG;
//initiate SDL stuff. If return < 0 error occured
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
printf("Failed to initate SDL: %s", SDL_GetError());
exit(1);
}
//create a window
app.window = SDL_CreateWindow("pong", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, windowFlags);
if (!app.window) {
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Failed to create window: %s", SDL_GetError());
exit(1);
}
//set hint for SDL
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
//create a renderer
app.renderer = SDL_CreateRenderer(app.window, -1, rendererFlags);
if (!app.renderer) {
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Failed to create renderer: %s", SDL_GetError());
exit(1);
}
//init IMG
if (IMG_Init(imgFlags) == 0) {
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Failed to initialize SDL_Image: %s", SDL_GetError());
exit(1);
}
//init TTF
if (TTF_Init() == -1) {
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, "Failed to initialize SDL_ttf: %s", SDL_GetError());
exit(1);
}
}
void cleanUp() {
/*
Procedure to quit all facilities gracefully and announce a normal exit of the program
*/
SDL_DestroyRenderer(app.renderer);
SDL_DestroyWindow(app.window);
IMG_Quit();
TTF_Quit();
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Exited normally");
SDL_Quit();
}
int main(int argc, char* argv[]) {
/*
Possibly here we will have that HINSTANCE hDLL = LoadLibrary(".\\libs\\mydll.DLL");
to deal with all the DLLs I have in the libs folder
*/
initSDL();
atexit(cleanUp);
exit(0);
}
Now fortunately this code can be compiled both on Windows (mingw32) and Linux if I so choose myself. What I'm worrying about is that, after adding Windows-specific codes, my source will lose such liberty.

Error running Allegro on OSX

I ran brew install allegro on OSX
following this tutorial: https://wiki.allegro.cc/index.php?title=Example_ExHello
my code
include <allegro.h>
int main(void) {
if (allegro_init() != 0)
return 1;
/* set up the keyboard handler */
install_keyboard();
/* set a graphics mode sized 320x200 */
if (set_gfx_mode(GFX_AUTODETECT, 320, 200, 0, 0) != 0) {
if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
return 1;
}
}
/* set the color palette */
set_palette(desktop_palette);
/* clear the screen to white */
clear_to_color(screen, makecol(255, 255, 255));
/* you don't need to do this, but on some platforms (eg. Windows) things
* will be drawn more quickly if you always acquire the screen before
* trying to draw onto it.
*/
acquire_screen();
/* write some text to the screen with black letters and transparent background */
textout_centre_ex(screen, font, "Hello, world!", SCREEN_W/2, SCREEN_H/2, makecol(0,0,0), -1);
/* you must always release bitmaps before calling any input functions */
release_screen();
/* wait for a keypress */
readkey();
return 0;
}
1.c:1:1: error: unknown type name 'include'
include <allegro.h>
^
1.c:1:9: error: expected identifier or '('
include <allegro.h>
^
2 errors generated.
make: *** [1] Error 1
Presuming that the typo of doing include <allegro.h> was supposed to be #include <allegro.h>, You've installed allegro5 - the API is very different between allegro4 (which this example is from) and allegro5. The display initialization sample program for allegro5 shows some of the differences:
#include <stdio.h>
#include <allegro5/allegro.h>
int main(int argc, char **argv){
ALLEGRO_DISPLAY *display = NULL;
if(!al_init()) { // allegro_init in allegro4
fprintf(stderr, "failed to initialize allegro!\n");
return -1;
}
display = al_create_display(640, 480); // very different to allegro4
if(!display) {
fprintf(stderr, "failed to create display!\n");
return -1;
}
al_clear_to_color(al_map_rgb(0,0,0)); // makecol -> al_map_rgb, clear_to_color -> al_clear_to_color
al_flip_display();
al_rest(10.0);
al_destroy_display(display);
return 0;
}
I built it using:
c++ -I/usr/local/include allegro_display.cc -o allegro_display -L/usr/local/lib -lallegro -lallegro_main
where the code was in the file allegro_display.cc. Note that I compile using the C++ compiler, because allegro is really a C++ api (and the sample does not work when compiled as C code because there is no proper calling convention for structs in C, whereas there is for C++)

Use root X11 window as the main SDL2 window

I'm trying to make SDL2 use the root X window to display things, but it doesn't seem to work - the window doesn't get changed in any way. Also, the whole program doesn't quit after the SDL_Delay() for some reason. Is it not possible? Am I doing something wrong?
#include <SDL.h>
#include <X11/Xlib.h>
#include <stdio.h>
// clang -lSDL2 -lX11 -I/usr/include/SDL2 -Weverything x11.c -o x11
int main(void)
{
Display *x11_d;
int x11_s;
Window x11_w;
SDL_Window *w;
SDL_Renderer *r;
x11_d = XOpenDisplay(NULL);
if(!x11_d) {
fprintf(stderr, "couldn't open display\n");
return 1;
}
x11_s = DefaultScreen(x11_d);
x11_w = RootWindow(x11_d, x11_s);
if(SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "couldn't initialize SDL: %s\n", SDL_GetError());
return 1;
}
w = SDL_CreateWindowFrom((void *)x11_w);
XCloseDisplay(x11_d);
if(!w) {
fprintf(stderr, "couldn't attach to the root X11 window: %s\n", SDL_GetError());
return 1;
}
r = SDL_CreateRenderer(w, -1, 0);
SDL_SetRenderDrawColor(r, 255, 0, 0, 255);
SDL_RenderClear(r);
SDL_RenderPresent(r);
SDL_Delay(5700);
SDL_Quit();
return 0;
}
You're closing the X Display right after you create the SDL window, so you lose the connection. That obviously isn't helping but you also left out about 95% of the code required to get X working. Tutorial here.

How can i get screen resolution in c (Operating system QNX or Linux)

I am in a GUI development with QNX(screen resolution interdependent design)
How can i get screen resolution in c.
I am using QNX operating system.
Is it possible?
Is any OS function for this solution?
thanks
Assume you are using a device with a framebuffer (and have root access):
(taken from this answer: Paint Pixels to Screen via Linux FrameBuffer)
Also, as mentioned above, what graphics library you are using will make a lot of difference as this code will only tell you what the framebuffer is set to and not what the GUI code is using. So might not be useful at all. If you are not using X or any other graphics library, then you will probably need to be using the framebuffer, and you can see the rest of the answer for how to do that. (I strongly suggest you use DirectFB this will save you implementing a LOT of code).
Also, you could also use the gl drivers that turn up on most devices (inc. embedded ones) so this will also effect how you do what you require.
Are you using a SOC? Does the manufacturer have there own driver layer? That may work completely different and would probably come with it's own API to handle this.
But anyway, I hope this helps.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
int main()
{
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0;
int x = 0, y = 0;
long int location = 0;
// Open the file for reading and writing
fbfd = open("/dev/fb0", O_RDWR);
if (fbfd == -1) {
perror("Error: cannot open framebuffer device");
exit(1);
}
printf("The framebuffer device was opened successfully.\n");
// Get fixed screen information
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
perror("Error reading fixed information");
exit(2);
}
// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
perror("Error reading variable information");
exit(3);
}
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
// Figure out the size of the screen in bytes
//
close(fbfd);
}
For a unix-like OS, you may use the library X11, but if you need cross-platform solution, try the GTK+.
A full code
// The C standart library
#include <stdlib.h>
// GTK+
#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gprintf.h>
// X11
#include <X11/Xlib.h>
/*
Printing a current screen resoltion by using the GTK+3
https://en.wikipedia.org/wiki/GTK%2B
*/
int
print_screen_resolution_by_GTK(int argc, char *argv[])
{
GdkScreen *screen;
gint width, height;
gtk_init(&argc, &argv);
if ((screen = gdk_screen_get_default()) != NULL) {
width = gdk_screen_get_width(screen);
height = gdk_screen_get_height(screen);
g_printf("Current screen resolution: %dx%d (by used GTK+)\n", width, height);
}
return 0;
}
/*
Printing a current screen resoltion by using the libX11 (worked only for Unix-like OS)
https://en.wikipedia.org/wiki/X_Window_System
Based on:
https://www.x.org/releases/X11R7.6/doc/libX11/specs/libX11/libX11.html
http://surfingtroves.blogspot.com/2011/01/how-to-get-screen-resolution-in-linux-c.html
*/
int
print_display_resolution_by_X11()
{
Display *display;
Window window;
XWindowAttributes xw_attrs;
if ((display = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "Failed to open default display\n");
return -1;
}
window = DefaultRootWindow(display);
XGetWindowAttributes(display, window, &xw_attrs);
printf("Current window resolution: %dx%d (by used X11)\n", xw_attrs.width, xw_attrs.height);
XCloseDisplay(display);
return 0;
}
int main(int argc, char *argv[])
{
print_screen_resolution_by_GTK(argc, argv);
print_display_resolution_by_X11();
return EXIT_SUCCESS;
}
A compilation
gcc -o main main.c `pkg-config --libs --cflags gtk+-3.0 x11`
A result (actual for my computer)
Current screen resolution: 1366x768 (by used GTK+)
Current window resolution: 1366x768 (by used X11)
You can simply use this function I created, it get screen size from your configuration files, split it, and then return 2 values (resolution as x and y)
I tried it on Ubuntu 20.04 and it works perfectly !
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
unsigned short *get_screen_size(void)
{
static unsigned short size[2];
char *array[8];
char screen_size[64];
char* token = NULL;
FILE *cmd = popen("xdpyinfo | awk '/dimensions/ {print $2}'", "r");
if (!cmd)
return 0;
while (fgets(screen_size, sizeof(screen_size), cmd) != NULL);
pclose(cmd);
token = strtok(screen_size, "x\n");
if (!token)
return 0;
for (unsigned short i = 0; token != NULL; ++i) {
array[i] = token;
token = strtok(NULL, "x\n");
}
size[0] = atoi(array[0]);
size[1] = atoi(array[1]);
size[2] = -1;
return size;
}
int main(void)
{
unsigned short *size = get_screen_size();
printf("Screen resolution = %dx%d\n", size[0], size[1]);
return 0;
}
If you have any question, do not hesitate ! :)

Resources