diff --git a/include/mlx_events.h b/include/mlx_events.h new file mode 100644 index 0000000..3f1a0ab --- /dev/null +++ b/include/mlx_events.h @@ -0,0 +1,120 @@ +/** + * mlx_events.h + * for the project "MinilibX for 3DS" + * by Zy + * at https://github.com/frzysk/mlx3ds + * + * The content of this file comes from "X.h". + */ + +/** + * This file contains the event and mask values to use with mlx_hook(). + * The mask values are here for compatibility, they aren't used for the 3DS. + */ + +#ifndef MLX_EVENTS_H +# define MLX_EVENTS_H + +// TODO docs mlx_events.h +// TODO add function prototypes. + +/// @brief Sent when a key starts to be pressed. +/// Hook prototype: `int keypress_hook(int keycode, void *param);` +# define KeyPress 2 +/// @brief Sent when a key ends being pressed. +/// Hook prototype: `int keyrelease_hook(int keycode, void *param);` +# define KeyRelease 3 +/// @brief Sent when the window was just created, before all other events. +/// Hook prototype: `int create_hook(void *param);` +# define CreateNotify 16 +/// @brief Sent to all the windows when the software is quitting, after all +/// the other events. +/// Hook prototype: `int destroy_hook(void *param);` +# define DestroyNotify 17 + +# define ButtonPress 4 // Unused, this event will never be sent. +# define ButtonRelease 5 // Unused, this event will never be sent. +# define MotionNotify 6 // Unused, this event will never be sent. +# define EnterNotify 7 // Unused, this event will never be sent. +# define LeaveNotify 8 // Unused, this event will never be sent. +# define FocusIn 9 // Unused, this event will never be sent. +# define FocusOut 10 // Unused, this event will never be sent. +# define KeymapNotify 11 // Unused, this event will never be sent. +# define Expose 12 // Unused, this event will never be sent. +# define GraphicsExpose 13 // Unused, this event will never be sent. +# define NoExpose 14 // Unused, this event will never be sent. +# define VisibilityNotify 15 // Unused, this event will never be sent. +# define UnmapNotify 18 // Unused, this event will never be sent. +# define MapNotify 19 // Unused, this event will never be sent. +# define MapRequest 20 // Unused, this event will never be sent. +# define ReparentNotify 21 // Unused, this event will never be sent. +# define ConfigureNotify 22 // Unused, this event will never be sent. +# define ConfigureRequest 23 // Unused, this event will never be sent. +# define GravityNotify 24 // Unused, this event will never be sent. +# define ResizeRequest 25 // Unused, this event will never be sent. +# define CirculateNotify 26 // Unused, this event will never be sent. +# define CirculateRequest 27 // Unused, this event will never be sent. +# define PropertyNotify 28 // Unused, this event will never be sent. +# define SelectionClear 29 // Unused, this event will never be sent. +# define SelectionRequest 30 // Unused, this event will never be sent. +# define SelectionNotify 31 // Unused, this event will never be sent. +# define ColormapNotify 32 // Unused, this event will never be sent. +# define ClientMessage 33 // Unused, this event will never be sent. +# define MappingNotify 34 // Unused, this event will never be sent. +# define GenericEvent 35 // Unused, this event will never be sent. +# define LASTEvent 36 // A number bigger that all event values. + +// Unused, the MinilibX for 3DS doesn't use event masks. +# define NoEventMask 0L +// Unused, the MinilibX for 3DS doesn't use event masks. +# define KeyPressMask (1L<<0) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define KeyReleaseMask (1L<<1) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define ButtonPressMask (1L<<2) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define ButtonReleaseMask (1L<<3) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define EnterWindowMask (1L<<4) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define LeaveWindowMask (1L<<5) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define PointerMotionMask (1L<<6) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define PointerMotionHintMask (1L<<7) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define Button1MotionMask (1L<<8) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define Button2MotionMask (1L<<9) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define Button3MotionMask (1L<<10) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define Button4MotionMask (1L<<11) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define Button5MotionMask (1L<<12) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define ButtonMotionMask (1L<<13) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define KeymapStateMask (1L<<14) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define ExposureMask (1L<<15) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define VisibilityChangeMask (1L<<16) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define StructureNotifyMask (1L<<17) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define ResizeRedirectMask (1L<<18) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define SubstructureNotifyMask (1L<<19) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define SubstructureRedirectMask (1L<<20) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define FocusChangeMask (1L<<21) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define PropertyChangeMask (1L<<22) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define ColormapChangeMask (1L<<23) +// Unused, the MinilibX for 3DS doesn't use event masks. +# define OwnerGrabButtonMask (1L<<24) + +#endif diff --git a/include/mlx_hook.h b/include/mlx_hook.h index cea3161..a350869 100644 --- a/include/mlx_hook.h +++ b/include/mlx_hook.h @@ -27,6 +27,7 @@ # define MLX_HOOK_H # include "mlx3ds_typealiases.h" +# include "mlx_events.h" /// @brief Doesn't do anything, the 3DS doesn't have a mouse. /// @@ -34,10 +35,10 @@ /// @param funct_ptr Unused /// @param param Unused. /// @return Unused. -int _mlx_mouse_hook(t_win win_ptr, int (*funct_ptr)(), void *param); +int mlx_mouse_hook(t_win win_ptr, int (*funct_ptr)(), void *param); -/// @brief Assign a function to a key event. When a key is pressed, the function -/// will be called. +/// @brief Assign a function to a key event. When a key is released, the +/// function will be called. /// /// @param win_ptr Window to affect. /// @param funct_ptr Function to call when the event occurs. `keycode` is the @@ -47,7 +48,7 @@ int _mlx_mouse_hook(t_win win_ptr, int (*funct_ptr)(), void *param); /// 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 mlx_key_hook(t_win win_ptr, int (*funct_ptr)(int keycode, void *param), void *param); /// @brief Assign a function which will be called when the window should be @@ -60,7 +61,7 @@ int _mlx_key_hook(t_win win_ptr, /// unused. /// @param param Address to pass to the function every time it is called. /// @return Unused. -int _mlx_expose_hook(t_win win_ptr, +int mlx_expose_hook(t_win win_ptr, int (*funct_ptr)(void *param), void *param); /// @brief Assign a function which will be called in loop non-stop. @@ -71,21 +72,34 @@ int _mlx_expose_hook(t_win win_ptr, /// unused. /// @param param Address to pass to the function every time it is called. /// @return Unused. -int _mlx_loop_hook(t_mlx mlx_ptr, +int mlx_loop_hook(t_mlx mlx_ptr, int (*funct_ptr)(void *param), void *param); /// @brief Loop indefinitely and wait for events to occurs to call the /// corresponding functions. /// /// @param mlx_ptr mlx connection identifier returned by mlx_init(). -/// @return This function never returns. -int _mlx_loop(void *mlx_ptr); +/// @return This function returns only when the program is quitting or if +/// mlx_loop_end() is used. The return value is unused. +int mlx_loop(t_mlx mlx_ptr); -// ??? -int _mlx_loop_end(void *mlx_ptr); +/// @brief Stop the loop of mlx_loop. Doesn't do anything if mlx_loop isn't +/// running. +/// +/// @param mlx_ptr mlx connection identifier returned by mlx_init(). +/// @return Unused. +int mlx_loop_end(t_mlx mlx_ptr); -// god -int _mlx_hook(t_mlx win_ptr, int x_event, int x_mask, +/// @brief Assign a function to a custom event. +/// +/// @param win_ptr Window to affect. +/// @param x_event The event to use. "See mlx_events.h". +/// @param x_mask Unused. +/// @param funct Function to call when the event occurs. The prototype depends +/// on the event type, "see mlx_events.h". +/// @param param Address to pass to the function every time it is called. +/// @return Unused. +int mlx_hook(t_mlx win_ptr, int x_event, int x_mask, int (*funct)(), void *param); #endif diff --git a/include/mlx_init.h b/include/mlx_init.h index f6d99a7..1e24620 100644 --- a/include/mlx_init.h +++ b/include/mlx_init.h @@ -34,6 +34,4 @@ /// NULL if failed. t_mlx mlx_init(void); // TODO should call this before REALLY anything else? -// TODO a mlx_end() necessary? - #endif diff --git a/source/main.cpp b/source/main.cpp index e8f79f7..942ccef 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -40,6 +40,47 @@ static void draw_rainbow_image(t_mlx mlx, t_win win, int x, int y) } } +static int keydown_hook(int keycode, void *param) +{ + (void)param; + cout << "[D" << keycode << "] (B to continue)" << endl; + return (0); +}; + +static int create_hook(void *param) { + (void)param; + cout << "create_hook() called" << endl; + int file = open("__tmp_heya.txt", O_RDONLY); + if (!file) + cout << "can't open file __tmp_heya.txt" << endl; + else { + static char buf[100]; + if (read(file, buf, 100) < 0) + cout << "can't read file" << endl; + else + cout << "buf :\"" << buf << "\"" << endl; + } + close(file); + cout + << "quit software to create file:" << endl + << " __tmp_heya.txt" << endl; + return (0); +}; + +static int destroy_hook(void **ptrs) { + int file = open("__tmp_heya.txt", O_WRONLY | O_CREAT); + if (!file) { + cout << "can't create file" << endl; + return (0); + } + write(file, "spaghetti :3\n", 13); + if (!file) + cout << "can't write on file" << endl; + close(file); + mlx_loop_end(ptrs[0]); + return (0); +}; + int main(void) { void *const mlx = mlx_init(); void *win; @@ -47,7 +88,7 @@ int main(void) { // MENU { switch (uc_menu_quick("pixels", "images", "xpm", "files", - "navigate", "assets", "xpm files", "quit", NULL)) + "navigate", "assets", "xpm files", "events", "quit", NULL)) { case 0: goto pixels; @@ -70,6 +111,9 @@ int main(void) { case 6: goto xpm_files; break; + case 7: + goto events; + break; } goto end; } @@ -582,6 +626,59 @@ xpm_files: } goto end; +events: + { + win = mlx_new_window(mlx, 400, 240, NULL); + + const static auto loop_hook = [](void *param) -> int { + (void)param; + cout << "hi from loop_hook (B to continue)" << endl; + hidScanInput(); + if (hidKeysDown() & KEY_B) + mlx_loop_end(param); + return (0); + }; + mlx_loop_hook(mlx, loop_hook, mlx); + + mlx_loop(mlx); + cout << "mlx_loop() returned." << endl; + mlx_destroy_window(mlx, win); + mlx_loop_hook(mlx, NULL, NULL); + uc_pause(); + } + { + win = mlx_new_window(mlx, 400, 240, NULL); + + const static auto key_hook = [](int keycode, void *param) -> int { + cout << "[U" << keycode << "] (B to continue)" << endl; + if (keycode == KEY_B) + mlx_loop_end(param); + return (0); + }; + mlx_key_hook(win, key_hook, mlx); + + mlx_hook(win, KeyPress, 0, (int (*)())(void *)&keydown_hook, mlx); + + mlx_loop(mlx); + cout << "mlx_loop() returned." << endl; + mlx_destroy_window(mlx, win); + uc_pause(); + } + { + win = mlx_new_window(mlx, 400, 240, NULL); + void *ptrs[2]; + ptrs[0] = mlx; + ptrs[1] = win; + mlx_hook(win, CreateNotify, 0, (int (*)())(void *)&create_hook, NULL); + mlx_hook(win, DestroyNotify, 0, (int (*)())(void *)&destroy_hook, &ptrs); + + mlx_loop(mlx); + cout << "mlx_loop() returned." << endl; + mlx_destroy_window(mlx, win); + uc_pause(); + } + goto end; + end: cout << "Exit..." << endl; uc_pause(); diff --git a/source/mlx_hook.c b/source/mlx_hook.c new file mode 100644 index 0000000..394ef95 --- /dev/null +++ b/source/mlx_hook.c @@ -0,0 +1,135 @@ +/** + * mlx_hook.c + * for the project "MinilibX for 3DS" + * by Zy + * at https://github.com/frzysk/mlx3ds + */ + +#include "mlx_hook.h" + +#include "mlx_internal.h" + +int mlx_mouse_hook(t_win win_ptr, int (*funct_ptr)(), void *param) +{ + (void)win_ptr; + (void)funct_ptr; + (void)param; + + // ╮(︶▽︶)╭ + return (0); +} + +int mlx_key_hook(t_win win_ptr, + int (*funct_ptr)(int keycode, void *param), void *param) +{ + ((t_internal_win *)win_ptr)->hooks[KeyRelease].hook = funct_ptr; + ((t_internal_win *)win_ptr)->hooks[KeyRelease].param = param; + return (0); +} + +int mlx_expose_hook(t_win win_ptr, + int (*funct_ptr)(void *param), void *param) +{ + // TODO use expose hook? + ((t_internal_win *)win_ptr)->hooks[Expose].hook = funct_ptr; + ((t_internal_win *)win_ptr)->hooks[Expose].param = param; + return (0); +} +#include // remove debug TODO +#include +#include "utilsconsole.h" + +int mlx_loop_hook(t_mlx mlx_ptr, + int (*funct_ptr)(void *param), void *param) +{ + ((t_internal_mlx *)mlx_ptr)->hook_loop_function = funct_ptr; + ((t_internal_mlx *)mlx_ptr)->hook_loop_param = param; + return (0); +} + +int mlx_hook(t_win win_ptr, int x_event, int x_mask, + int (*funct)(), void *param) +{ + (void)x_mask; + ((t_internal_win *)win_ptr)->hooks[x_event].hook = funct; + ((t_internal_win *)win_ptr)->hooks[x_event].param = param; + return (0); +} + +typedef struct +{ + u32 held; + u32 down; + u32 up; +} t_keys_status; + +static t_keys_status keys_events(void) +{ + static u32 last_held; + static u32 real_last_held; + t_keys_status r; + u32 real_held = hidKeysHeld(); + + hidScanInput(); + r.held = real_held | real_last_held; + r.up = last_held & ~r.held; + r.down = ~last_held & r.held; + last_held = r.held; + real_last_held = real_held; + return (r); +} + +static void call_events(t_internal_win *win, t_keys_status keys) +{ + if (win->hooks[KeyRelease].hook && keys.up) + for (int i = 0; i < 32; i++) + if ((keys.up >> i) & 1) + win->hooks[KeyRelease].hook( + 1 << i, win->hooks[KeyRelease].param); + if (win->hooks[KeyPress].hook && keys.down) + for (int i = 0; i < 32; i++) + if ((keys.down >> i) & 1) + win->hooks[KeyPress].hook( + 1 << i, win->hooks[KeyPress].param); +} + +static void call_start_events(t_internal_win *win) +{ + if (win->hooks[CreateNotify].hook) + win->hooks[CreateNotify].hook(win->hooks[CreateNotify].param); +} + +static void call_end_events(t_internal_win *win) +{ + if (win->hooks[DestroyNotify].hook) + win->hooks[DestroyNotify].hook(win->hooks[DestroyNotify].param); +} + +int mlx_loop(t_mlx mlx_ptr) +{ + t_internal_mlx *mlx = mlx_ptr; + + mlx->loop = true; + if (mlx->top_window) + call_start_events(mlx->top_window); + while (aptMainLoop() && mlx->loop) + { + t_keys_status keys = keys_events(); + if (mlx->top_window) + call_events(mlx->top_window, keys); + if (mlx->hook_loop_function) + mlx->hook_loop_function(mlx->hook_loop_param); + gfxFlushBuffers(); // aptMainLoop() doesn't work without that + } + if (!aptMainLoop()) + if (mlx->top_window) + call_end_events(mlx->top_window); + mlx->loop = false; + return (0); +} + +int mlx_loop_end(t_mlx mlx_ptr) +{ + ((t_internal_mlx *)mlx_ptr)->loop = false; + return (0); +} diff --git a/source/mlx_init.c b/source/mlx_init.c index 0d50bd0..4876ac5 100644 --- a/source/mlx_init.c +++ b/source/mlx_init.c @@ -11,10 +11,7 @@ #include #include "3ds.h" -static t_internal_mlx g_internal_mlx = { - .is_init_called = false, - .top_window = NULL, -}; +static t_internal_mlx g_internal_mlx; t_mlx mlx_init(void) { diff --git a/source/mlx_internal.h b/source/mlx_internal.h index 4627a8a..db5018d 100644 --- a/source/mlx_internal.h +++ b/source/mlx_internal.h @@ -14,6 +14,7 @@ # include # include "3ds.h" +# include "mlx_events.h" /// @brief Write an error message and exit the program. /// @@ -29,23 +30,38 @@ typedef struct s_internal_mlx bool is_init_called; /// @brief Window displayed on the top screen. struct s_internal_win *top_window; + /// @brief Function called in loop by mlx_loop(). + int (*hook_loop_function)(void *param); + /// @brief Parameter given to hook_loop_function. + void *hook_loop_param; + /// @brief Set to false to break the loop of mlx_loop(). + bool loop; } t_internal_mlx; +// TODO docs +typedef struct s_internal_win_hook +{ + int (*hook)(); + void *param; +} t_internal_win_hook; + /// @brief Represents a window. typedef struct s_internal_win { /// @brief mlx connection identifier - t_internal_mlx *mlx; + t_internal_mlx *mlx; /// @brief Width of the window - int width; + int width; /// @brief Height of the window - int height; + int height; /// @brief Buffer of the screen. - u8 *framebuffer; + u8 *framebuffer; /// @brief Width of the screen (on x). - u16 framebuffer_width; + u16 framebuffer_width; /// @brief Height of the screen (on y). - u16 framebuffer_height; + u16 framebuffer_height; + // TODO docs + t_internal_win_hook hooks[LASTEvent]; } t_internal_win; /// @brief Represents an image in memory. diff --git a/source/mlx_window.c b/source/mlx_window.c index aec4586..5e60bfb 100644 --- a/source/mlx_window.c +++ b/source/mlx_window.c @@ -10,6 +10,7 @@ #include "3ds.h" #include "mlx_internal.h" #include +#include t_win mlx_new_window(t_mlx mlx_ptr, int size_x, int size_y, const char *title) { @@ -21,10 +22,11 @@ t_win mlx_new_window(t_mlx mlx_ptr, int size_x, int size_y, const char *title) r = malloc(sizeof(t_internal_win)); if (!r) return (NULL); + bzero(r, sizeof(t_internal_win)); r->mlx = mlx_ptr; + r->mlx->top_window = r; r->width = size_x; r->height = size_y; - r->framebuffer = NULL; return (r); }