part: implement some functions (windows & display)

part:
- remove mlx3ds_3dskey.h, instead use hid.h
- add definitions of:
  - mlx_init()
  - mlx_new_window()
  - mlx_clear_window()
  - mlx_destroy_window()
  - mlx_pixel_put()
  - mlx_new_image()
  - mlx_get_data_addr()
  - mlx_put_image_to_window()
  - mlx_destroy_image()

dev:
- Makefile: add rule check_headers
- Makefile: add variable FLAGS
    (call 'make rule FLAGS=flags'
	to add flags to compilation)
- add /dev/norm.sh: nicer norminette
- add tester in main(), and related utilsconsole.*

docs:
- make clear mlx_get_data_addr() docs
This commit is contained in:
Zy 2024-10-06 16:24:31 +02:00
parent c7d0dba4fd
commit 13b93106ee
19 changed files with 766 additions and 141 deletions

View file

@ -56,7 +56,7 @@ RSF := $(TOPDIR)/$(RESOURCES)/template.rsf
# options for code generation # options for code generation
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
COMMON := -Wall -O2 -mword-relocations -fomit-frame-pointer -ffunction-sections $(ARCH) $(INCLUDE) -D__3DS__ COMMON := -Wall -O2 -mword-relocations -fomit-frame-pointer -ffunction-sections $(ARCH) $(INCLUDE) -D__3DS__ $(FLAGS)
CFLAGS := $(COMMON) -std=gnu99 CFLAGS := $(COMMON) -std=gnu99
CXXFLAGS := $(COMMON) -fno-rtti -fno-exceptions -std=gnu++11 CXXFLAGS := $(COMMON) -fno-rtti -fno-exceptions -std=gnu++11
ASFLAGS := $(ARCH) ASFLAGS := $(ARCH)
@ -343,3 +343,27 @@ endef
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
endif endif
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
# Added by Zy
# To check if the headers are protected and if they include everything they need.
check_headers :
@ERROR=0; \
for HEADER in $(wildcard *.h) $(wildcard **/*.h) $(wildcard *.hpp) $(wildcard **/*.hpp); \
do \
echo "check header $$HEADER..."; \
> __tmp_check_header.cpp echo "#include \"$$HEADER\""; \
>> __tmp_check_header.cpp echo "#include \"$$HEADER\""; \
>> __tmp_check_header.cpp echo "int main(void) {}"; \
$(CPP) $(INCLUDE) $(COMMON) $(LIBS) -o __tmp_check_header.out -c __tmp_check_header.cpp; \
if [ $$? -ne 0 ]; \
then \
ERROR=1; \
/bin/echo " ### error :( ###"; \
else \
/bin/echo " ### good :) ###"; \
fi; \
2> /dev/null rm -- "__tmp_check_header.out" "__tmp_check_header.c"; \
done; \
if [ $$ERROR -eq 0 ]; then true; else false; fi;
.PHONY : check_headers

19
dev/norm.sh Executable file
View file

@ -0,0 +1,19 @@
#!/bin/sh
# Use the norminette but without all these rules that make coding harder.
norminette | grep -v \
"INVALID_HEADER
VAR_DECL_START_FUNC
EMPTY_LINE_FUNCTION
TOO_MANY_ARGS
TOO_MANY_FUNCS
TOO_MANY_LINES
PREPROC_CONSTANT
MACRO_FUNC_FORBIDDEN
GLOBAL_VAR_DETECTED
WRONG_SCOPE_COMMENT
MULT_DECL_LINE
GOTO_FBIDDEN
LABEL_FBIDDEN
TERNARY_FBIDDEN"

View file

@ -33,7 +33,6 @@
#ifndef MLX_H #ifndef MLX_H
# define MLX_H # define MLX_H
# include "mlx3ds_3dskey.h"
# include "mlx3ds_typealiases.h" # include "mlx3ds_typealiases.h"
# include "mlx_init.h" # include "mlx_init.h"
# include "mlx_window.h" # include "mlx_window.h"

View file

@ -1,95 +0,0 @@
/**
* mlx3ds_3dskey.h
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
/**
* Enum of all the keys of the 3ds.
*
* These are the same constants defined in hid.h from devkitPro,
* but with documentation that basically repeat what the name
* of the constants already say.
*/
#ifndef MLX3DS_3DSKEY_H
# define MLX3DS_3DSKEY_H
# define BIT(n) 1U << n
/// @brief Represents a key of the 3DS.
typedef enum e_3dskey
{
/* BUTTONS */
/// @brief A button
KEY_A = BIT(0),
/// @brief B button
KEY_B = BIT(1),
/// @brief X button
KEY_X = BIT(10),
/// @brief Y button
KEY_Y = BIT(11),
/// @brief Select button
KEY_SELECT = BIT(2),
/// @brief Start button
KEY_START = BIT(3),
/// @brief R button
KEY_R = BIT(8),
/// @brief L button
KEY_L = BIT(9),
/* DIRECTIONS */
/// @brief D-Pad Right
KEY_DRIGHT = BIT(4),
/// @brief D-Pad Left
KEY_DLEFT = BIT(5),
/// @brief D-Pad Up
KEY_DUP = BIT(6),
/// @brief D-Pad Down
KEY_DDOWN = BIT(7),
/// @brief C-Pad Right
KEY_CPAD_RIGHT= BIT(28),
/// @brief C-Pad Left
KEY_CPAD_LEFT = BIT(29),
/// @brief C-Pad Up
KEY_CPAD_UP = BIT(30),
/// @brief C-Pad Down
KEY_CPAD_DOWN = BIT(31),
/* TOUCH */
// ??? unsure what KEY_TOUCH is... TODO
KEY_TOUCH = BIT(20),
/* New 3DS */
/// @brief C-Stick Right (only for New 3DS)
KEY_CSTICK_RIGHT = BIT(24),
/// @brief C-Stick Left (only for New 3DS)
KEY_CSTICK_LEFT = BIT(25),
/// @brief C-Stick Up (only for New 3DS)
KEY_CSTICK_UP = BIT(26),
/// @brief C-Stick Down (only for New 3DS)
KEY_CSTICK_DOWN = BIT(27),
/// @brief ZL button (only for New 3DS)
KEY_ZL = BIT(14),
/// @brief ZR button (only for New 3DS)
KEY_ZR = BIT(15),
// TODO implement catch-all directions?
/*
// ???
KEY_UP = KEY_DUP | KEY_CPAD_UP, ///< D-Pad Up or Circle Pad Up
// ???
KEY_DOWN = KEY_DDOWN | KEY_CPAD_DOWN, ///< D-Pad Down or Circle Pad Down
// ???
KEY_LEFT = KEY_DLEFT | KEY_CPAD_LEFT, ///< D-Pad Left or Circle Pad Left
// ???
KEY_RIGHT = KEY_DRIGHT | KEY_CPAD_RIGHT, ///< D-Pad Right or Circle PadRight
*/
} t_3dskey;
#endif

View file

@ -27,7 +27,6 @@
# define MLX_HOOK_H # define MLX_HOOK_H
# include "mlx3ds_typealiases.h" # include "mlx3ds_typealiases.h"
# include "mlx3ds_3dskey.h"
/// @brief Doesn't do anything, the 3DS doesn't have a mouse. /// @brief Doesn't do anything, the 3DS doesn't have a mouse.
/// ///
@ -43,14 +42,14 @@ int _mlx_mouse_hook(t_win win_ptr, int (*funct_ptr)(), void *param);
/// @param win_ptr Window to affect. /// @param win_ptr Window to affect.
/// @param funct_ptr Function to call when the event occurs. `keycode` is the /// @param funct_ptr Function to call when the event occurs. `keycode` is the
/// key pressed (note that they are obviously different than /// key pressed (note that they are obviously different than
/// the keyboard ones, see 'mlx3ds_3ds_key.h')). /// the keyboard ones, use the KEY_* constants from hid.h)).
/// (TODO what was the name of the key constants use with mlx?) /// (TODO what was the name of the key constants use with mlx?)
/// `param` is set as the address given to mlx_key_hook(). /// `param` is set as the address given to mlx_key_hook().
/// The return value is unused. /// The return value is unused.
/// @param param Address to pass to the function every time it is called. /// @param param Address to pass to the function every time it is called.
/// @return Unused. /// @return Unused.
int _mlx_key_hook(t_win win_ptr, int _mlx_key_hook(t_win win_ptr,
int (*funct_ptr)(t_3dskey keycode, void *param), void *param); int (*funct_ptr)(int keycode, void *param), void *param);
/// @brief Assign a function which will be called when the window should be /// @brief Assign a function which will be called when the window should be
/// redrawn. /// redrawn.

View file

@ -37,22 +37,23 @@
/// @param width Desired width of the image. /// @param width Desired width of the image.
/// @param height Desired height of the image. /// @param height Desired height of the image.
/// @return A reference to the image. NULL if failed. /// @return A reference to the image. NULL if failed.
t_image _mlx_new_image(void *mlx_ptr, int width, int height); t_image mlx_new_image(void *mlx_ptr, int width, int height);
/// @brief Get an address containing all the pixel values of the image. The user /// @brief Get an address containing all the pixel values of the image. The user
/// can then modify the image using this pointer directly. /// can then modify the image using this pointer directly.
/// ///
/// @param img_ptr Image to use. /// @param img_ptr Image to use.
/// @param bits_per_pixel Will be filled with the number of bits used for each /// @param bits_per_pixel Will be filled with the number of bits used for each
/// pixel. Will always be 24. /// pixel. Can be NULL, result will always be 24.
/// @param size_line Will be filled with the number of bytes used to store /// @param size_line Will be filled with the number of bytes used to store
/// a line of the image, aka 3 * width. /// a line of the image, aka 3 * width. Can be NULL.
/// @param endian Will be filled with the endianness of all color values. /// @param endian Will be filled with the endianness of all color values.
/// 1 for big endian, 0 for little endian. Will always be 1. /// 1 for big endian, 0 for little endian. Can be NULL, result
/// will always be 1.
/// @return Address to the data of the image. /// @return Address to the data of the image.
/// Use `data[y * size_line + x * 3]` to get the first color value (red) /// Use `data[y * size_line + x * 3]` to get the first color value (red)
/// of a specific pixel. /// of a specific pixel.
char *_mlx_get_data_addr(t_image img_ptr, int *bits_per_pixel, char *mlx_get_data_addr(t_image img_ptr, int *bits_per_pixel,
int *size_line, int *endian); int *size_line, int *endian);
/// @brief Draw an image on the window. /// @brief Draw an image on the window.
@ -63,7 +64,7 @@ char *_mlx_get_data_addr(t_image img_ptr, int *bits_per_pixel,
/// @param x x position of the image to draw. /// @param x x position of the image to draw.
/// @param y y position of the image to draw. /// @param y y position of the image to draw.
/// @return Unused. /// @return Unused.
int _mlx_put_image_to_window(t_mlx mlx_ptr, t_win win_ptr, t_image img_ptr, int mlx_put_image_to_window(t_mlx mlx_ptr, t_win win_ptr, t_image img_ptr,
int x, int y); int x, int y);
// ??? // ???
@ -79,6 +80,6 @@ t_image _mlx_xpm_file_to_image(void *mlx_ptr, char *filename,
/// @param mlx_ptr mlx connection identifier returned by mlx_init(). /// @param mlx_ptr mlx connection identifier returned by mlx_init().
/// @param img_ptr Image to destroy. /// @param img_ptr Image to destroy.
/// @return Unused. /// @return Unused.
int _mlx_destroy_image(t_mlx mlx_ptr, t_image img_ptr); int mlx_destroy_image(t_mlx mlx_ptr, t_image img_ptr);
#endif #endif

View file

@ -35,6 +35,8 @@
/// ///
/// @return The mlx connection identifier to use for all other functions. /// @return The mlx connection identifier to use for all other functions.
/// NULL if failed. /// NULL if failed.
t_mlx _mlx_init(void); // TODO should call this before REALLY anything else? t_mlx mlx_init(void); // TODO should call this before REALLY anything else?
// TODO a mlx_end() necessary?
#endif #endif

View file

@ -41,7 +41,7 @@
/// @param y y position of the pixel to set. /// @param y y position of the pixel to set.
/// @param color Color to use in 0x00RRGGBB format. /// @param color Color to use in 0x00RRGGBB format.
/// @return Unused. /// @return Unused.
int _mlx_pixel_put(t_mlx mlx_ptr, t_win win_ptr, int x, int y, int color); int mlx_pixel_put(t_mlx mlx_ptr, t_win win_ptr, int x, int y, int color);
/// @brief Print text on the window. /// @brief Print text on the window.
/// ///

View file

@ -41,20 +41,21 @@
/// Maximum 240 to be able to adapt to the top screen of the 3DS. /// Maximum 240 to be able to adapt to the top screen of the 3DS.
/// @param title The title of the window. Unused in the 3DS. /// @param title The title of the window. Unused in the 3DS.
/// @return A reference to the window. NULL if failed. /// @return A reference to the window. NULL if failed.
t_win _mlx_new_window(t_mlx mlx_ptr, int size_x, int size_y, char *title); t_win mlx_new_window(t_mlx mlx_ptr, int size_x, int size_y,
const char *title);
/// @brief Clear the window in black. /// @brief Clear the window in black.
/// ///
/// @param mlx_ptr mlx connection identifier returned by mlx_init(). /// @param mlx_ptr mlx connection identifier returned by mlx_init().
/// @param win_ptr Window to affect. /// @param win_ptr Window to affect.
/// @return Unused. /// @return Unused.
int _mlx_clear_window(t_mlx mlx_ptr, t_win win_ptr); int mlx_clear_window(t_mlx mlx_ptr, t_win win_ptr);
/// @brief Destroy a window. /// @brief Destroy a window.
/// ///
/// @param mlx_ptr mlx connection identifier returned by mlx_init(). /// @param mlx_ptr mlx connection identifier returned by mlx_init().
/// @param win_ptr Window to destroy. /// @param win_ptr Window to destroy.
/// @return Unused. /// @return Unused.
int _mlx_destroy_window(t_mlx mlx_ptr, t_win win_ptr); int mlx_destroy_window(t_mlx mlx_ptr, t_win win_ptr);
#endif #endif

52
include/utilsconsole.hpp Normal file
View file

@ -0,0 +1,52 @@
/**
* utilsconsole.hpp
* by Zy
*/
/**
* utilsconsole.hpp (uc_*() functions)
* Utility functions for the 3DS console.
*/
#pragma once
#include <string>
#include <vector>
using namespace std;
/// @brief To use with uc_menu(): Represent an element of the menu.
typedef struct {
/// @brief Displayed name.
string name;
/// @brief true if this button can be used to change the value of a number.
bool is_number_prompt;
/// @brief Only if is_number_prompt is true: the number to change.
int *value;
/// @brief Only if is_number_prompt is true: the minimum value possible for
/// value.
int min;
/// @brief Only if is_number_prompt is true: the maximum value possible for
/// value.
int max;
} s_uc_menu_element;
/// @brief Display a menu with several items in the console.
/// @details
/// Non-blocking function, must be called in loop.
/// Only one menu can work at the same time. 'elements' must not change in
/// address.
/// @param elements A pointer to a vector of all the elements of the menu.
/// @return The index of the confirmed element. If none, return -1.
int uc_menu(vector<s_uc_menu_element> &elements);
/// @brief Display a menu quickly using only confirm buttons.
/// @details
/// Blocking function. Will return -1 if aptMainLoop() becomes false.
/// @param str Text representing each element. The last parameter must be NULL.
/// @return The index of the confirmed element.
int uc_menu_quick(const char *str, ...);
/// @brief Blocks the program until the user presses A.
/// @details Will also return if aptMainLoop() becomes false.
void uc_pause(void);

View file

@ -1,25 +1,182 @@
#include <3ds.h> extern "C" {
#include <stdio.h> #include "mlx.h"
#include "3ds.h"
int main(int argc, char **argv) {
gfxInitDefault();
consoleInit(GFX_TOP, NULL);
printf("Test Code::Blocks project!");
printf("\x1b[20;15HPress Start to exit.");
while(aptMainLoop()) {
gspWaitForVBlank();
hidScanInput();
if(hidKeysDown() & KEY_START)
break;
gfxFlushBuffers();
gfxSwapBuffers();
} }
#include <iostream>
#include <string.h>
#include "utilsconsole.hpp"
using namespace std;
static void draw_rainbow_image(t_mlx mlx, t_win win, int x, int y)
{
cout << "Draw rainbow image at (" << x << "," << y << ")..." << endl;
uc_pause();
{
static const u32 red = 0xFF0000;
static const u32 green = 0x00FF00;
static const u32 blue = 0x0000FF;
t_image img = mlx_new_image(mlx, 30, 30);
char *img_data = mlx_get_data_addr(img, NULL, NULL, NULL);
for (int x = 0; x < 30; x++)
{
for (int y = 0; y < 20; y++)
{
if (x < 10)
memcpy(img_data + 3 * (y * 30 + x), &red, 3);
else if (x < 20)
memcpy(img_data + 3 * (y * 30 + x), &green, 3);
else
memcpy(img_data + 3 * (y * 30 + x), &blue, 3);
}
}
mlx_put_image_to_window(mlx, win, img, x, y);
mlx_destroy_image(mlx, img);
}
}
int main(void) {
void *const mlx = mlx_init();
void *win;
// MENU
{
switch (uc_menu_quick("pixels", "images", "quit", NULL))
{
case 0:
goto pixels;
break;
case 1:
goto images;
break;
}
goto end;
}
pixels:
cout << "create window..." << endl;
uc_pause();
win = mlx_new_window(mlx, 400, 240, "hey");
cout << "clear window..." << endl;
uc_pause();
mlx_clear_window(mlx, win);
cout
<< "Put pixels for red green and blue" << endl
<< "at the top-left corner..." << endl;
uc_pause();
{
int block_y = 20;
for (
int block_x = 20, color = 0xFF0000;
block_x <= 60;
block_x += 20, color = color >> 8
)
for (int x = 0; x < 10; x++)
for (int y = 0; y < 10; y++)
mlx_pixel_put(mlx, win, block_x + x, block_y + y, color);
}
cout << "clear window..." << endl;
uc_pause();
mlx_clear_window(mlx, win);
cout << "destroy window..." << endl;
uc_pause();
mlx_destroy_window(mlx, win);
goto end;
images:
cout << "create window..." << endl;
uc_pause();
win = mlx_new_window(mlx, 400, 240, "hoi");
cout << "clear window..." << endl;
uc_pause();
mlx_clear_window(mlx, win);
draw_rainbow_image(mlx, win, 185, 110);
draw_rainbow_image(mlx, win, -15, 100);
draw_rainbow_image(mlx, win, 385, 120);
draw_rainbow_image(mlx, win, 170, -10);
draw_rainbow_image(mlx, win, 200, 230);
draw_rainbow_image(mlx, win, -15, -10);
draw_rainbow_image(mlx, win, 385, -10);
draw_rainbow_image(mlx, win, -15, 230);
draw_rainbow_image(mlx, win, 385, 230);
cout << "Draw RG image at (20,10)..." << endl;
uc_pause();
{
t_image img = mlx_new_image(mlx, 30, 20);
char *img_data = mlx_get_data_addr(img, NULL, NULL, NULL);
for (int x = 0; x < 30; x++)
{
for (int y = 0; y < 20; y++)
{
u32 color =
((0xFF0000 * x / 30) & 0xFF0000)
| ((0x00FF00 * y / 20) & 0x00FF00);
memcpy(img_data + 3 * (y * 30 + x), &color, 3);
}
}
mlx_put_image_to_window(mlx, win, img, 20, 10);
mlx_destroy_image(mlx, img);
}
cout << "Draw GB image at (50,0)..." << endl;
uc_pause();
{
t_image img = mlx_new_image(mlx, 10, 50);
char *img_data = mlx_get_data_addr(img, NULL, NULL, NULL);
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 50; y++)
{
u32 color =
((0x00FF00 * x / 10) & 0x00FF00)
| ((0x0000FF * y / 50) & 0x0000FF);
memcpy(img_data + 3 * (y * 10 + x), &color, 3);
}
}
mlx_put_image_to_window(mlx, win, img, 50, 0);
mlx_destroy_image(mlx, img);
}
cout << "Draw BR image at (200,100)..." << endl;
uc_pause();
{
t_image img = mlx_new_image(mlx, 100, 100);
char *img_data = mlx_get_data_addr(img, NULL, NULL, NULL);
for (int x = 0; x < 100; x++)
{
for (int y = 0; y < 100; y++)
{
u32 color =
((0x0000FF * x / 100) & 0x0000FF)
| ((0xFF0000 * y / 100) & 0xFF0000);
memcpy(img_data + 3 * (y * 100 + x), &color, 3);
}
}
mlx_put_image_to_window(mlx, win, img, 200, 100);
mlx_destroy_image(mlx, img);
}
cout << "clear window..." << endl;
uc_pause();
mlx_clear_window(mlx, win);
cout << "destroy window..." << endl;
uc_pause();
mlx_destroy_window(mlx, win);
goto end;
end:
cout << "Exit..." << endl;
uc_pause();
gfxExit(); gfxExit();
return 0;
} }

123
source/mlx_image.c Normal file
View file

@ -0,0 +1,123 @@
/**
* mlx_image.c
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
#include "mlx_image.h"
#include <stdlib.h>
#include "mlx_internal.h"
#include "mlx_inline.c"
t_image mlx_new_image(void *mlx_ptr, int width, int height)
{
t_internal_image *r;
(void)mlx_ptr;
r = NULL;
r = malloc(sizeof(t_internal_image));
if (!r)
goto error;
r->data = NULL;
r->data = malloc(width * height * 3 * sizeof(u8));
if (!r->data)
goto error;
r->width = width;
r->height = height;
return (r);
error:
free(r);
return (NULL);
}
int mlx_destroy_image(t_mlx mlx_ptr, t_image img_ptr)
{
if (img_ptr)
free(((t_internal_image *)img_ptr)->data);
free(img_ptr);
return (0);
}
char *mlx_get_data_addr(t_image img_ptr, int *bits_per_pixel,
int *size_line, int *endian)
{
if (bits_per_pixel)
*bits_per_pixel = 24;
if (size_line)
*size_line = 3 * ((t_internal_image *)img_ptr)->width;
if (endian)
*endian = 1;
return ((char *)((t_internal_image *)img_ptr)->data);
}
/// @brief Return the minimum between a and b.
/// @param a Number a.
/// @param b Number b.
/// @return The result.
static inline int min(int a, int b)
{
return (a < b ? a : b);
}
/// @brief Return the maximum between a and b.
/// @param a Number a.
/// @param b Number b.
/// @return The result.
static inline int max(int a, int b)
{
return (a > b ? a : b);
}
int mlx_put_image_to_window(t_mlx mlx_ptr, t_win win_ptr, t_image img_ptr,
int x, int y)
{
// buffers
u8 *frame_data, *img_data;
int // dimensions
frame_width, frame_height, img_width, img_height,
// interval of pixels to draw, to avoid drawing outside of the buffer
min_x_on_screen, max_x_on_screen, min_y_on_screen, max_y_on_screen,
// position relative to frame
x_on_screen, y_on_screen;
// variables that contain results of constant expressions
int a, b;
u8 *c, *d, *e, *f;
mlx3ds_internal_drawstart(mlx_ptr, win_ptr);
frame_data = ((t_internal_win *)win_ptr)->framebuffer;
frame_width = ((t_internal_win *)win_ptr)->framebuffer_width;
frame_height = ((t_internal_win *)win_ptr)->framebuffer_height;
img_data = ((t_internal_image *)img_ptr)->data;
img_width = ((t_internal_image *)img_ptr)->width;
img_height = ((t_internal_image *)img_ptr)->height;
min_x_on_screen = max(x, 0);
max_x_on_screen = min(x + img_width, frame_width);
min_y_on_screen = max(y, 0);
max_y_on_screen = min(y + img_height, frame_height);
a = 3 * img_width;
b = 3 * frame_height;
c = img_data - 3 * x - a * y;
d = frame_data - 3 + b;
x_on_screen = min_x_on_screen;
while (x_on_screen < max_x_on_screen)
{
e = c + 3 * x_on_screen;
f = d + b * x_on_screen;
y_on_screen = min_y_on_screen;
while (y_on_screen < max_y_on_screen)
{
mlx_inline_drawcolor(
f - 3 * y_on_screen,
e + a * y_on_screen);
y_on_screen++;
}
x_on_screen++;
}
mlx3ds_internal_drawend(mlx_ptr, win_ptr);
return (0);
}

29
source/mlx_init.c Normal file
View file

@ -0,0 +1,29 @@
/**
* mlx_init.c
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
#include "mlx_init.h"
#include "mlx_internal.h"
#include <stdlib.h>
#include "3ds.h"
static t_internal_mlx g_internal_mlx = {
.is_init_called = false,
.top_window = NULL,
};
t_mlx mlx_init(void)
{
if (g_internal_mlx.is_init_called)
mlx3ds_internal_fatalerror("mlx_init() must be called only once");
g_internal_mlx.is_init_called = true;
gfxInitDefault();
gfxSetDoubleBuffering(GFX_TOP, false);
gfxSetDoubleBuffering(GFX_BOTTOM, false);
consoleInit(GFX_BOTTOM, NULL);
return (&g_internal_mlx);
}

31
source/mlx_inline.c Normal file
View file

@ -0,0 +1,31 @@
/**
* mlx_inline.c
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
#include "3ds.h"
/// @brief Write a color at a specific location of the memory,
/// the 3 color values will be in the same order.
/// It's basically a memcpy of 3 bytes.
/// @param dest Address to the first color value of the destination.
/// @param src Address to the first color value of the source.
inline void mlx_inline_drawcolor(u8 *dest, u8 *src)
{
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
}
/// @brief Write a color at a specific location of the memory,
/// the 3 color values will be in opposite order.
/// @param dest Address to the first color value of the destination.
/// @param src Address to the first color value of the source.
inline void mlx_inline_drawcolor_invert(u8 *dest, u8 *src)
{
dest[2] = src[0];
dest[1] = src[1];
dest[0] = src[2];
}

35
source/mlx_internal.cpp Normal file
View file

@ -0,0 +1,35 @@
/**
* mlx_internal.cpp
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
#include "utilsconsole.hpp"
extern "C" {
#include "mlx_internal.h"
#include "3ds.h"
void mlx3ds_internal_fatalerror(const char *msg)
{
consoleInit(GFX_BOTTOM, NULL);
printf("\e[0;31m[mlx3ds] Fatal error:\e[0m\n%s\n", msg);
uc_pause();
}
void mlx3ds_internal_drawstart(
t_internal_mlx *mlx_ptr, t_internal_win *win_ptr)
{
win_ptr->framebuffer = gfxGetFramebuffer(GFX_TOP, GFX_LEFT,
&win_ptr->framebuffer_height, &win_ptr->framebuffer_width);
gspWaitForVBlank();
}
void mlx3ds_internal_drawend(
t_internal_mlx *mlx_ptr, t_internal_win *win_ptr)
{
gfxFlushBuffers();
gfxSwapBuffers();
}
}

71
source/mlx_internal.h Normal file
View file

@ -0,0 +1,71 @@
/**
* mlx_internal.c
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
/**
* Internal utils of the mlx3ds.
*/
#include <stdbool.h>
#include "3ds.h"
/// @brief Write an error message and exit the program.
///
/// @param msg Message to write. Must use less than 38 colons.
void mlx3ds_internal_fatalerror(const char *msg);
typedef struct s_internal_win t_internal_win;
/// @brief Content of the mlx connection identifier.
typedef struct s_internal_mlx
{
/// @brief is true after mlx_init() was called, is false before.
bool is_init_called;
/// @brief Window displayed on the top screen.
t_internal_win *top_window;
} t_internal_mlx;
/// @brief Represents a window.
typedef struct s_internal_win
{
/// @brief mlx connection identifier
t_internal_mlx *mlx;
/// @brief Width of the window
int width;
/// @brief Height of the window
int height;
/// @brief Buffer of the screen.
u8 *framebuffer;
/// @brief Width of the screen (on x).
u16 framebuffer_width;
/// @brief Height of the screen (on y).
u16 framebuffer_height;
} t_internal_win;
/// @brief Represents an image in memory.
typedef struct s_internal_image
{
/// @brief Width of the image
int width;
/// @brief Height of the image
int height;
/// @brief Address of the image data. Returned by mlx_get_data_addr().
u8 *data;
} t_internal_image;
/// @brief To call before drawing. Init winptr->framebuffer.
///
/// @param mlx_ptr mlx connection identifier
/// @param win_ptr Reference to the window to draw on.
void mlx3ds_internal_drawstart(
t_internal_mlx *mlx_ptr, t_internal_win *win_ptr);
/// @brief To call when finish drawing. Flush and swap buffers.
///
/// @param mlx_ptr mlx connection identifier
/// @param win_ptr Reference to the window to draw on.
void mlx3ds_internal_drawend(
t_internal_mlx *mlx_ptr, t_internal_win *win_ptr);

32
source/mlx_put.c Normal file
View file

@ -0,0 +1,32 @@
/**
* mlx_put.c
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
#include "mlx_put.h"
#include "mlx_internal.h"
#include "mlx_inline.c"
int mlx_pixel_put(t_mlx mlx_ptr, t_win win_ptr, int x, int y, int color)
{
u8 *frame;
mlx3ds_internal_drawstart(mlx_ptr, win_ptr);
if (x < 0 || x >= ((t_internal_win *)win_ptr)->framebuffer_width
|| y < 0 || y >= ((t_internal_win *)win_ptr)->framebuffer_height)
return (0);
frame = ((t_internal_win *)win_ptr)->framebuffer;
mlx_inline_drawcolor(
frame + 3 * (
(x + 1) * ((t_internal_win *)win_ptr)->framebuffer_height - y - 1
),
(u8 *)&color);
mlx3ds_internal_drawend(mlx_ptr, win_ptr);
return (0);
}

52
source/mlx_window.c Normal file
View file

@ -0,0 +1,52 @@
/**
* mlx_window.c
* for the project "MinilibX for 3DS"
* by Zy
* at https://github.com/frzysk/mlx3ds
*/
#include "mlx_window.h"
#include "3ds.h"
#include "mlx_internal.h"
#include <stdlib.h>
t_win mlx_new_window(t_mlx mlx_ptr, int size_x, int size_y, const char *title)
{
t_internal_win *r;
(void)title;
if (size_x > GSP_SCREEN_HEIGHT_TOP || size_y > GSP_SCREEN_WIDTH)
mlx3ds_internal_fatalerror("Window too big for the screen");
r = malloc(sizeof(t_internal_win));
if (!r)
return (NULL);
r->mlx = mlx_ptr;
r->width = size_x;
r->height = size_y;
r->framebuffer = NULL;
return (r);
}
int mlx_clear_window(t_mlx mlx_ptr, t_win win_ptr)
{
u8 *frame_i;
u8 *frame;
mlx3ds_internal_drawstart(mlx_ptr, win_ptr);
frame = ((t_internal_win *)win_ptr)->framebuffer;
frame_i = frame
+ 3 * ((t_internal_win *)win_ptr)->framebuffer_width
* ((t_internal_win *)win_ptr)->framebuffer_height;
while (--frame_i >= frame)
*frame_i = 0x00;
mlx3ds_internal_drawend(mlx_ptr, win_ptr);
return (0);
}
int mlx_destroy_window(t_mlx mlx_ptr, t_win win_ptr)
{
((t_internal_mlx *)mlx_ptr)->top_window = NULL;
free(win_ptr);
return (0);
}

93
source/utilsconsole.cpp Normal file
View file

@ -0,0 +1,93 @@
/**
* utilsconsole.cpp
* by Zy
*/
#include "utilsconsole.hpp"
#include <3ds.h>
#include <iostream>
#include <stdarg.h>
int uc_menu(vector<s_uc_menu_element> &elements) {
static void *lastElements = NULL;
static size_t selected = 0;
bool confirmed = false;
if (&elements != lastElements) {
// init if new menu
consoleClear();
lastElements = &elements;
selected = 0;
}
else {
// read inputs
hidScanInput();
if (hidKeysDown() & KEY_UP) {
if (selected-- == 0)
selected += elements.size();
}
else if (hidKeysDown() & KEY_DOWN) {
if (++selected >= elements.size())
selected -= elements.size();
}
else if (!elements[selected].is_number_prompt) {
if (hidKeysDown() & KEY_A) {
confirmed = true;
lastElements = NULL;
}
}
else {
if (hidKeysDown() & KEY_LEFT)
if (*elements[selected].value > elements[selected].min)
(*elements[selected].value)--;
if (hidKeysDown() & KEY_RIGHT)
if (*elements[selected].value < elements[selected].max)
(*elements[selected].value)++;
}
}
// display
cout << "\e[H\e[0m";
for (size_t i = 0; i < elements.size(); i++) {
cout << (i == selected && !confirmed ? "\e[7m" : "")
<< elements[i].name << "\e[0m";
if (elements[i].is_number_prompt)
cout
<< ((*(elements[i].value) == elements[i].min) ? " " : " <")
<< *(elements[i].value)
<< ((*(elements[i].value) == elements[i].max) ? " " : ">");
cout << "\e[0K" << endl;
}
if (!confirmed)
cout << "\e[2m" << "[Up/Down] Move / "
<< (elements[selected].is_number_prompt ?
"[Left/Right] Modify" : "(A) Confirm") << "\e[0K" << endl;
else
cout << "\e[2mOK.\e[0K\e[0m" << endl;
return confirmed ? selected : -1;
}
int uc_menu_quick(const char *str, ...) {
va_list arguments;
auto elements = vector<s_uc_menu_element>();
for (va_start(arguments, str); str; str = va_arg(arguments, const char *))
elements.push_back({str, false, NULL, 0, 0});
int r = -1;
while (r < 0 && aptMainLoop())
r = uc_menu(elements);
return r;
}
void uc_pause(void) {
cout << "\e[0;2mPress (A) to continue...\e[0m" << endl;
while (aptMainLoop()) {
hidScanInput();
if (hidKeysDown() & KEY_A)
break;
gfxFlushBuffers();
}
}