MLX42 - Intro

11 months

2024 01

Learn how to use the brand new mlx library, baked in CODAM just for you!

MLX42 is simple and gives you more freedom!

A few useful things before you get started:

MLX42 is the new un-official version of mlx, make sure this tutorial is appropiate for your project
MLX42 has an official documentation which you can visit here: https://github.com/codam-coding-college/MLX42/wiki
MLX42 functions can be found here 

Alright, that last one was a troll... you can find them (all the functions) on the headers.

Let's get started!


First you will need to download the library, for that you can use the following commands:

git clone https://github.com/codam-coding-college/MLX42
cd MLX42
cmake -B build
make -C build/
Now you should be able to locate the libmlx42.a file.
You can use mlx42 library by linking the library
-L<path-to-library> -lmlx42

MLX42 is dependent on a few other libraries, when you are using Linux you should also link the libraries required. With the following command.
-ldl -lglfw -lm -lpthread
dl -> Dynamically loaded (DL)
glfw -> Graphics Library Framework
m -> Math library

I hope this helps demystify what all those weird symbols are when you are compiling! It should all make sense now.

Make sure to run the example which is on the MLX42 GitHub README to make sure that you have installed the library with success!
* Note: 
You can go ahead and copy-paste the example from the GitHub and compile with the following:

gcc -o test main.c -ldl -lglfw -lm -lpthread -LMLX42/build -lmlx42 -IMLX42/include

./test
Now that everything is setup, we can begin creating a program.
For this tutorial we are going to build a color game!
It's simple, the game consist on choosing the same color as the one displayed by the program, trust your eyes!

It will include:
Animations
Menu
Game dynamics
Let's start by creating the menu. The menu is the first thing that your user will see. The user will be able to choose between a set of options in order to use your program.

For this tutorial we will keep it simple, a button to start the game and another to configure the difficulty level.

The menu will have a fixed image as a background, and we will create a selector for the user to choose the options. (No mouse, the user selects with the keys)
Download the menu image bellow and save it in a folder accessible to your project.

*Note: I created the background image  with Tiled and this sprite pack.
As you might have noticed, the second button is empty, that's because the text that will be on top of that button might change depending on the option that the player chooses. So I've prepared all the images with the possible text.
"Easy", "Medium" & "Hard"
Let's start simple, create a main, load our texture (the menu background image) convert it to an image and place the image into the window.

"in order to display a sprite image onto our window we would first load the texture from disk into our memory and store the information in a mlx_texture_t*. After that we create a new mlx_image_t* based on the information given by the texture and then we can display our image onto the window" - from the wiki

#include <stdlib.h>
#include <stdio.h>
#include "MLX42/MLX42.h"

#define WIDTH 640
#define HEIGHT 360

static void error(void)
{
  puts(mlx_strerror(mlx_errno));
  exit(EXIT_FAILURE);
}

int32_t main(void)
{
  // Start mlx
  mlx_t* mlx = mlx_init(WIDTH, HEIGHT, "Color Game", false);
  if (!mlx)
        error();

  // Try to load the file
  mlx_texture_t* texture = mlx_load_png("./images/menu_bg.png");
  if (!texture)
        error();
  
  // Convert texture to a displayable image
  mlx_image_t* img = mlx_texture_to_image(mlx, texture);
  if (!img)
        error();

  // Display the image
  if (mlx_image_to_window(mlx, img, 0, 0) < 0)
        error();

  mlx_loop(mlx);

  mlx_delete_image(mlx, img);
  mlx_delete_texture(texture);

  // Optional, terminate will clean up any leftover images (not textures!)
  mlx_terminate(mlx);
  return (EXIT_SUCCESS);
}

We initialize mlx with mlx_init (the last parameter is fasle, indicating we don't want this to be a resizable window).
Then you should be able to visualize the image in your mlx window.

Animations


Now let's create an animation.
We will animate our selector (to choose the options). We will use a linked list to iterate through each of the images.
If you don't understand the code bellow, go to my animations tutorial where everything is explained.

I've create a sprite-sheet with the animation using Pixilart.
Download the sheet so we can cut it to create our animation.

Slices


Let's create the functions which will allow us to cut in slices our sprite-sheet.
I will be using my libft in order to use the linked lists (t_list).
Let's start by defining the header

animate.h

#ifndef __ANIMATIONS_H__
# define __ANIMATIONS_H__

# include "libft.h"
# include "MLX42/MLX42.h"

typedef struct s_animation {
  t_list*   frames;
  int       frame_speed;        // The speed of the animation in miliseconds
  double    accum;              // The accumulator to controll the speed
  int       current_frame_num;  // Which frame is selected
  int       mirrored;
  long int  frame_count;        // The frame count
} t_animation;

typedef struct sprite_slice {
  int x;
  int y;
  int width;
  int height;
  int padding_x;
  int padding_y;
} sprite_slice;

typedef struct s_sprite {
  mlx_image_t*  sprite_img;
  mlx_t*        mlx;
} t_sprite;

/* SPRITES */
t_sprite      new_sprite(char* file_name, mlx_t* mlx);
t_animation*  slice_sprite(t_sprite* s, sprite_slice slice, int mirrored, int frames, int delay);
void          destroy_sprite(t_sprite* s);

#endif

The t_sprite is really just to help us convert the PNG, then we have a sprite_slice which helps us cut the sprite.
In the case of the sprite-sheet above each frame is 128x32 px, so the slice will need to cut those dimensions.

Now let's create the code:
(I've included the declaration of the error function on the top (the error function is in the main.c file)

animate.c

#include "animate.h"

void error(void);

t_sprite new_sprite(char* file_path, mlx_t* mlx) {
  mlx_image_t* img;
  mlx_texture_t* texture;

  // Load png
  texture = mlx_load_png(file_path);
  if (!texture)
    error();

  // Create image from png texture
  img = mlx_texture_to_image(mlx, texture);
  if (!img)
    error();

  mlx_delete_texture(texture);

  return (t_sprite){img, mlx};
}

void    destroy_sprite(t_sprite* s) {
    if (!s)
        error();
    mlx_delete_image(s->mlx, s->sprite_img);
}

static void add_frame(t_animation* a, t_sprite* s, sprite_slice slice, int mirrored) {
    mlx_image_t* frame;

    frame = mlx_new_image(s->mlx, slice.width - (slice.padding_x * 2), slice.height - (slice.padding_y * 2));
    if (!frame)
        error();
    for (int i = 0; i < slice.height - (slice.padding_y * 2); i++) {
        for (int j = 0; j < slice.width - (slice.padding_x * 2); j++) {
            if (mirrored)
                mlx_put_pixel(frame, (slice.height - (slice.padding_y * 2)) - j - 1, i, mlx_get_pixel(s->sprite_img, slice.x + j + slice.padding_x, slice.y + i + slice.padding_y));
            else
                mlx_put_pixel(frame, j, i, mlx_get_pixel(s->sprite_img, slice.x + j + slice.padding_x, slice.y + i + slice.padding_y));
        }
    }
    ft_lstadd_back(&a->frames, ft_lstnew(frame));
}

t_animation* slice_sprite(t_sprite* s, sprite_slice slice, int mirrored, int frames, int delay) {
    t_animation *   a;

    a = (t_animation*)calloc(sizeof(t_animation), 1);
    if (!a)
        error();
    *a = (t_animation){NULL, delay, 0, 0, mirrored, 0, 0};
    for (int i = 0; i < frames; i++) {
        add_frame(a, s, slice, mirrored);
        slice.x += slice.width;
        if ((uint32_t)slice.x >= s->sprite_img->width) {
            slice.x = 0;
            slice.y += slice.height;
        }
    }
    return (a);
}

Now you can compile your code with this:
(We must add the linking of our libft, I usually create a soft-link ln -s <path_to_libft> libft)

gcc -o test animate.c main.c -ldl -lglfw -lm -lpthread -LMLX42/build -lmlx42 -Llibft/ -lft -IMLX42/include -Ilibft/includes/

After you compile this you will see an error.

animate.c:39:89: warning: implicit declaration of function ‘mlx_get_pixel’; did you mean ‘mlx_put_pixel’?

The MLX42 library has a function mlx_put_pixel but it lacks the function mlx_get_pixel.

We can create this function easily...

image_utils.c

#include "MLX42/MLX42.h"

#define BPP sizeof(int32_t)

static int get_rgba(int r, int g, int b, int a) {
  return (r << 24 | g << 16 | b << 8 | a);
}

/**
 * In MLX42 Colors are as follows:
 * 0000 0000 A (1st byte) -> uint8_t because it's 8 bits
 * 0000 0000 R (2nd byte)
 * 0000 0000 G (3rd byte)
 * 0000 0000 B (4th byte)
 **/
int32_t mlx_get_pixel(mlx_image_t* image, uint32_t x, uint32_t y) {
  if (x > image->width || y > image->height)
    return 0xFF000000;
  uint8_t* pixelstart = image->pixels + (y * image->width + x) * BPP;
  return get_rgba(*(pixelstart), *(pixelstart + 1),
    * (pixelstart + 2), *(pixelstart + 3));
}

Add the declaration of the mlx_get_pixel on top of your animate.c file.

Now we can add the file image_utils.c to our compilation and it should compile without any issues.

Let's test then to see if the code works! Let's put it to practice.

As I mentioned before, the slice will be something like this:
(sprite_slice){0, 0, 128, 32, 0, 0}

We can create a structure to hold our game components, this way we can control all the game elements through one single pointer.

typedef struct s_color_game {
  mlx_t * mlx;
  mlx_image_t * menu_bg;
  t_animation * select_animation;
} t_color_game;

Now your main.c should look like this:

#include <stdlib.h>
#include <stdio.h>
#include "MLX42/MLX42.h"
#include "animate.h"

#define WIDTH 640
#define HEIGHT 360

typedef struct s_color_game {
  mlx_t * mlx;
  mlx_image_t * menu_bg;
  t_animation * select_animation;
} t_color_game;

void error(void) {
  puts(mlx_strerror(mlx_errno));
  exit(EXIT_FAILURE);
}

void bait(void*ptr){(void)ptr;};

t_color_game init_game() {
  mlx_t*          mlx;
  mlx_texture_t*  texture;
  mlx_image_t*    img;
  t_animation*    anim;
  t_sprite        sprite;

  // Start mlx
  mlx = mlx_init(WIDTH, HEIGHT, "Color Game", false);
  if (!mlx)
    error();
  // Try to load the file
  texture = mlx_load_png("./images/menu_bg.png");
  if (!texture)
    error();
  // Convert texture to a displayable image
  img = mlx_texture_to_image(mlx, texture);
  if (!img)
    error();
  mlx_delete_texture(texture);
  // Sprite & Animation
  sprite = new_sprite("./images/select_sprite_sheet.png", mlx);
  anim = slice_sprite(&sprite, (sprite_slice){0, 0, 128, 32, 0, 0}, false, 5, 120);
  destroy_sprite(&sprite);

  return (t_color_game){mlx, img, anim};
}

int32_t main(void)
{
  t_color_game cg;

  cg = init_game();

  if (mlx_image_to_window(cg.mlx, cg.menu_bg, 0, 0) == -1)
    error();

  mlx_loop(cg.mlx);

  ft_lstclear(&cg.select_animation->frames, bait);
  free(cg.select_animation);
  mlx_terminate(cg.mlx);
  return (EXIT_SUCCESS);
}

*Note: I've set up the frame_speed to 120ms, later we will use this to update the animation.

Awesome! Finally we can use our animation. Let's implement a hook to display our image.

In order to display animations, we need to draw the current frame to an image and display the image, when the animation is supposed to change, the frame should be repainted, and so on and so on.

We need a "foreground" image which will hold the foreground of our game, that is the outer-most layer. We will paint the frames of the animation in the foreground layer. It will look something like this:

Image Utils


In order to paint the frames of the animation to the foreground we need to create a function which can copy an image to another image (we must copy the frame to the foreground).

That function can look something like this:
(The put_pixel_valid function is just a protection in case the dst image is smaller)

image_utils.c

static int put_pixel_valid(mlx_image_t* img, uint32_t x, uint32_t y)
  return (x < img->width && y < img->height && mlx_get_pixel(img, x, y) != 0);
}

void  put_img_to_img(mlx_image_t* dst, mlx_image_t* src, int x, int y) {
  int i;
  int j;

  i = 0;
  while(i < src->width) {
    j = 0;
    while (j < src->height) {
      if (put_pixel_valid(src, i, j))
        mlx_put_pixel(dst, x + i, y + j, mlx_get_pixel(src, i, j));
      j++;
    }
    i++;
  }
}

Now let's add the foreground to our game structure:

typedef struct s_color_game {
  mlx_t * mlx;
  mlx_image_t * menu_bg;
  mlx_image_t * foreground;
  t_animation * select_animation;
  int fps;
} t_color_game;

In the game_init function let's add the foreground creation:

...
  mlx_image_t*    foreground_img;
...
  // Foreground
  foreground_img = mlx_new_image(mlx, WIDTH, HEIGHT);
  if (!foreground_img)
    error();
  return (t_color_game){mlx, img, foreground_img, anim};
}

And finally let's create an update function for the loop hook!

void update(void * ptr) {
  t_color_game* cg = (t_color_game*)ptr;

  mlx_image_t * frame = (mlx_image_t *)ft_lstget(cg->select_animation->frames, cg->select_animation->current_frame_num)->content;
  if (!frame)
    error();
  put_img_to_img(cg->foreground, frame, 256, 160);
  update_animation(cg->select_animation, cg->mlx->delta_time);
}

We use ft_lstget to retrieve the current frame of the animation. Then we place the animation at coordinates [256, 160]. (This is the coordinates of the button, later we will implement something more dynamic)

You can compile this with the following command:
gcc -fsanitize=address -o test image_utils.c animate.c main.c -ldl -lglfw -lm -lpthread -LMLX42/build -lmlx42 -Llibft/ -lft -IMLX42/include -Ilibft/includes 

You will see an error:
main.c:71:3: warning: implicit declaration of function ‘update_animation’

Of course we are missing this fun function!

Update Animation


This function is super important to understand.
Basically the delta time will tell up how much time has passed between the last frame and the current frame.
Accumulating the time that passes between each frame will let us know when we are supposed to change the animation.
This method is very good, because the animation speed is not dependant on the fps (or the performance) of the program.
The animation will have a fixed frame speed and will change it's frame at that speed.

So we can implement the function finally:

animate.h

void update_animation(t_animation * a, double dt);

animate.c

void update_animation(t_animation * a, double dt) {
  if (a) {
    a->accum += dt * 1000;
    if (a->accum > a->frame_speed) {
      a->accum -= a->frame_speed;
      a->current_frame_num++;
      a->current_frame_num %= ft_lstsize(a->frames);
    }
  }
}

The accumulation is counted in miliseconds, just as our frame speed.
We multiply in order to be in the same order of magnitude (miliseconds), since the delta time is given in seconds.
Remember 1 seconds = 1000 miliseconds.

Finally implement the hook, on the main function and we will be able to see an animation! :D

mlx_loop_hook(cg.mlx, update, &cg);

Game Dynamics

We've reached a very important part of the tutorial, the game dynamics. I always recommend not to hesitate on having as many useful data types as you can, but also be sure not to overwhelm so much the program to a point where you don't understand why you need that data type.

That is why I suggest only the following data types to control the menu:

#define SELECTION_LEN 2

enum game_status {
  MENU,
  PLAYING
};

enum menu_selection {
  SELECT_PLAY,
  SELECT_DIFFICULTY
};

The game status will allow us to control the input when the player is playing and when the player is selecting stuff in the menu.

The menu selection helps keeping track of our selection, we only have two options in this case, that's why I've set the define SELECTION_LEN to 2.

Finally let's add this to out color game struct:

typedef struct s_color_game {
  mlx_t * mlx;
  mlx_image_t * menu_bg;
  mlx_image_t * foreground;
  t_animation * select_animation;
  enum menu_selection menu_selection;
  enum game_status game_status;
} t_color_game;

And of course, add this to the game_init return:

return (t_color_game){mlx, img, foreground_img, anim, SELECT_PLAY, MENU};

Now we can add a static member to our update function, a matrix with the exact coordinates of our buttons.

static int menu_selection_coords[SELECTION_LEN][SELECTION_LEN] = {{256, 160}, {256, 256}};

Now, we can modify the put_img_to_img function so it will choose the appropriate coordinate depending on the menu_selection.

put_img_to_img(cg->foreground, frame,
    menu_selection_coords[cg->menu_selection][0],
    menu_selection_coords[cg->menu_selection][1]);

And finally we can add the logic to our menu!

In the update function add the logic for the menu, when the user presses the down key, move the selection down, when the user press up, choose the selection which is above.

  // Logic for the menu
  if (cg->game_status == MENU) {
    if (mlx_is_key_down(cg->mlx, MLX_KEY_DOWN)) {
      cg->menu_selection = SELECT_DIFFICULTY;
    }
    if (mlx_is_key_down(cg->mlx, MLX_KEY_UP)) {
      cg->menu_selection = SELECT_PLAY;
    }
  }

Finally if you compile this, you should be able to change between selection, there is just one problem...
When we change the selection, the other selection is not erased from the foreground!

This can be fixed very easily, just clean the foreground before you paint the animation frame!

On the first line of execution of out update function add the following line:

memset(cg->foreground->pixels, 0xFF000000, cg->foreground->width * cg->foreground->height * BPP);

It's simple, just set all the pixels to transparent. Ta-Da it's clean! :D

Menu Multiple Options

As you would have guessed from the title, now we are going to create the difficulty selection.
We are not going to implement this one in the update function just yet... That is because the update function can only receive input when the key is pressed, and this is repeated each frame, it would be very complicated to implement a multiple options when you press a key, if they key is pressed 1000 times in one second.

We have 3 options EASY, MEDIUM & HARD, when the user selects the difficulty button, they should be able to change the difficulty options with the right & left keys, but only when the key is pressed the first time.

So we can implement that with a key hook!

First let's modify our game structure:

#define DIFFICULTY_LEN 3

enum game_difficulty {
  EASY,
  MEDIUM,
  HARD
};

typedef struct s_color_game {
  mlx_t * mlx;
  mlx_image_t * menu_bg;
  mlx_image_t * foreground;
  t_animation * select_animation;
  enum menu_selection menu_selection;
  enum game_status game_status;
  enum game_difficulty game_difficulty;
} t_color_game;

And in the init_game function remember to modify the return as well.

return (t_color_game){mlx, img, foreground_img, anim, SELECT_PLAY, MENU, EASY};

Now we can implement the key_hook:

void key_update(mlx_key_data_t keydata, void* ptr) {
  t_color_game* cg = (t_color_game*)ptr;

  if (cg->game_status == MENU && cg->menu_selection == SELECT_DIFFICULTY && keydata.action == MLX_PRESS) {
    if (keydata.key == MLX_KEY_LEFT) {
      cg->game_difficulty--;
      if (cg->game_difficulty == -1)
        cg->game_difficulty = DIFFICULTY_LEN - 1;
    } else if (keydata.key == MLX_KEY_RIGHT) {
      cg->game_difficulty++;
      cg->game_difficulty %= DIFFICULTY_LEN;
    }
  }
}

So if they game status is menu, and we are choosing the difficulty and the input is a key pressed, then:
When the key pressed is the left key, then subtract one value, unless the value is lower negative, then go to the last value (DIFICULTY_LEN - 1)
When the key pressed is the right key, then increment one value, and do the modulus with the DIFICULTY_LEN to make sure that it will never surpass the options we have.

Now this code will make sure that the input get's handled, but we still need to show the user the level which he is choosing, to do this we will need to create 3 images, one for each level. 

Modify, again, the color_game structure.

typedef struct s_color_game {
  mlx_t * mlx;
  mlx_image_t * menu_bg;
  mlx_image_t * foreground;
  mlx_image_t * difficulty_imgs[DIFFICULTY_LEN];
  t_animation * select_animation;
  enum menu_selection menu_selection;
  enum game_status game_status;
  enum game_difficulty game_difficulty;
} t_color_game;

And of course, we must also edit the init_game function.

Because we would need to create a texture and an image for each difficulty image, it would be so repetitive to put this as it is. That is why I will create a function my_load_png and in this function I will take advantage and will also setup the image to the window, so it will enable 1 instance for each image. (I can also take advantage and replace the code from the menu background)

The functions would look something like this:

mlx_image_t * my_load_png( char* file_path, mlx_t * mlx) {
  mlx_image_t*    img;
  mlx_texture_t*  texture;
  // Try to load the file
  texture = mlx_load_png(file_path);
  if (!texture)
    error();
  // Convert texture to a displayable image
  img = mlx_texture_to_image(mlx, texture);
  if (!img)
    error();
  if (mlx_image_to_window(mlx, img, 0, 0) == -1)
    error();
  mlx_delete_texture(texture);
  return (img);
}

t_color_game init_game() {
  mlx_t*          mlx;
  mlx_image_t*    img;
  mlx_image_t*    foreground_img;
  mlx_image_t*    difficulty_imgs[DIFFICULTY_LEN];
  t_animation*    anim;
  t_sprite        sprite;

  // Start mlx
  mlx = mlx_init(WIDTH, HEIGHT, "Color Game", false);
  if (!mlx)
    error();
  // Load the Background and Menu images
  img = my_load_png("./images/menu_bg.png", mlx);
  difficulty_imgs[0] = my_load_png("./images/menu_easy.png", mlx);
  difficulty_imgs[1] = my_load_png("./images/menu_medium.png", mlx);
  difficulty_imgs[2] = my_load_png("./images/menu_hard.png", mlx);
  // Sprite & Animation
  sprite = new_sprite("./images/select_sprite_sheet.png", mlx);
  anim = slice_sprite(&sprite, (sprite_slice){0, 0, 128, 32, 0, 0}, false, 5, 120);
  destroy_sprite(&sprite);
  // Foreground
  foreground_img = mlx_new_image(mlx, WIDTH, HEIGHT);
  if (!foreground_img)
    error();
  return (t_color_game){mlx, img, foreground_img,
    {difficulty_imgs[0], difficulty_imgs[1], difficulty_imgs[2]},
    anim, SELECT_PLAY, MENU, EASY};
}

Also make sure to remove from the main, the mlx_image_to_window of the background. But leave the foreground one, or else you wont be able to see the animation anymore :( .

Now, if we start the program we will see the 3 images stack one after another, we don't want that, we only want to see the dificulty level which is selected. For this we can play with the instance enabled boolean.

Set all the dificulty images enabled to false on the update function, and only activate the one which is selected, and Voila!

update

  // Logic for the menu
  if (cg->game_status == MENU) {  
    for (int i = 0; i < DIFFICULTY_LEN; i++) {
      cg->difficulty_imgs[i]->instances[0].enabled = false;
    }
    cg->difficulty_imgs[cg->game_difficulty]->instances[0].enabled = true;
    if (mlx_is_key_down(cg->mlx, MLX_KEY_DOWN)) {
      cg->menu_selection = SELECT_DIFFICULTY;
    }
    if (mlx_is_key_down(cg->mlx, MLX_KEY_UP)) {
      cg->menu_selection = SELECT_PLAY;
    }
  }

Start the game!

Now we can start the game!

We want to start the game when the use releases the "Enter"
And so we can create this logic on the key_hook function. And also with a few modification on the update function.
The game will need to have a background image as well.
We are going to add an extra attribute to the color game, the game_bg

typedef struct s_color_game {
  mlx_t * mlx;
  mlx_image_t * menu_bg;
  mlx_image_t * game_bg;
  mlx_image_t * foreground;
  mlx_image_t * difficulty_imgs[DIFFICULTY_LEN];
  t_animation * select_animation;
  enum menu_selection menu_selection;
  enum game_status game_status;
  enum game_difficulty game_difficulty;
} t_color_game;

Make sure to create this image in the init_game, we can just use the new function we created to load PNG images.

init_game

init_game()
...
mlx_image_t*    img2;
...
img2 = my_load_png("./images/game_bg.png", mlx);
...
return (t_color_game){mlx, img, img2, foreground_img,
    {difficulty_imgs[0], difficulty_imgs[1], difficulty_imgs[2]},
    anim, SELECT_PLAY, MENU, EASY};

The image which I will use for the game background is the one bellow.
When the user chooses to start playing, we must remove the menu background, the difficulty images, the selection animation, well basically everything which was on the menu.

The foreground by default will be cleaned each frame, so we don't need to worry about the select animation, although we should move the logic to put it in the foreground inside the if "menu" .

As for the other components, we can simply set the enabled boolean to false and they will forever be gone! :D
MLX42 is just so simple!

So the code would be something like this:

void update(void * ptr) {
  static int menu_selection_coords[SELECTION_LEN][SELECTION_LEN] = {{256, 160}, {256, 256}};
  t_color_game* cg = (t_color_game*)

  // Clean the foreground
  memset(cg->foreground->pixels, 0xFF000000, cg->foreground->width * cg->foreground->height * BPP);
  if (cg->game_status == MENU) {
    // Paint the select animation on the foreground
    mlx_image_t * frame = (mlx_image_t *)ft_lstget(cg->select_animation->frames, cg->select_animation->current_frame_num)->content;
    if (!frame)
      error();
    put_img_to_img(cg->foreground, frame,
      menu_selection_coords[cg->menu_selection][0],
      menu_selection_coords[cg->menu_selection][1]);
    update_animation(cg->select_animation, cg->mlx->delta_time);
    // Logic for the menu
    for (int i = 0; i < DIFFICULTY_LEN; i++) {
      cg->difficulty_imgs[i]->instances[0].enabled = false;
    }
    cg->difficulty_imgs[cg->game_difficulty]->instances[0].enabled = true;
    if (mlx_is_key_down(cg->mlx, MLX_KEY_DOWN)) {
      cg->menu_selection = SELECT_DIFFICULTY;
    }
    if (mlx_is_key_down(cg->mlx, MLX_KEY_UP)) {
      cg->menu_selection = SELECT_PLAY;
    }
  }
}

void key_update(mlx_key_data_t keydata, void* ptr) {
  t_color_game* cg = (t_color_game*)ptr;

  if (cg->game_status == MENU && keydata.action == MLX_RELEASE) {
    if (keydata.key == MLX_KEY_ENTER) {
      // Disable the menu components
      for (int i = 0; i < DIFFICULTY_LEN; i++)
        cg->difficulty_imgs[i]->instances[0].enabled = false;
      cg->menu_bg->instances[0].enabled = false;
      cg->game_status = PLAYING;
    }
  } else if (cg->game_status == MENU && cg->menu_selection == SELECT_DIFFICULTY && keydata.action == MLX_PRESS) {
    if (keydata.key == MLX_KEY_LEFT) {
      cg->game_difficulty--;
      if (cg->game_difficulty == -1)
        cg->game_difficulty = DIFFICULTY_LEN - 1;
    } else if (keydata.key == MLX_KEY_RIGHT) {
      cg->game_difficulty++;
      cg->game_difficulty %= DIFFICULTY_LEN;
    }
  }
}
Now because the tutorial is getting very very long, the actual game I will publish in another blog post. I hope you enjoyed so far!

Thanks a lot to CODAM for creating this awesome library!