diff --git a/Makefile b/Makefile index b160df8..7770c71 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ RSF := $(TOPDIR)/$(RESOURCES)/template.rsf # options for code generation #--------------------------------------------------------------------------------- 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 CXXFLAGS := $(COMMON) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := $(ARCH) @@ -343,3 +343,27 @@ endef #--------------------------------------------------------------------------------------- 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 diff --git a/dev/norm.sh b/dev/norm.sh new file mode 100755 index 0000000..95e39dd --- /dev/null +++ b/dev/norm.sh @@ -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" diff --git a/include/mlx.h b/include/mlx.h index 220a7be..dde4bf4 100644 --- a/include/mlx.h +++ b/include/mlx.h @@ -33,7 +33,6 @@ #ifndef MLX_H # define MLX_H -# include "mlx3ds_3dskey.h" # include "mlx3ds_typealiases.h" # include "mlx_init.h" # include "mlx_window.h" diff --git a/include/mlx3ds_3dskey.h b/include/mlx3ds_3dskey.h deleted file mode 100644 index ce85936..0000000 --- a/include/mlx3ds_3dskey.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/include/mlx_hook.h b/include/mlx_hook.h index a449268..847eda9 100644 --- a/include/mlx_hook.h +++ b/include/mlx_hook.h @@ -27,7 +27,6 @@ # define MLX_HOOK_H # include "mlx3ds_typealiases.h" -# include "mlx3ds_3dskey.h" /// @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 funct_ptr Function to call when the event occurs. `keycode` is the /// 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?) /// `param` is set as the address given to mlx_key_hook(). /// The return value is unused. /// @param param Address to pass to the function every time it is called. /// @return Unused. 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 /// redrawn. @@ -90,4 +89,4 @@ int _mlx_loop_end(void *mlx_ptr); int _mlx_hook(t_mlx win_ptr, int x_event, int x_mask, int (*funct)(), void *param); -#endif \ No newline at end of file +#endif diff --git a/include/mlx_image.h b/include/mlx_image.h index 4a626c1..545920f 100644 --- a/include/mlx_image.h +++ b/include/mlx_image.h @@ -37,22 +37,23 @@ /// @param width Desired width of the image. /// @param height Desired height of the image. /// @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 /// can then modify the image using this pointer directly. /// /// @param img_ptr Image to use. /// @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 -/// 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. -/// 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. /// Use `data[y * size_line + x * 3]` to get the first color value (red) /// 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); /// @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 y y position of the image to draw. /// @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); // ??? @@ -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 img_ptr Image to destroy. /// @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 \ No newline at end of file +#endif diff --git a/include/mlx_init.h b/include/mlx_init.h index c8f2dda..e424af0 100644 --- a/include/mlx_init.h +++ b/include/mlx_init.h @@ -35,6 +35,8 @@ /// /// @return The mlx connection identifier to use for all other functions. /// 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? -#endif \ No newline at end of file +// TODO a mlx_end() necessary? + +#endif diff --git a/include/mlx_put.h b/include/mlx_put.h index e5ade71..510653d 100644 --- a/include/mlx_put.h +++ b/include/mlx_put.h @@ -41,7 +41,7 @@ /// @param y y position of the pixel to set. /// @param color Color to use in 0x00RRGGBB format. /// @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. /// @@ -57,4 +57,4 @@ int _mlx_string_put(void *mlx_ptr, void *win_ptr, int x, int y, int color, // ??? void _mlx_set_font(void *mlx_ptr, void *win_ptr, char *name); -#endif \ No newline at end of file +#endif diff --git a/include/mlx_window.h b/include/mlx_window.h index 593b8cd..80aeca1 100644 --- a/include/mlx_window.h +++ b/include/mlx_window.h @@ -41,20 +41,21 @@ /// 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. /// @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. /// /// @param mlx_ptr mlx connection identifier returned by mlx_init(). /// @param win_ptr Window to affect. /// @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. /// /// @param mlx_ptr mlx connection identifier returned by mlx_init(). /// @param win_ptr Window to destroy. /// @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 \ No newline at end of file +#endif diff --git a/include/utilsconsole.hpp b/include/utilsconsole.hpp new file mode 100644 index 0000000..09e3ddd --- /dev/null +++ b/include/utilsconsole.hpp @@ -0,0 +1,52 @@ +/** + * utilsconsole.hpp + * by Zy + */ + +/** + * utilsconsole.hpp (uc_*() functions) + * Utility functions for the 3DS console. + */ + +#pragma once + +#include +#include + +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 &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); diff --git a/source/main.cpp b/source/main.cpp index d01f026..9bf999d 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,25 +1,182 @@ -#include <3ds.h> -#include - -int main(int argc, char **argv) { - gfxInitDefault(); - consoleInit(GFX_TOP, NULL); - - printf("Test Code::Blocks project!"); +extern "C" { +#include "mlx.h" +#include "3ds.h" +} +#include +#include +#include "utilsconsole.hpp" - printf("\x1b[20;15HPress Start to exit."); - - while(aptMainLoop()) { - gspWaitForVBlank(); - hidScanInput(); - - if(hidKeysDown() & KEY_START) - break; - - gfxFlushBuffers(); - gfxSwapBuffers(); - } - - gfxExit(); - return 0; -} +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(); +} diff --git a/source/mlx_image.c b/source/mlx_image.c new file mode 100644 index 0000000..5fe387a --- /dev/null +++ b/source/mlx_image.c @@ -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 +#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); +} diff --git a/source/mlx_init.c b/source/mlx_init.c new file mode 100644 index 0000000..0d50bd0 --- /dev/null +++ b/source/mlx_init.c @@ -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 +#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); +} diff --git a/source/mlx_inline.c b/source/mlx_inline.c new file mode 100644 index 0000000..9f7c28d --- /dev/null +++ b/source/mlx_inline.c @@ -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]; +} diff --git a/source/mlx_internal.cpp b/source/mlx_internal.cpp new file mode 100644 index 0000000..3a5f5b4 --- /dev/null +++ b/source/mlx_internal.cpp @@ -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(); +} +} diff --git a/source/mlx_internal.h b/source/mlx_internal.h new file mode 100644 index 0000000..5844c1a --- /dev/null +++ b/source/mlx_internal.h @@ -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 +#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); diff --git a/source/mlx_put.c b/source/mlx_put.c new file mode 100644 index 0000000..d5ddb0c --- /dev/null +++ b/source/mlx_put.c @@ -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); +} diff --git a/source/mlx_window.c b/source/mlx_window.c new file mode 100644 index 0000000..aec4586 --- /dev/null +++ b/source/mlx_window.c @@ -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 + +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); +} diff --git a/source/utilsconsole.cpp b/source/utilsconsole.cpp new file mode 100644 index 0000000..5706453 --- /dev/null +++ b/source/utilsconsole.cpp @@ -0,0 +1,93 @@ +/** + * utilsconsole.cpp + * by Zy + */ + +#include "utilsconsole.hpp" + +#include <3ds.h> +#include +#include + +int uc_menu(vector &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(); + 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(); + } +} \ No newline at end of file