I'm using SDL to code a simple game, and i have a big problem - i'm trying to do some animations with function,
in the fuction i call static int which keeps raising every game tick, and dependant on value of static int i change my variable image (with image=SDL_LoadBMP(myfile)), it's working great, but after 10 minutes of running a program, which had been working with 50MB of memory before without this really simple animation, ram usage of my program is starting to get bigger and bigger, and as i said, after 10 minutes it's 3GB and keeps raising every animation occur(so, like every 3 seconds).
Weird thing is also that i have other image which animation is a little bit simplier - i change my image upon clicking any arrow (still in main), and then call function, so after a second it gives back the initial image to a variable(it's giving image back in function), and it's working great - with that i mean - even if i keep clicking arrows, memory usage is constant.
my function looks like that:
void func(obj* image)
{
static int time1;
time1++;
if(time1>1000)
{
time1=0;
SDL_FreeSurface(image->image); //this doesn't change anything
image->image=SDL_LoadBMP("path");
}
else if(time1>800)
image->image=SDL_LoadBMP("path2");
else if(time1>600)
image->image=SDL_LoadBMP("path3");
else if(time1>400)
image->image=SDL_LoadBMP("path4");
}
typedef struct {
SDL_Surface* image;
}obj;
int main()
{
obj struct;
func(&struct);
}
ofc it's fulfilled with all this SDL library calls to make a window etc
https://i.ibb.co/YBcvjnF/Bez-tytu-u.png
If I understand correctly you're making SDL_Surface* over and over again, you never call SDL_FreeSurface()(info).
You need to load a some point all the BMP needed to play the animation into SDL_Surface* then reuse these BMP(s).
In your main (or into an init function) you need to store into an array or pointers the BMP images.
// Somewhere on one of your struct
SDL_Surface *animationImages[4];
// Then in an init function you do
animationImages[0] = SDL_LoadBMP("path");
animationImages[1] = SDL_LoadBMP("path2");
animationImages[2] = SDL_LoadBMP("path3");
animationImages[3] = SDL_LoadBMP("path4");
// And finally
void func(obj* image) {
static int time1;
time1++;
if (time1>1000) {
time1 = 0;
image->image = animationImages[0];
} else if (time1>800) {
image->image = animationImages[1];
} else if (time1>600) {
image->image = animationImages[2];
} else if (time1>400) {
image->image = animationImages[3];
}
}
And before the end of your game or when you don't need these animationImages anymore call SDL_FreeSurface() for each SDL_Surface* you have created.
// In a specific function used to clean up allocated stuff you do
SDL_FreeSurface(animationImages[0]);
SDL_FreeSurface(animationImages[1]);
SDL_FreeSurface(animationImages[2]);
SDL_FreeSurface(animationImages[3]);
Related
I’ve been recently getting into OS development (completely from scratch), and I’m stuck on an issue where plotting pixels to the screen does not seem to work at all.
For reference, I’m using EDK2 for the UEFI utilities and compiling my bootloader using its build system.
I obtain the framebuffer from the GOP handle after setting my wanted mode (which should be 1366x768, BGRA colour format), but writing any value to the framebuffer memory space seems to not translate anything to the screen. Here are the projects (bootloader and OS) for references:
* OS: https://github.com/kernel-dev/kernelOS
* Bootloader: https://github.com/kernel-dev/kernelOSBootloader
Furthermore, here are the relevant snippets of code that should work, but don’t:
* Function declarations: https://github.com/kernel-dev/kernelOS/blob/main/src/Kernel/Graphics/KernGraphics.c
* Calling the function for clearing the screen: https://github.com/kernel-dev/kernelOS/blob/main/src/Kernel/Kernel.c
Solved
The reason why it wasn't working is because I wasn't properly getting the passed down arguments in my kernel.
This is how it looked like:
// Entry point for kernel
VOID
KernMain (
IN EFI_RUNTIME_SERVICES *RT,
IN EFI_KERN_MEMORY_MAP *MemoryMap,
IN ACPI_DIFFERENTIATED_SYSTEM_DESCRIPTOR_TABLE *Dsdt,
IN KERN_FRAMEBUFFER *Framebuffer)
{
ScreenClearTerminal (Framebuffer);
//
// Should never reach here.
// Will be removed later.
//
while (TRUE) {};
}
However, the way I actually pass them down is like this:
//
// Prepare the arguments to be passed down.
//
LoaderBlock->MemoryMap = &MemoryMap;
LoaderBlock->Dsdt = Dsdt;
LoaderBlock->RT = SystemTable->RuntimeServices;
LoaderBlock->Framebuffer = FB;
//
// Exit boot services.
//
/* ... */
//
// Locate the EP function and call it with the arguments.
//
typedef void (__attribute__((ms_abi)) *EntryPointFunction) (LOADER_PARAMS *LP);
EntryPointFunction EntryPointPlaceholder = (EntryPointFunction) (BaseAddress + EntryPoint);
EntryPointPlaceholder (LoaderBlock);
It's contained inside of a struct. So the appropriate way to obtain them would be like this:
/**
A structure used to "contain" all
the parameters to be passed down
to the kernel's EP.
**/
typedef struct {
EFI_RUNTIME_SERVICES *RT; /// Pointer to the runtime services.
EFI_KERN_MEMORY_MAP *MemoryMap; /// Pointer to the EFI_KERN_MEMORY_MAP.
ACPI_DIFFERENTIATED_SYSTEM_DESCRIPTOR_TABLE **Dsdt; /// Pointer to the DSDT pointer.
KERN_FRAMEBUFFER *Framebuffer; /// Pointer to the KERN_FRAMEBUFFER.
} LOADER_PARAMS;
// Entry point for kernel
VOID
KernMain (
LOADER_PARAMS *LP)
{
ScreenClearTerminal (LP->Framebuffer);
//
// Should never reach here.
// Will be removed later.
//
while (TRUE) {};
}
or, alternatively keeping the old method, but alternating the way they're passed down:
//
// Locate the EP function and call it with the arguments.
//
typedef void (__attribute__((ms_abi)) *EntryPointFunction) (
EFI_RUNTIME_SERVICES *RT,
EFI_KERN_MEMORY_MAP *MemoryMap,
ACPI_DIFFERENTIATED_SYSTEM_DESCRIPTOR_TABLE **Dsdt,
KERN_FRAMEBUFFER *Framebuffer
);
EntryPointFunction EntryPointPlaceholder = (EntryPointFunction) (BaseAddress + EntryPoint);
EntryPointPlaceholder (
SystemTable->RuntimeServices,
&MemoryMap,
Dsdt,
FB);
Completely my bad lol.
Thank you to #user123 and #Dave S for helping me.
I'm making a GTK+3 application in C and I want a spinner to show when the program is processing the data. Here's what I generally have:
main()
{
//Some statements
g_signal_connect(G_OBJECT(btnGenerate), "clicked", G_CALLBACK(Generate), &mainform);
}
void Generate(GtkWidget *btnGenerate, form_widgets *p_main_form)
{
gtk_spinner_start(GTK_SPINNER(p_main_form->spnProcessing));
Begin_Lengthy_Processing(Parameters, Galore, ...);
//gtk_spinner_stop(GTK_SPINNER(p_main_form->spnProcessing));
}
I have the stop function commented out so I can see the spinner spin even after the function has finished, but the spinner starts after the function is finished, and I suspect it turns on in the main loop.
I also found out that the entire interface freezes during the execution of the long going function.
Is there a way to get it to start and display inside the callback function? I found the same question, but it uses Python and threads. This is C, not Python, so I would assume things are different.
You need to run your lengthy computation in a separate thread, or break it up into chunks and run each of them separately as idle callbacks in the main thread.
If your lengthy computation takes a single set of inputs and doesn’t need any more inputs until it’s finished, then you should construct it as a GTask and use g_task_run_in_thread() to start the task. Its result will be delivered back to the main thread via the GTask’s GAsyncReadyCallback. There’s an example here.
If it takes more input as it progresses, you probably want to use a GAsyncQueue to feed it more inputs, and a GThreadPool to provide the threads (amortising the cost of creating threads over multiple calls to the lengthy function, and protecting against denial of service).
The GNOME developer docs give an overview of how to do threading.
This is what I got:
int main()
{
// Statements...
g_signal_connect(G_OBJECT(btnGenerate), "clicked", G_CALLBACK(Process), &mainform);
// More statements...
}
void Process(GtkWidget *btnGenerate, form_widgets *p_main_form)
{
GError *processing_error;
GThread *start_processing;
gtk_spinner_start(GTK_SPINNER(p_main_form->spnProcessing));
active = true;
if((start_processing = g_thread_try_new(NULL, (GThreadFunc)Generate, p_main_form, &processing_error)) == NULL)
{
printf("%s\n", processing_error->message);
printf("Error, cannot create thread!?!?\n\n");
exit(processing_error->code);
}
}
void Generate(form_widgets *p_main_form)
{
// Long process
active = false;
}
My program, once cleaned up and finished, as there are many other bugs in the program, will be put on GitHub.
Thank you all for your help. This answer comes from looking at all of your answers and comments as well as reading some more documentation, but mostly your comments and answers.
I did something similar in my gtk3 program. It's not that difficult. Here's how I would go about it.
/**
g_idle_add_full() expects a pointer to a function with the signature below:
(*GSourceFunc) (gpointer user_data).
So your function signature must adhere to that in order to be called.
But you might want to pass variables to the function.
If you don't want to have the variables in the global scope
then you can do this:
typedef struct myDataType {
char* name;
int age;
} myDataType;
myDataType person = {"Max", 25};
then when calling g_idle_add_full() you do it this way:
g_idle_add_full(G_PRIORITY_HIGH_IDLE, myFunction, person, NULL);
*/
int main()
{
// Assumming there exist a pointer called data
g_idle_add_full(G_PRIORITY_HIGH_IDLE, lengthyProcessCallBack, data, NULL);
// GTK & GDK event loop continues and window should be responsive while function runs in background
}
gboolean lengthyProcessCallBack(gpointer data)
{
myDataType person = (myDataType) *data;
// Doing lenghthy stuff
while(;;) {
sleep(3600); // hypothetical long process :D
}
return FALSE; // removed from event sources and won't be called again.
}
I've been designing a personal news feed app for the original pebble which displays news content in a menu layer. I have two C files, one which contains a window that displays a loading image and handles the data transfer between the js on the phone and my watch app. The data is saved into some extern variables which are shared between the files but is irrelevant in the example I show below (all of that works fine I believe).
I'm calling news_list_window_init() in my main .c file within a function when certain conditions are met. Right now I'm just trying to display a window with a scrollable menu to test that my menu looks acceptable. The problem I'm having is that my app crashes when I try to scroll. The window appears, all my menu items are there but when I scroll the app exists with:
ault_handling.c:78> App fault! {f5ec8c0d-9f21-471a-9a5c-c83320f7477d} PC: 0x800fd5b LR: ???
Program Counter (PC) : 0x800fd5b ???
Link Register (LR) : ??? ???
I've tested my .c file below independently, where I create a new project with only this code, comment out the non-relevant news_list.h and externs.h header files, and comment in the main function at the bottom of the file. This works fine, my menu scrolls and there are no crashes and everything looks fine.
I don't see how the problem could be in my main file, because the only function I call in it which is contained in this file is news_list_window_init(), moreover the menu does indeed display correctly. I can even close the app properly with the back button. Trying to scroll however crashes the application. I'm sort of at a loss as to what could be causing this error. Does anybody have any suggestions? Thanks!
This is the relevant .c file:
// news_list.c
#include "pebble.h"
#include "news_list.h"
#include "externs.h"
#define NUM_MENU_SECTIONS 1
static Window *news_list_window;
static MenuLayer *menu_layer;
static uint16_t menu_get_num_sections_callback(MenuLayer *menu_layer, void *data) {
return NUM_MENU_SECTIONS;
}
static uint16_t menu_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
return str_count; // Variable story count
}
static int16_t menu_get_header_height_callback(MenuLayer *menu_layer, uint16_t section_index, void *data) {
return MENU_CELL_BASIC_HEADER_HEIGHT;
}
static void menu_draw_header_callback(GContext* ctx, const Layer *cell_layer, uint16_t section_index, void *data) {
menu_cell_basic_header_draw(ctx, cell_layer, "Header");
}
static void menu_draw_row_callback(GContext* ctx, const Layer *cell_layer, MenuIndex *cell_index, void *data) {
menu_cell_basic_draw(ctx, cell_layer, "Menu Item", NULL, NULL);
}
static void menu_select_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *data) {
// Currently Empty
}
int16_t menu_get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context)
{
return 40;
}
static void news_list_window_load(Window *window) {
// Now we prepare to initialize the menu layer
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_frame(window_layer);
// Create the menu layer
menu_layer = menu_layer_create(bounds);
menu_layer_set_callbacks(menu_layer, NULL, (MenuLayerCallbacks){
.get_num_sections = menu_get_num_sections_callback,
.get_num_rows = menu_get_num_rows_callback,
.get_header_height = menu_get_header_height_callback,
.draw_header = menu_draw_header_callback,
.draw_row = menu_draw_row_callback,
.select_click = menu_select_callback,
.get_cell_height = menu_get_cell_height_callback,
});
// Bind the menu layer's click config provider to the window for interactivity
menu_layer_set_click_config_onto_window(menu_layer, window);
layer_add_child(window_layer, menu_layer_get_layer(menu_layer));
}
static void news_list_window_unload(Window *window) {
// Destroy the menu layer
menu_layer_destroy(menu_layer);
}
void news_list_window_init() {
news_list_window = window_create();
window_set_window_handlers(news_list_window, (WindowHandlers) {
.load = news_list_window_load,
.unload = news_list_window_unload,
});
window_stack_push(news_list_window, true);
}
void news_list_window_deinit() {
window_destroy(news_list_window);
}
// int main(void) {
// news_list_window_init();
// app_event_loop();
// news_list_window_deinit();
// }
This is the relevant .h file:
// news_list.h
#ifndef NEWS_LIST_H
#define NEWS_LIST_H
// Public Function list
void news_list_window_init(void);
void news_list_window_deinit(void);
#endif
I've had this happen when I'm running really low on memory, or if I'm corrupting memory somehow.
I'd suggest checking all return values for NULL and logging and bailing if they are (menu_layer_create, malloc etc.).
Also, try logging how much free memory you have - if you are working on an original Pebble, and allocating large memory buffers for communication, you can very quickly run out of memory.
Finally, walk through all code that allocates and uses memory to be absolutely sure you are not accidentally writing past the end of an array, or passing a value allocated on the stack into a pebble call. I like to use calloc rather than malloc to be sure I don't think I have a value in a malloc'd structure when I don't.
I've been here before - it isn't easy, but likely totally unrelated to your UI code - the crash is just a symptom. If all else fails maybe post the complete code in github so that we can take a look at the complete app...
Damian
I am in the beginning of a game of brick breaker type and I'm stuck in the SDL_Flip step. My CodeBlocks compiler says nothing and the console doesn't crash, but yet the SDL window shutdown and the console process returned code 3. When I ran the debugger it says:
SDL_Flip()
Display(Bricks=0x28f69c, screen=0x0)
and the Display type error was said located at the line of my SDL_Flip(screen);
Here's a glimpse of my code. My Brick_Coordinates and Brick_Surface struct are already initialize (my coordinates for Brick_Coordinates and NULL for Brick_Surface) by another function before that one:
void Display(BrickStruct Bricks[12][10],SDL_Surface *screen)
{
int i=0,j=0;
for(j=0;j<10;j++)
{
if( (j+1)%2==0 ) // If we are on even lines, display only 11 bricks
{
for(i=0;i<11;i++)
{
Bricks[i][j].Brick_Surface = IMG_Load("BrickTest1.png");
SDL_BlitSurface(Bricks[i][j].Brick_Surface, NULL, screen, &Bricks[i][j].Brick_Coordinates);
SDL_Flip(screen);
}
}
else // If we are on odd lines, display the 12 bricks
{
for(i=0;i<12;i++)
{
}
}
}
}
My Structure looks like this:
typedef struct BrickStruct
{
int type;
SDL_Rect Brick_Coordinates;
SDL_Surface *Brick_Surface;
}BrickStruct;
In my main, my code is like this:
SDL_Surface *screen= NULL;
BrickStruct Bricks[12][10]; // I create my 2D array of struct named Bricks
Display(Bricks,screen);
I've already tested with a fprintf the values of my coordinates initialized. These are good. And apparently my SDL_Blit is working. But The Flip isn't. My screen surface is big enough for all my images (480x540 and my images are 40x20). I was wondering if that problem has to do with an impossibility for Blit to place an image on top of another but the Flip doesn't even work when I try with only one image (without my loops).
Can somebody please have the kindness to indicate me where is located my problem ?
Thanks in advance
There reason was that that you didn't save screen into the global variable.
You probably had a line in your SDL_Initialisation similar to this:
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
This creates a new local variable called screen. Since you wanted to save this into the global one, you should change it to:
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
According to your debugger and your example code your screen structure is null. So your call to SDL_BlitSurface will fail. The reason it probably works for you when you do your Display call inside your Initialize is that you've just initialized your screen and used it right after.
You need to store the surface you are writing to and use it again when you're blitting.
Also, as others have recommended, you should take a look at a tutorial for SDL and perhaps some more C tutorials to reinforce some concepts.
I've got one function do_refresh which should draw some character in a window like this:
void do_refresh(WINDOW *w_game, int *xPos, int *yPos, char vector[], snake *snake){
mvwaddch(w_game, (*yPos), (*xPos), snake->headsym);// mv to new pos and place char
wrefresh(w_game);
}
The window w_game has also a panel pendant which lies on top of all other panels.
Before that function gets called, I let the user do non blocking input with getch() and timeout(0):
fflush(stdin);
key = getch();
if(key != ERR){ ...
only the first time I call do_refresh, the char gets drawn to the window, later nothing changes though xPos & yPos as well as all other parameters to mvwaddch are valid and change over the time.
Doing a redrawwin on the window causes a segfault, using wgetch(w_game) instead of getch() returns no input.
I would be very grateful if someone could at least link to a decent documentation what has to be considered when using wgetch instead and what it does different.
UPDATE
I found the solution to the problem, The function which sets up the panel stuff modifies the address of the windows!, you have to return the (new) destination of the pointer to main and reset it there like this:
...setup_panels(...){
return w_game; // my window
}
int main(...){
WINDOW *w_game;
[...]
w_game = setup_panels(...);
}