From 779354b1965e5b8bf12925e504f5eedcade89f85 Mon Sep 17 00:00:00 2001 From: qkdreyer Date: Sun, 26 Nov 2023 18:28:15 +0100 Subject: [PATCH 1/2] feat: add macOS compatibility --- package/garlicui/src/clock.c | 4 + package/garlicui/src/gui.c | 155 ++++++++++++++++++++++++++- package/garlicui/src/gui_main_menu.h | 6 +- package/garlicui/src/io.c | 13 +++ package/garlicui/src/io.h | 22 ++-- package/garlicui/src/localization.c | 8 +- package/garlicui/src/sdl.h | 5 + 7 files changed, 199 insertions(+), 14 deletions(-) diff --git a/package/garlicui/src/clock.c b/package/garlicui/src/clock.c index 2df9ffd7d5..3f06b75753 100644 --- a/package/garlicui/src/clock.c +++ b/package/garlicui/src/clock.c @@ -1,6 +1,8 @@ #include #include +#ifdef __linux__ #include +#endif #include #include "clock.h" @@ -66,6 +68,7 @@ void clock_set_current_time(time_t timestamp, int utc_offset) // Open the device for read/write access int dev_fd = open(dev_path, O_RDWR); +#ifdef __linux__ // We managed to open the device for read/write access if (dev_fd >= 0) { @@ -87,6 +90,7 @@ void clock_set_current_time(time_t timestamp, int utc_offset) // Close the device close(dev_fd); } +#endif // Free the device path buffer free(dev_path); diff --git a/package/garlicui/src/gui.c b/package/garlicui/src/gui.c index 2cd690ebc1..ab2588c3ca 100644 --- a/package/garlicui/src/gui.c +++ b/package/garlicui/src/gui.c @@ -518,7 +518,7 @@ struct gui_context * gui_create_context(int argc, char * argv[]) // Render the east face button icon context->surfaces.bars.bottom.actions.select.surface = gui_render_select_legend_surface(context); - + // We failed to render the east face button icon if (context->surfaces.bars.bottom.actions.select.surface == NULL) { @@ -586,6 +586,7 @@ struct gui_context * gui_create_context(int argc, char * argv[]) context->surfaces.overlays.notch.position.w = context->surfaces.overlays.notch.surface->w; context->surfaces.overlays.notch.position.h = context->surfaces.overlays.notch.surface->h; +#ifndef __MACOSX__ // We couldn't enumerate the joysticks if (SDL_NumJoysticks() <= 0) { @@ -602,7 +603,7 @@ struct gui_context * gui_create_context(int argc, char * argv[]) // No use going further if we have no way of controlling the UI goto free_menu_action_surface; } - +#endif // Allocate memory for the additional main menu data main_menu_node_data = calloc(1, sizeof(struct gui_menu_node_data)); @@ -630,7 +631,9 @@ struct gui_context * gui_create_context(int argc, char * argv[]) context->menu.root->activate(context->menu.root); // Enable joystick input +#ifndef __MACOSX__ SDL_JoystickEventState(SDL_ENABLE); +#endif // Restore the previous UI state gui_restore_ui_state(context, (const char *)context->settings.ui_state); @@ -684,8 +687,10 @@ struct gui_context * gui_create_context(int argc, char * argv[]) free(main_menu_node_data); close_internal_joystick: +#ifndef __MACOSX__ // Close the internal joystick SDL_JoystickClose(context->inputs.internal.joystick); +#endif free_notch_overlay_surface: // Free the notch overlay @@ -1026,6 +1031,150 @@ void gui_update(struct gui_context * context) // Differentiate input event types switch (event.type) { + // Keyboard button events + case SDL_KEYUP: + case SDL_KEYDOWN: + { + // Determine the pressed state + int pressed = event.type == SDL_KEYDOWN; + + // Differentiate between buttons + switch (event.key.keysym.sym) + { + // The up button + case SDLK_UP: + { + // Update + context->inputs.internal.current.dpad_y = pressed ? -1 : 0; + + // Break + break; + } + + // The right button + case SDLK_RIGHT: + { + // Update + context->inputs.internal.current.dpad_x = pressed ? 1 : 0; + + // Break + break; + } + + // The down button + case SDLK_DOWN: + { + // Update + context->inputs.internal.current.dpad_y = pressed ? 1 : 0; + + // Break + break; + } + + // The left button + case SDLK_LEFT: + { + // Update + context->inputs.internal.current.dpad_x = pressed ? -1 : 0; + + // Break + break; + } + + // The south-facing face button (A on XBOX, B on Nintendo, Cross on PlayStation) + case SDLK_z: + { + // Update + context->inputs.internal.current.south = pressed; + + // Break + break; + } + + // The east-facing face button (B on XBOX, A on Nintendo, Circle on PlayStation) + case SDLK_x: + { + // Update + context->inputs.internal.current.east = pressed; + + // Break + break; + } + + // The north-facing face button (Y on XBOX, X on Nintendo, Triangle on PlayStation) + case SDLK_c: + { + // Update + context->inputs.internal.current.north = pressed; + + // Break + break; + } + + // The west-facing face button (X on XBOX, Y on Nintendo, Square on PlayStation) + case SDLK_v: + { + // Update + context->inputs.internal.current.west = pressed; + + // Break + break; + } + + // The enter button + case SDLK_RETURN: + { + // Update + context->inputs.internal.current.start = pressed; + + // Break + break; + } + + // The backspace button + case SDLK_BACKSPACE: + { + // Update + context->inputs.internal.current.select = pressed; + + // Break + break; + } + + // The space button + case SDLK_SPACE: + { + // Update + context->inputs.internal.current.mode = pressed; + + // Break + break; + } + + // The escape button + case SDLK_ESCAPE: + { + // Update + context->inputs.internal.current.power = pressed; + + // Break + break; + } + + // Other buttons + default: + { + // Break + break; + } + } + + // Log the event + // printf("button %d state %d\n", event.jbutton.button, pressed); + + // Break + break; + } // Regular button events case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: @@ -1888,4 +2037,4 @@ void gui_write_configuration(struct gui_context * context) // Cleanup xmlFreeDoc(document); } -} \ No newline at end of file +} diff --git a/package/garlicui/src/gui_main_menu.h b/package/garlicui/src/gui_main_menu.h index 68e2d83c55..b1b8fc585c 100644 --- a/package/garlicui/src/gui_main_menu.h +++ b/package/garlicui/src/gui_main_menu.h @@ -7,6 +7,10 @@ #include "gui_context_menu.h" #include "gui_retroarch_menu.h" +#ifndef VERSION +#define VERSION "dev" +#endif + /** * @brief Activates the main menu. */ @@ -143,4 +147,4 @@ static void gui_invalidate_main_menu(struct gui_node * this) } } -#endif \ No newline at end of file +#endif diff --git a/package/garlicui/src/io.c b/package/garlicui/src/io.c index 1aa55602af..6cb22abec8 100644 --- a/package/garlicui/src/io.c +++ b/package/garlicui/src/io.c @@ -476,6 +476,12 @@ int io_get_content_items(const char * path, char *** items, int include_files, i // Open the given directory DIR * directory = opendir(path); + // We failed to open the directory + if (directory == NULL) + { + return 0; + } + // The collected items char ** collected_items = NULL; @@ -929,6 +935,13 @@ void io_shutdown() // Close the SysRq trigger file fclose(sysrq); } +#ifdef __MACOSX__ + else + { + // Exit the application + exit(0); + } +#endif } /** diff --git a/package/garlicui/src/io.h b/package/garlicui/src/io.h index 63d2eca69d..c2e4dd2345 100644 --- a/package/garlicui/src/io.h +++ b/package/garlicui/src/io.h @@ -11,6 +11,17 @@ */ typedef char * (*textformatter)(const char *); +/** + * @brief The directory in which all boot related files are stored. + * + * This includes the boot script, rootfs, global configuration file, etc. + */ +#ifdef __MACOSX__ +#define FOLDER_CONFIGURATION_BOOT_FOLDER "" +#else +#define FOLDER_CONFIGURATION_BOOT_FOLDER "/media/boot/" +#endif + /** * @brief The folder that contains the game library template. */ @@ -29,14 +40,7 @@ typedef char * (*textformatter)(const char *); /** * @brief The folder that contains the icon overrides. */ -#define ICON_FOLDER_PATH "/media/boot/icons" - -/** - * @brief The directory in which all boot related files are stored. - * - * This includes the boot script, rootfs, global configuration file, etc. - */ -#define FOLDER_CONFIGURATION_BOOT_FOLDER "/media/boot" +#define ICON_FOLDER_PATH FOLDER_CONFIGURATION_BOOT_FOLDER "icons" /** * @brief The folder icon file name. @@ -189,4 +193,4 @@ void io_copy_directory(const char * src_dir, const char * dest_dir); */ void io_unpack_resources(); -#endif \ No newline at end of file +#endif diff --git a/package/garlicui/src/localization.c b/package/garlicui/src/localization.c index 378a00f33f..105125a972 100644 --- a/package/garlicui/src/localization.c +++ b/package/garlicui/src/localization.c @@ -1,3 +1,5 @@ +#include + #include "icon.h" #include "localization.h" @@ -32,7 +34,11 @@ const char * localization_font_file_path(const char * locale) if (strcmp(localized_font_file_path, font_file_path_msgid) == 0) { // Fall back to a semi-safe latin default font (better than nothing) +#ifdef __MACOSX__ + localized_font_file_path = "/System/Library/Fonts/Monaco.ttf"; +#else localized_font_file_path = "/usr/share/fonts/oswald/Oswald-Regular.ttf"; +#endif } // Copy the localized font file path into the static buffer @@ -204,4 +210,4 @@ int localization_get_supported_locales(char *** locales) // Return the number of supported locales return num_locales; -} \ No newline at end of file +} diff --git a/package/garlicui/src/sdl.h b/package/garlicui/src/sdl.h index f83ae65082..b9e7d67824 100644 --- a/package/garlicui/src/sdl.h +++ b/package/garlicui/src/sdl.h @@ -8,6 +8,11 @@ #include #include +// https://github.com/OpenDingux/SDL/blob/dd7260f1d7f79a58aba95a03fd6532729181eadb/include/SDL_main.h#L54 +#if defined(main) +#undef main +#endif + /** * @brief Fills the given SDL surface with a solid color. * From 92d5d25a8595265b55e9de3832563b053e130e67 Mon Sep 17 00:00:00 2001 From: qkdreyer Date: Tue, 28 Nov 2023 09:17:05 +0100 Subject: [PATCH 2/2] wip: add network --- configs/garlicos_aarch64_defconfig | 6 +- configs/garlicos_armhf_defconfig | 6 +- configs/garlicos_mipsel_defconfig | 6 +- package/garlicui/Config.in | 1 + package/garlicui/garlicui.mk | 7 +- package/garlicui/src/Makefile | 4 +- package/garlicui/src/gui.c | 72 +- package/garlicui/src/gui.h | 79 ++- package/garlicui/src/gui_context_menu.h | 19 + package/garlicui/src/gui_menu.c | 2 +- .../garlicui/src/gui_network_context_menu.h | 198 ++++++ .../src/gui_network_setup_context_menu.h | 146 ++++ .../gui_network_setup_security_context_menu.h | 238 +++++++ package/garlicui/src/icons/rect.svg | 3 + "package/garlicui/src/icons/\342\207\246.svg" | 2 + "package/garlicui/src/icons/\342\207\247.svg" | 2 + "package/garlicui/src/icons/\342\207\251.svg" | 2 + "package/garlicui/src/icons/\342\212\225.svg" | 4 + "package/garlicui/src/icons/\342\217\216.svg" | 2 + package/garlicui/src/io.h | 24 +- .../src/locale/af_ZA/LC_MESSAGES/garlicui.po | 12 + .../src/locale/am_ET/LC_MESSAGES/garlicui.po | 12 + .../src/locale/bg_BG/LC_MESSAGES/garlicui.po | 12 + .../src/locale/ca_ES/LC_MESSAGES/garlicui.po | 12 + .../src/locale/cs_CZ/LC_MESSAGES/garlicui.po | 12 + .../src/locale/da_DK/LC_MESSAGES/garlicui.po | 12 + .../src/locale/de_DE/LC_MESSAGES/garlicui.po | 12 + .../src/locale/el_GR/LC_MESSAGES/garlicui.po | 12 + .../src/locale/en_US/LC_MESSAGES/garlicui.po | 12 + .../src/locale/es_ES/LC_MESSAGES/garlicui.po | 12 + .../src/locale/et_EE/LC_MESSAGES/garlicui.po | 12 + .../src/locale/fi_FI/LC_MESSAGES/garlicui.po | 12 + .../src/locale/fil_PH/LC_MESSAGES/garlicui.po | 12 + .../src/locale/fr_FR/LC_MESSAGES/garlicui.po | 12 + .../src/locale/he_IL/LC_MESSAGES/garlicui.po | 12 + .../src/locale/hi_IN/LC_MESSAGES/garlicui.po | 12 + .../src/locale/hr_HR/LC_MESSAGES/garlicui.po | 12 + .../src/locale/hu_HU/LC_MESSAGES/garlicui.po | 12 + .../src/locale/id_ID/LC_MESSAGES/garlicui.po | 12 + .../src/locale/is_IS/LC_MESSAGES/garlicui.po | 12 + .../src/locale/it_IT/LC_MESSAGES/garlicui.po | 12 + .../src/locale/ja_JP/LC_MESSAGES/garlicui.po | 12 + .../src/locale/ko_KR/LC_MESSAGES/garlicui.po | 12 + .../src/locale/lt_LT/LC_MESSAGES/garlicui.po | 12 + .../src/locale/lv_LV/LC_MESSAGES/garlicui.po | 12 + .../src/locale/ms_MY/LC_MESSAGES/garlicui.po | 12 + .../src/locale/nb_NO/LC_MESSAGES/garlicui.po | 12 + .../src/locale/nl_NL/LC_MESSAGES/garlicui.po | 12 + .../src/locale/pl_PL/LC_MESSAGES/garlicui.po | 12 + .../src/locale/pt_BR/LC_MESSAGES/garlicui.po | 12 + .../src/locale/pt_PT/LC_MESSAGES/garlicui.po | 12 + .../src/locale/ro_RO/LC_MESSAGES/garlicui.po | 12 + .../src/locale/ru_RU/LC_MESSAGES/garlicui.po | 12 + .../src/locale/sk_SK/LC_MESSAGES/garlicui.po | 12 + .../src/locale/sl_SI/LC_MESSAGES/garlicui.po | 12 + .../src/locale/sr_RS/LC_MESSAGES/garlicui.po | 12 + .../src/locale/sv_SE/LC_MESSAGES/garlicui.po | 12 + .../src/locale/sw_KE/LC_MESSAGES/garlicui.po | 12 + .../src/locale/th_TH/LC_MESSAGES/garlicui.po | 12 + .../src/locale/tr_TR/LC_MESSAGES/garlicui.po | 12 + .../src/locale/uk_UA/LC_MESSAGES/garlicui.po | 12 + .../src/locale/vi_VN/LC_MESSAGES/garlicui.po | 12 + .../src/locale/zh_CN/LC_MESSAGES/garlicui.po | 12 + .../src/locale/zh_TW/LC_MESSAGES/garlicui.po | 12 + .../src/locale/zu_ZA/LC_MESSAGES/garlicui.po | 12 + package/garlicui/src/network.c | 175 +++++ package/garlicui/src/network.h | 45 ++ package/garlicui/src/osk.h | 93 +++ package/garlicui/src/osk_context_menu.h | 648 ++++++++++++++++++ 69 files changed, 2283 insertions(+), 41 deletions(-) create mode 100644 package/garlicui/src/gui_network_context_menu.h create mode 100644 package/garlicui/src/gui_network_setup_context_menu.h create mode 100644 package/garlicui/src/gui_network_setup_security_context_menu.h create mode 100644 package/garlicui/src/icons/rect.svg create mode 100644 "package/garlicui/src/icons/\342\207\246.svg" create mode 100644 "package/garlicui/src/icons/\342\207\247.svg" create mode 100644 "package/garlicui/src/icons/\342\207\251.svg" create mode 100644 "package/garlicui/src/icons/\342\212\225.svg" create mode 100644 "package/garlicui/src/icons/\342\217\216.svg" create mode 100644 package/garlicui/src/network.c create mode 100644 package/garlicui/src/network.h create mode 100644 package/garlicui/src/osk.h create mode 100644 package/garlicui/src/osk_context_menu.h diff --git a/configs/garlicos_aarch64_defconfig b/configs/garlicos_aarch64_defconfig index d3902d6300..9b91dc8238 100644 --- a/configs/garlicos_aarch64_defconfig +++ b/configs/garlicos_aarch64_defconfig @@ -379,8 +379,8 @@ BR2_FORTIFY_SOURCE_1=y # BR2_ROOTFS_SKELETON_DEFAULT=y # BR2_ROOTFS_SKELETON_CUSTOM is not set -BR2_TARGET_GENERIC_HOSTNAME="buildroot" -BR2_TARGET_GENERIC_ISSUE="Welcome to Buildroot" +BR2_TARGET_GENERIC_HOSTNAME="GarlicOS" +BR2_TARGET_GENERIC_ISSUE="Welcome to GarlicOS" BR2_TARGET_GENERIC_PASSWD_SHA256=y # BR2_TARGET_GENERIC_PASSWD_SHA512 is not set BR2_TARGET_GENERIC_PASSWD_METHOD="sha-256" @@ -416,7 +416,7 @@ BR2_TARGET_GENERIC_GETTY_BAUDRATE="0" BR2_TARGET_GENERIC_GETTY_TERM="vt100" BR2_TARGET_GENERIC_GETTY_OPTIONS="" BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW=y -BR2_SYSTEM_DHCP="" +BR2_SYSTEM_DHCP="wlan0" BR2_SYSTEM_DEFAULT_PATH="/bin:/sbin:/usr/bin:/usr/sbin" # BR2_ENABLE_LOCALE_PURGE is not set BR2_GENERATE_LOCALE="af_ZA am_ET bg_BG ca_ES cs_CZ da_DK de_DE el_GR en_US es_ES et_EE fi_FI fil_PH fr_FR he_IL hi_IN hr_HR hu_HU id_ID is_IS it_IT ja_JP ko_KR lt_LT lv_LV ms_MY nb_NO nl_NL pl_PL pt_BR pt_PT ro_RO ru_RU sk_SK sl_SI sr_RS sv_SE sw_KE th_TH tr_TR uk_UA vi_VN zh_CN zh_TW zu_ZA" diff --git a/configs/garlicos_armhf_defconfig b/configs/garlicos_armhf_defconfig index b72f20a651..289d1efe82 100644 --- a/configs/garlicos_armhf_defconfig +++ b/configs/garlicos_armhf_defconfig @@ -411,8 +411,8 @@ BR2_FORTIFY_SOURCE_1=y # BR2_ROOTFS_SKELETON_DEFAULT=y # BR2_ROOTFS_SKELETON_CUSTOM is not set -BR2_TARGET_GENERIC_HOSTNAME="buildroot" -BR2_TARGET_GENERIC_ISSUE="Welcome to Buildroot" +BR2_TARGET_GENERIC_HOSTNAME="GarlicOS" +BR2_TARGET_GENERIC_ISSUE="Welcome to GarlicOS" BR2_TARGET_GENERIC_PASSWD_SHA256=y # BR2_TARGET_GENERIC_PASSWD_SHA512 is not set BR2_TARGET_GENERIC_PASSWD_METHOD="sha-256" @@ -448,7 +448,7 @@ BR2_TARGET_GENERIC_GETTY_BAUDRATE="0" BR2_TARGET_GENERIC_GETTY_TERM="vt100" BR2_TARGET_GENERIC_GETTY_OPTIONS="" BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW=y -BR2_SYSTEM_DHCP="" +BR2_SYSTEM_DHCP="wlan0" BR2_SYSTEM_DEFAULT_PATH="/bin:/sbin:/usr/bin:/usr/sbin" # BR2_ENABLE_LOCALE_PURGE is not set BR2_GENERATE_LOCALE="af_ZA am_ET bg_BG ca_ES cs_CZ da_DK de_DE el_GR en_US es_ES et_EE fi_FI fil_PH fr_FR he_IL hi_IN hr_HR hu_HU id_ID is_IS it_IT ja_JP ko_KR lt_LT lv_LV ms_MY nb_NO nl_NL pl_PL pt_BR pt_PT ro_RO ru_RU sk_SK sl_SI sr_RS sv_SE sw_KE th_TH tr_TR uk_UA vi_VN zh_CN zh_TW zu_ZA" diff --git a/configs/garlicos_mipsel_defconfig b/configs/garlicos_mipsel_defconfig index 95f7758faa..6af1119914 100644 --- a/configs/garlicos_mipsel_defconfig +++ b/configs/garlicos_mipsel_defconfig @@ -327,8 +327,8 @@ BR2_FORTIFY_SOURCE_1=y # BR2_ROOTFS_SKELETON_DEFAULT=y # BR2_ROOTFS_SKELETON_CUSTOM is not set -BR2_TARGET_GENERIC_HOSTNAME="buildroot" -BR2_TARGET_GENERIC_ISSUE="Welcome to Buildroot" +BR2_TARGET_GENERIC_HOSTNAME="GarlicOS" +BR2_TARGET_GENERIC_ISSUE="Welcome to GarlicOS" BR2_TARGET_GENERIC_PASSWD_SHA256=y # BR2_TARGET_GENERIC_PASSWD_SHA512 is not set BR2_TARGET_GENERIC_PASSWD_METHOD="sha-256" @@ -364,7 +364,7 @@ BR2_TARGET_GENERIC_GETTY_BAUDRATE="0" BR2_TARGET_GENERIC_GETTY_TERM="vt100" BR2_TARGET_GENERIC_GETTY_OPTIONS="" BR2_TARGET_GENERIC_REMOUNT_ROOTFS_RW=y -BR2_SYSTEM_DHCP="" +BR2_SYSTEM_DHCP="wlan0" BR2_SYSTEM_DEFAULT_PATH="/bin:/sbin:/usr/bin:/usr/sbin" # BR2_ENABLE_LOCALE_PURGE is not set BR2_GENERATE_LOCALE="af_ZA am_ET bg_BG ca_ES cs_CZ da_DK de_DE el_GR en_US es_ES et_EE fi_FI fil_PH fr_FR he_IL hi_IN hr_HR hu_HU id_ID is_IS it_IT ja_JP ko_KR lt_LT lv_LV ms_MY nb_NO nl_NL pl_PL pt_BR pt_PT ro_RO ru_RU sk_SK sl_SI sr_RS sv_SE sw_KE th_TH tr_TR uk_UA vi_VN zh_CN zh_TW zu_ZA" diff --git a/package/garlicui/Config.in b/package/garlicui/Config.in index 60c6c19d14..f25b9f4f62 100644 --- a/package/garlicui/Config.in +++ b/package/garlicui/Config.in @@ -25,5 +25,6 @@ menuconfig BR2_PACKAGE_GARLICUI select BR2_PACKAGE_LIBXML2 select BR2_PACKAGE_CJSON select BR2_PACKAGE_ICU + select BR2_PACKAGE_NETWORK_MANAGER help Provides the Garlic user interface diff --git a/package/garlicui/garlicui.mk b/package/garlicui/garlicui.mk index 1204ae9784..8885acd1a6 100644 --- a/package/garlicui/garlicui.mk +++ b/package/garlicui/garlicui.mk @@ -7,11 +7,12 @@ GARLICUI_VERSION = 2.0 GARLICUI_SITE = package/garlicui/src GARLICUI_SITE_METHOD = local -GARLICUI_CFLAGS = $(TARGET_CFLAGS) -I$(STAGING_DIR)/usr/include -I$(STAGING_DIR)/usr/include/librsvg-2.0 -I$(STAGING_DIR)/usr/include/glib-2.0 -I$(STAGING_DIR)/usr/lib/glib-2.0/include -I$(STAGING_DIR)/usr/include/cairo -I$(STAGING_DIR)/usr/include/gdk-pixbuf-2.0 -I$(STAGING_DIR)/usr/include/libxml2 -I$(STAGING_DIR)/usr/include/cjson -GARLICUI_DEPENDENCIES = librsvg sdl sdl_gfx sdl_image sdl_ttf libxml2 cjson icu +GARLICUI_CFLAGS = $(TARGET_CFLAGS) -I$(STAGING_DIR)/usr/include -I$(STAGING_DIR)/usr/include/librsvg-2.0 -I$(STAGING_DIR)/usr/include/glib-2.0 -I$(STAGING_DIR)/usr/lib/glib-2.0/include -I$(STAGING_DIR)/usr/include/cairo -I$(STAGING_DIR)/usr/include/gdk-pixbuf-2.0 -I$(STAGING_DIR)/usr/include/libxml2 -I$(STAGING_DIR)/usr/include/cjson -I$(STAGING_DIR)/usr/include/libnm -I$(STAGING_DIR)/usr/include/libmount -I$(STAGING_DIR)/usr/include/blkid +GARLICUI_LDFLAGS = -lSDL -lSDL_image -lSDL_ttf -lSDL_gfx -lrsvg-2 -lcairo -lgobject-2.0 -lglib-2.0 -lxml2 -lcjson -licuuc -lm -pthread -lnm -lgio-2.0 +GARLICUI_DEPENDENCIES = librsvg sdl sdl_gfx sdl_image sdl_ttf libxml2 cjson icu network-manager define GARLICUI_BUILD_CMDS - $(MAKE) CFLAGS="$(GARLICUI_CFLAGS)" CC="$(TARGET_CC)" LD="$(TARGET_LD)" VERSION=$(GARLICUI_VERSION) -C $(@D) + $(MAKE) CFLAGS="$(GARLICUI_CFLAGS)" LDFLAGS="$(GARLICUI_LDFLAGS)" CC="$(TARGET_CC)" LD="$(TARGET_LD)" VERSION=$(GARLICUI_VERSION) -C $(@D) endef define GARLICUI_INSTALL_TARGET_CMDS diff --git a/package/garlicui/src/Makefile b/package/garlicui/src/Makefile index aaf5e5de7e..20ccfa9eed 100644 --- a/package/garlicui/src/Makefile +++ b/package/garlicui/src/Makefile @@ -7,12 +7,12 @@ MO_FILES = $(patsubst %.po,%.mo,$(PO_FILES)) all: $(TARGET) translations $(TARGET): $(wildcard *.c) - $(CC) $(CFLAGS) -lSDL -lSDL_image -lSDL_ttf -lSDL_gfx -lrsvg-2 -lcairo -lgobject-2.0 -lglib-2.0 -lxml2 -lcjson -licuuc -lm -DVERSION='"$(VERSION)"' -o '$@' $^ + $(CC) $(CFLAGS) $(LDFLAGS) -DVERSION='"$(VERSION)"' -o '$@' $^ translations: $(MO_FILES) %.mo: %.po - msgfmt -o $@ $< + $(HOST_DIR)/bin/msgfmt -o $@ $< clean: -rm -rf $(TARGET) $(MO_FILES) *.o diff --git a/package/garlicui/src/gui.c b/package/garlicui/src/gui.c index ab2588c3ca..4a7117a2e1 100644 --- a/package/garlicui/src/gui.c +++ b/package/garlicui/src/gui.c @@ -412,7 +412,11 @@ struct gui_context * gui_create_context(int argc, char * argv[]) } // Create a 32bpp double-buffered screen surface +#ifdef __MACOSX__ + context->surfaces.screen = SDL_SetVideoMode(SCALING_REFERENCE_WIDTH, SCALING_REFERENCE_HEIGHT, 32, SDL_HWSURFACE | SDL_DOUBLEBUF); +#else context->surfaces.screen = SDL_SetVideoMode(info->current_w, info->current_h, 32, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN); +#endif // If 32bpp isn't available... if (context->surfaces.screen == NULL) @@ -556,6 +560,13 @@ struct gui_context * gui_create_context(int argc, char * argv[]) goto free_menu_action_surface; } + // Initialize the on-screen keyboard menu + if (osk_initialize_menu(context) != 0) + { + // No use continuing if we're missing on-screen keyboard + goto free_menu_action_surface; + } + // Calculate the garlic icon position context->surfaces.bars.top.logo.position.x = SCREEN_MARGIN * context->surfaces.scale_factor; context->surfaces.bars.top.logo.position.y = SCREEN_MARGIN * context->surfaces.scale_factor; @@ -1121,6 +1132,26 @@ void gui_update(struct gui_context * context) break; } + // The left bumper button (L1) + case SDLK_b: + { + // Update the internal button state + context->inputs.internal.current.left_bumper = pressed; + + // Break + break; + } + + // The right bumper button (R1) + case SDLK_n: + { + // Update the internal button state + context->inputs.internal.current.right_bumper = pressed; + + // Break + break; + } + // The enter button case SDLK_RETURN: { @@ -1799,25 +1830,29 @@ void gui_render(struct gui_context * context) SDL_BlitSurface(context->surfaces.bars.top.battery.surface, NULL, context->surfaces.screen, &context->surfaces.bars.top.battery.position); } - // We need to render the east button icon - if (context->surfaces.bars.bottom.actions.select.surface != NULL) + // We need to render the bottom bar + if (context->menu.active == NULL || context->menu.active->type != NODE_TYPE_CONTEXT_MENU_OSK_MENU) { - // Render the east button icon - SDL_BlitSurface(context->surfaces.bars.bottom.actions.select.surface, NULL, context->surfaces.screen, &context->surfaces.bars.bottom.actions.select.position); - } + // We need to render the east button icon + if (context->surfaces.bars.bottom.actions.select.surface != NULL) + { + // Render the east button icon + SDL_BlitSurface(context->surfaces.bars.bottom.actions.select.surface, NULL, context->surfaces.screen, &context->surfaces.bars.bottom.actions.select.position); + } - // We need to render the south button icon - if (context->surfaces.bars.bottom.actions.back.surface != NULL) - { - // Render the south button icon - SDL_BlitSurface(context->surfaces.bars.bottom.actions.back.surface, NULL, context->surfaces.screen, &context->surfaces.bars.bottom.actions.back.position); - } + // We need to render the south button icon + if (context->surfaces.bars.bottom.actions.back.surface != NULL) + { + // Render the south button icon + SDL_BlitSurface(context->surfaces.bars.bottom.actions.back.surface, NULL, context->surfaces.screen, &context->surfaces.bars.bottom.actions.back.position); + } - // We need to render the start button icon - if (context->surfaces.bars.bottom.actions.menu.surface != NULL) - { - // Render the start button icon - SDL_BlitSurface(context->surfaces.bars.bottom.actions.menu.surface, NULL, context->surfaces.screen, &context->surfaces.bars.bottom.actions.menu.position); + // We need to render the start button icon + if (context->surfaces.bars.bottom.actions.menu.surface != NULL) + { + // Render the start button icon + SDL_BlitSurface(context->surfaces.bars.bottom.actions.menu.surface, NULL, context->surfaces.screen, &context->surfaces.bars.bottom.actions.menu.position); + } } // Flip the front and back buffer @@ -1845,6 +1880,9 @@ void gui_read_configuration(struct gui_context * context) context->colors.notch.background = 0x000000; context->colors.notch.foreground = 0xffffff; + // Configures the on-screen keyboard menu + osk_configure_menu(context, document); + // We managed to load the XML document if (document != NULL) { @@ -2027,6 +2065,8 @@ void gui_write_configuration(struct gui_context * context) free(ui_state); } + // TODO persist network settings + // Save the document to a file io_write_folder_configuration(document, FOLDER_CONFIGURATION_BOOT_FOLDER); } diff --git a/package/garlicui/src/gui.h b/package/garlicui/src/gui.h index 80da645814..7761e53c29 100644 --- a/package/garlicui/src/gui.h +++ b/package/garlicui/src/gui.h @@ -18,6 +18,8 @@ struct gui_context; #include "process.h" #include "retroarch.h" #include "localization.h" +#include "network.h" +#include "osk.h" /** * @brief The reference width used for scaling. @@ -221,6 +223,36 @@ struct gui_context; */ #define NODE_TYPE_CONTEXT_MENU_DATE_AND_TIME_UTC_OFFSET_SETTER 21 +/** + * @brief The on-screen keyboard menu node type. + */ +#define NODE_TYPE_CONTEXT_MENU_OSK_MENU 22 + +/** + * @brief The network menu node type. + */ +#define NODE_TYPE_CONTEXT_MENU_NETWORK_MENU 23 + +/** + * @brief The network setup menu node type. + */ +#define NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_MENU 24 + +/** + * @brief The network setup setter. + */ +#define NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_SETTER 25 + +/** + * @brief The network setup security menu node type. + */ +#define NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_SECURITY_MENU 26 + +/** + * @brief The network setup security setter. + */ +#define NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_SECURITY_SETTER 27 + /** * The supported buttons. */ @@ -316,6 +348,9 @@ struct gui_context_bar_surfaces struct gui_context_overlay_surfaces { struct gui_context_surface notch; /** The scroll letter notch */ + struct gui_context_surface osk; /** The on-screen keyboard */ + struct gui_context_surface osk_buffer; /** The on-screen keyboard buffer */ + struct gui_context_surface osk_active_key; /** The on-screen keyboard active key */ }; /** @@ -358,6 +393,7 @@ struct gui_context_colors struct gui_context_bgfg_color legend; /** The legend colors */ struct gui_context_bgfg_color icon; /** The icon colors */ struct gui_context_bgfg_color notch; /** The notch colors */ + struct gui_context_bgfg_color osk; /** The on-screen keyboard colors */ }; /** @@ -452,6 +488,8 @@ struct gui_context_settings char locale[16]; /** The user's current locale setting (intentionally 1 frame late so surfaces can be re-rendered) */ char ui_state[128 + PATH_MAX * 2]; /** The UI state of the previous GarlicUI instance (populated on load, restored post-main-menu activation) */ int resume; /** The auto-resume flag */ + int network_ap_count; /** The number of user's network access points */ + struct network_access_point ** network_aps; /** The user's network access points */ }; /** @@ -478,6 +516,7 @@ struct gui_context struct gui_context_status status; /** The context status */ struct gui_context_settings settings; /** The context settings */ struct gui_context_node_tree menu; /** The menu tree nodes */ + struct osk_context osk; /** The on-screen keyboard context */ }; /** @@ -534,7 +573,45 @@ struct gui_locale_menu_node_data }; /** - * @brief A extended version of gui_menu_node_data that can accomodate a path. + * @brief A data structure holding network data. + */ +struct gui_network_menu_node_data +{ + struct gui_menu_node_data base; /** The base menu data */ + int network_ap_count; /** The number of network access points */ + struct network_access_point ** network_aps; /** The network access points */ +}; + +/** + * @brief A data structure holding network setup data. + */ +struct gui_network_setup_menu_node_data +{ + struct gui_menu_node_data base; /** The base menu data */ + struct network_access_point * network_ap; /** The network access point */ +}; + +/** + * @brief A data structure holding network setup security data. + */ +struct gui_network_setup_security_menu_node_data +{ + struct gui_menu_node_data base; /** The base menu data */ + char * security; /** The current security setting */ +}; + +/** + * @brief A data structure holding on-screen keyboard data. + */ +struct gui_osk_menu_node_data +{ + struct gui_menu_node_data base; /** The base menu data */ + char * title; /** The on-screen keyboard title */ + char * buffer; /** The on-screen keyboard buffer */ +}; + +/** + * @brief A extended version of gui_menu_node_data that can accommodate a path. */ struct gui_path_menu_node_data { diff --git a/package/garlicui/src/gui_context_menu.h b/package/garlicui/src/gui_context_menu.h index 58eb41d04a..f38b43c86b 100644 --- a/package/garlicui/src/gui_context_menu.h +++ b/package/garlicui/src/gui_context_menu.h @@ -77,6 +77,7 @@ static struct gui_node * gui_create_context_menu_item(struct gui_node * this, st #include "gui_content_context_menu.h" #include "gui_rtc_context_menu.h" #include "gui_locale_context_menu.h" +#include "gui_network_context_menu.h" /** * @brief Activates the context menu. @@ -174,6 +175,24 @@ static void gui_activate_context_menu(struct gui_node * this) free(locale_menu_node_data); } } + + // TODO ensure interface exists + // Allocate the additional network menu data + struct gui_network_menu_node_data * network_menu_node_data = calloc(1, sizeof(struct gui_network_menu_node_data)); + + // We managed to allocate the additional network menu data + if (network_menu_node_data != NULL) + { + // Create a node for the network menu + struct gui_node * network_menu_node = gui_create_context_menu_item(this, &first_node, &previous_node, gettext("Change Network"), gui_activate_network_menu, gui_deactivate_network_menu, gui_update_basic_menu, gui_render_vertical_scrolling_list_menu, gui_invalidate_network_menu, NODE_TYPE_CONTEXT_MENU_NETWORK_MENU, network_menu_node_data); + + // We failed to create a node for the network menu + if (network_menu_node == NULL) + { + // Free the additional menu data + free(network_menu_node_data); + } + } } } diff --git a/package/garlicui/src/gui_menu.c b/package/garlicui/src/gui_menu.c index 98fbde9225..e3ef75ab20 100644 --- a/package/garlicui/src/gui_menu.c +++ b/package/garlicui/src/gui_menu.c @@ -462,4 +462,4 @@ void gui_render_vertical_scrolling_list_menu(struct gui_node * this) } } } -} \ No newline at end of file +} diff --git a/package/garlicui/src/gui_network_context_menu.h b/package/garlicui/src/gui_network_context_menu.h new file mode 100644 index 0000000000..b91618b5b7 --- /dev/null +++ b/package/garlicui/src/gui_network_context_menu.h @@ -0,0 +1,198 @@ +#ifndef GUI_NETWORK_CONTEXT_MENU_H +#define GUI_NETWORK_CONTEXT_MENU_H + +#include "gui_network_setup_context_menu.h" + +/** + * @brief Creates a network context menu item. + */ +static struct gui_node * gui_create_network_menu_item(struct gui_node * this, struct gui_node ** first_node, struct gui_node ** previous_node, const char * display_name, struct network_access_point * network_ap) +{ + struct gui_node * network_setup_menu_node = NULL; + + // Allocate the additional network setup menu data + struct gui_network_setup_menu_node_data * network_setup_menu_node_data = calloc(1, sizeof(struct gui_network_setup_menu_node_data)); + + // We managed to allocate the additional network setup menu data + if (network_setup_menu_node_data != NULL) + { + // Assign the access point + network_setup_menu_node_data->network_ap = network_ap; + + // Create a node for the network setup menu + network_setup_menu_node = gui_create_context_menu_item(this, first_node, previous_node, display_name, gui_activate_network_setup_menu, gui_deactivate_network_setup_menu, gui_update_basic_menu, gui_render_vertical_scrolling_list_menu, gui_invalidate_network_setup_menu, NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_MENU, network_setup_menu_node_data); + + // We failed to create a node for the network setup menu + if (network_setup_menu_node == NULL) + { + // Free the additional menu data + free(network_setup_menu_node_data); + } + } + + // Return the created node + return network_setup_menu_node; +} + +/** + * @brief Activates the network context menu. + */ +static void gui_activate_network_menu(struct gui_node * this) +{ + // Get the network menu node data + struct gui_network_menu_node_data * network_menu_node_data = (struct gui_network_menu_node_data *)this->data; + + // Render the title text surface + gui_activate_menu(this, gettext("Change Network")); + + // The first node + struct gui_node * first_node = NULL; + + // The previous node + struct gui_node * previous_node = NULL; + + // The visible networks + struct network_access_point ** visible_access_points = NULL; + + // Get the visible networks + int num_networks = network_get_access_points(&visible_access_points); + + // We managed to get the visible networks + if (visible_access_points != NULL) + { + // Iterate all visible networks + for (int i = 0; i < num_networks; i++) + { + // Create a network menu item + gui_create_network_menu_item(this, &first_node, &previous_node, visible_access_points[i]->ssid, visible_access_points[i]); + } + + // Free the list of visible networks + // TODO network_free_access_points(visible_access_points, num_networks); + } + + // Iterate all manual networks + for (int i = 0, n = network_menu_node_data->network_ap_count; i < n; i++) + { + // Create a network menu item + gui_create_network_menu_item(this, &first_node, &previous_node, network_menu_node_data->network_aps[i]->ssid, network_menu_node_data->network_aps[i]); + } + + // Allocate memory for the access point + struct network_access_point * access_point = (struct network_access_point *)calloc(1, sizeof(struct network_access_point)); + + // We managed to allocate the network access point + if (access_point != NULL) + { + // Create a network menu item + gui_create_network_menu_item(this, &first_node, &previous_node, gettext("Manual configuration"), access_point); + } + + // Link the first & last nodes together + if (first_node != NULL && previous_node != NULL) + { + // Link the first node to the last node + first_node->previous = previous_node; + + // Link the last node to the first node + previous_node->next = first_node; + + // Link this node to its first child node + this->child = first_node; + + // And keep track of it for moving pointer menus + this->children = this->child; + + // Initialize the node iterator + struct gui_node * network_node = this->child; + + // Iterate all nodes + do + { + // Cast the node's network data + struct gui_network_setup_menu_node_data * network_setup_node_data = (struct gui_network_setup_menu_node_data *)network_node->data; + + // We've found the current network's node + if (network_setup_node_data->network_ap->active) + { + // Pre-select this node so that the user always starts at his currently selected network + this->child = network_node; + + // No need to iterate the rest + break; + } + + // Move to the next node + network_node = network_node->next; + } while (network_node != this->child); + } + + // Make this node take focus on screen + this->context->menu.active = this; +} + +/** + * @brief Deactivates the network context menu. + */ +static void gui_deactivate_network_menu(struct gui_node * this) +{ + // The menu has been activated (aka. its in focus, this check is necessary because deactivate gets called on parent node disposal as well) + if (this->context->menu.active == this) + { + // Write the context settings to disk + gui_write_configuration(this->context); + + // Destroy the node's children + gui_destroy_node(this, 0); + + // Free the title text surface + gui_deactivate_menu(this); + + // We have a parent node to return to + if (this->parent != NULL) + { + // And return on screen focus to the parent node + this->context->menu.active = this->parent; + } + } +} + +/** + * @brief Re-renders the network context menu's surfaces. + */ +static void gui_invalidate_network_menu(struct gui_node * this) +{ + // Create a temporary node + struct gui_node * node = gui_create_context_menu_item(this, NULL, NULL, gettext("Change Network"), NULL, NULL, NULL, NULL, NULL, NODE_TYPE_CONTEXT_MENU_NETWORK_MENU, NULL); + + // We managed to create a temporary node + if (node != NULL) + { + // We have a no longer guaranteed to be valid normal surface + if (this->normal_surface != NULL) + { + // Free the normal surface + SDL_FreeSurface(this->normal_surface); + } + + // We have a no longer guaranteed to be valid selected surface + if (this->selected_surface != NULL) + { + // Free the selected surface + SDL_FreeSurface(this->selected_surface); + } + + // Steal the temporary node's display surfaces + this->normal_surface = node->normal_surface; + this->selected_surface = node->selected_surface; + + // NULL the temporary node's display surfaces (so they won't get freed with the rest of the node) + node->normal_surface = NULL; + node->selected_surface = NULL; + + // Free the temporay node + gui_destroy_node(node, 1); + } +} + +#endif diff --git a/package/garlicui/src/gui_network_setup_context_menu.h b/package/garlicui/src/gui_network_setup_context_menu.h new file mode 100644 index 0000000000..c01f734097 --- /dev/null +++ b/package/garlicui/src/gui_network_setup_context_menu.h @@ -0,0 +1,146 @@ +#ifndef GUI_NETWORK_SETUP_CONTEXT_MENU_H +#define GUI_NETWORK_SETUP_CONTEXT_MENU_H + +#include "osk_context_menu.h" +#include "gui_network_setup_security_context_menu.h" + +/** + * @brief Change the network setup connection + */ +static void gui_change_network_setup_connection(struct gui_node * this) +{ + // Get the selected network setup's node data + struct gui_network_setup_menu_node_data * network_setup_node_data = (struct gui_network_setup_menu_node_data *)this->data; + + // Get the network setup menu's node data + struct gui_network_setup_menu_node_data * network_setup_menu_node_data = (struct gui_network_setup_menu_node_data *)this->parent->data; + + network_change_access_point(network_setup_menu_node_data->network_ap); +} + +/** + * @brief Activates the network setup context menu. + */ +static void gui_activate_network_setup_menu(struct gui_node * this) +{ + // Get the network setup menu node data + struct gui_network_setup_menu_node_data * network_setup_menu_node_data = (struct gui_network_setup_menu_node_data *) this->data; + + // Get the network access point data + struct network_access_point * network_access_point = network_setup_menu_node_data->network_ap; + + // Render the title text surface + gui_activate_menu(this, network_access_point->ssid != NULL ? network_access_point->ssid : gettext("Manual configuration")); + + // The first node + struct gui_node * first_node = NULL; + + // The previous node + struct gui_node * previous_node = NULL; + + // Create a network setup ssid menu item + osk_create_menu(this, &first_node, &previous_node, gettext("SSID"), network_access_point->ssid); + + // Create a network setup security menu item + gui_create_network_setup_security_menu(this, &first_node, &previous_node, gettext("Security"), network_access_point->security); + + // Create a network setup password menu item + osk_create_menu(this, &first_node, &previous_node, gettext("Password"), network_access_point->password); + + // Create a network setup connection menu item + gui_create_context_menu_item(this, &first_node, &previous_node, gettext("Connection"), gui_change_network_setup_connection, NULL, NULL, NULL, NULL, NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_SETTER, NULL); + + // Link the first & last nodes together + if (first_node != NULL && previous_node != NULL) + { + // Link the first node to the last node + first_node->previous = previous_node; + + // Link the last node to the first node + previous_node->next = first_node; + + // Link this node to its first child node + this->child = first_node; + + // And keep track of it for moving pointer menus + this->children = this->child; + } + + // Make this node take focus on screen + this->context->menu.active = this; +} + +/** + * @brief Deactivates the network setup context menu. + */ +static void gui_deactivate_network_setup_menu(struct gui_node * this) +{ + // The menu has been activated (aka. its in focus, this check is necessary because deactivate gets called on parent node disposal as well) + if (this->context->menu.active == this) + { + // Get the network setup menu node data + struct gui_network_setup_menu_node_data * network_menu_node_data = (struct gui_network_setup_menu_node_data *)this->data; + + // Add the current network setup + // TODO update network settings + // this->context->settings.meridian_time = network_setup_security_menu_node_data->security; + // p ((struct gui_network_setup_menu_node_data *)this->parent->data)->network_ap + // p ((struct gui_network_setup_menu_node_data *)this->parent->data)->network_ap->security + + // Write the context settings to disk + gui_write_configuration(this->context); + + // Destroy the node's children + gui_destroy_node(this, 0); + + // Free the title text surface + gui_deactivate_menu(this); + + // We have a parent node to return to + if (this->parent != NULL) + { + // And return on screen focus to the parent node + this->context->menu.active = this->parent; + } + } +} + +/** + * @brief Re-renders the network setup context menu's surfaces. + */ +static void gui_invalidate_network_setup_menu(struct gui_node * this) +{ + // Create a temporary node + struct gui_node * node = gui_create_context_menu_item(this, NULL, NULL, gettext("Manual configuration"), NULL, NULL, NULL, NULL, NULL, NODE_TYPE_CONTEXT_MENU_NETWORK_MENU, NULL); + + // We managed to create a temporary node + if (node != NULL) + { + // We have a no longer guaranteed to be valid normal surface + if (this->normal_surface != NULL) + { + // Free the normal surface + SDL_FreeSurface(this->normal_surface); + } + + // We have a no longer guaranteed to be valid selected surface + if (this->selected_surface != NULL) + { + // Free the selected surface + SDL_FreeSurface(this->selected_surface); + } + + // Steal the temporary node's display surfaces + this->normal_surface = node->normal_surface; + this->selected_surface = node->selected_surface; + + // NULL the temporary node's display surfaces (so they won't get freed with the rest of the node) + node->normal_surface = NULL; + node->selected_surface = NULL; + + // Free the temporay node + gui_destroy_node(node, 1); + } +} + +#endif diff --git a/package/garlicui/src/gui_network_setup_security_context_menu.h b/package/garlicui/src/gui_network_setup_security_context_menu.h new file mode 100644 index 0000000000..1dbaf8fa33 --- /dev/null +++ b/package/garlicui/src/gui_network_setup_security_context_menu.h @@ -0,0 +1,238 @@ +#ifndef GUI_NETWORK_SETUP_SECURITY_CONTEXT_MENU_H +#define GUI_NETWORK_SETUP_SECURITY_CONTEXT_MENU_H + +/** + * @brief Sets the to-be-applied / saved new network setup security. + */ +static void gui_change_network_setup_security(struct gui_node * this) +{ + // Get the selected network setup security's node data + struct gui_network_setup_security_menu_node_data * network_setup_security_node_data = (struct gui_network_setup_security_menu_node_data *)this->data; + + // Get the network setup security menu's node data + struct gui_network_setup_security_menu_node_data * network_setup_security_menu_node_data = (struct gui_network_setup_security_menu_node_data *)this->parent->data; + + // Allocate the network setup security buffer + network_setup_security_menu_node_data->security = calloc(1, strlen(network_setup_security_node_data->security)); + + // Set the network setup security + strcpy(network_setup_security_menu_node_data->security, network_setup_security_node_data->security); + + // Deactivate the network setup security menu's node (which will set & flush the network setting to disk) + this->parent->deactivate(this->parent); +} + +/** + * @brief Creates a network setup security context menu item. + */ +static struct gui_node * gui_create_network_setup_security_menu_item(struct gui_node * this, struct gui_node ** first_node, struct gui_node ** previous_node, const char * security) +{ + // The created node + struct gui_node * network_setup_security_menu_node = NULL; + + // Allocate the network setup security menu node data buffer + struct gui_network_setup_security_menu_node_data * network_setup_security_menu_node_data = calloc(1, sizeof(struct gui_network_setup_security_menu_node_data)); + + // We managed to allocate the network setup security menu node data buffer + if (network_setup_security_menu_node_data != NULL) + { + // Allocate the network setup security buffer + network_setup_security_menu_node_data->security = calloc(1, strlen(security)); + + // We managed to allocate the network setup security buffer + if (network_setup_security_menu_node_data->security != NULL) + { + // Set the network setup security + strcpy(network_setup_security_menu_node_data->security, security); + } + + // Create a node for the network setup security menu + network_setup_security_menu_node = gui_create_context_menu_item(this, first_node, previous_node, security, gui_change_network_setup_security, NULL, NULL, NULL, NULL, NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_SECURITY_SETTER, network_setup_security_menu_node_data); + + // We failed to create a node for the network setup security menu + if (network_setup_security_menu_node == NULL) + { + // Free the network setup security buffer + free(network_setup_security_menu_node_data->security); + + // Free the network setup security menu node data buffer + free(network_setup_security_menu_node_data); + } + } + + // Return the created node + return network_setup_security_menu_node; +} + +/** + * @brief Activates the network setup security context menu. + */ +static void gui_activate_network_setup_security_menu(struct gui_node * this) +{ + // Get the network setup security menu node data + struct gui_network_setup_security_menu_node_data * network_setup_security_menu_node_data = (struct gui_network_setup_security_menu_node_data *)this->data; + + // Render the title text surface + gui_activate_menu(this, gettext("Security")); + + // The first node + struct gui_node * first_node = NULL; + + // The previous node + struct gui_node * previous_node = NULL; + + // Create a network setup security none menu item + gui_create_network_setup_security_menu_item(this, &first_node, &previous_node, gettext("None")); + + // Create a network setup security WEP menu item + gui_create_network_setup_security_menu_item(this, &first_node, &previous_node, gettext("WEP")); + + // Create a network setup security WPA menu item + gui_create_network_setup_security_menu_item(this, &first_node, &previous_node, gettext("WPA")); + + // Create a network setup security WPA2 menu item + gui_create_network_setup_security_menu_item(this, &first_node, &previous_node, gettext("WPA2")); + + // Create a network setup security WPA3 menu item + gui_create_network_setup_security_menu_item(this, &first_node, &previous_node, gettext("WPA3")); + + // Link the first & last nodes together + if (first_node != NULL && previous_node != NULL) + { + // Link the first node to the last node + first_node->previous = previous_node; + + // Link the last node to the first node + previous_node->next = first_node; + + // Link this node to its first child node + this->child = first_node; + + // And keep track of it for moving pointer menus + this->children = this->child; + + // Initialize the node iterator + struct gui_node * network_node = this->child; + + // Iterate all nodes + do + { + // Cast the node's network data + struct gui_network_setup_security_menu_node_data * network_setup_security_node_data = (struct gui_network_setup_security_menu_node_data *)network_node->data; + + // We've found the current network setup security's node + if (network_setup_security_menu_node_data->security != NULL && strcmp(network_setup_security_node_data->security, network_setup_security_menu_node_data->security) == 0) + { + // Pre-select this node so that the user always starts at his currently selected network + this->child = network_node; + + // No need to iterate the rest + break; + } + + // Move to the next node + network_node = network_node->next; + } while (network_node != this->child); + } + + // Make this node take focus on screen + this->context->menu.active = this; +} + +/** + * @brief Deactivates the network setup security context menu. + */ +static void gui_deactivate_network_setup_security_menu(struct gui_node * this) +{ + // The menu has been activated (aka. its in focus, this check is necessary because deactivate gets called on parent node disposal as well) + if (this->context->menu.active == this) + { + // Get the network setup security menu node data + struct gui_network_setup_security_menu_node_data * network_setup_security_menu_node_data = (struct gui_network_setup_security_menu_node_data *)this->data; + + // Destroy the node's children + gui_destroy_node(this, 0); + + // Free the title text surface + gui_deactivate_menu(this); + + // We have a parent node to return to + if (this->parent != NULL) + { + // And return on screen focus to the parent node + this->context->menu.active = this->parent; + } + } +} + +/** + * @brief Re-renders the network setup security context menu's surfaces. + */ +static void gui_invalidate_network_setup_security_menu(struct gui_node * this) +{ + // Create a temporary node + struct gui_node * node = gui_create_context_menu_item(this, NULL, NULL, gettext("Security"), NULL, NULL, NULL, NULL, NULL, NODE_TYPE_CONTEXT_MENU_NETWORK_MENU, NULL); + + // We managed to create a temporary node + if (node != NULL) + { + // We have a no longer guaranteed to be valid normal surface + if (this->normal_surface != NULL) + { + // Free the normal surface + SDL_FreeSurface(this->normal_surface); + } + + // We have a no longer guaranteed to be valid selected surface + if (this->selected_surface != NULL) + { + // Free the selected surface + SDL_FreeSurface(this->selected_surface); + } + + // Steal the temporary node's display surfaces + this->normal_surface = node->normal_surface; + this->selected_surface = node->selected_surface; + + // NULL the temporary node's display surfaces (so they won't get freed with the rest of the node) + node->normal_surface = NULL; + node->selected_surface = NULL; + + // Free the temporay node + gui_destroy_node(node, 1); + } +} + +/** + * @brief Creates a network setup security context menu. + */ +static struct gui_node * gui_create_network_setup_security_menu(struct gui_node * this, struct gui_node ** first_node, struct gui_node ** previous_node, const char * display_name, const char * security) +{ + // The created node + struct gui_node * network_setup_security_menu_node = NULL; + + // Allocate the additional network setup security menu data + struct gui_network_setup_security_menu_node_data * network_setup_security_menu_node_data = calloc(1, sizeof(struct gui_network_setup_security_menu_node_data)); + + // We managed to allocate the additional network setup security menu data + if (network_setup_security_menu_node_data != NULL) + { + // Assign the access point security + network_setup_security_menu_node_data->security = security; + + // Create a node for the network setup security menu + network_setup_security_menu_node = gui_create_context_menu_item(this, first_node, previous_node, display_name, gui_activate_network_setup_security_menu, gui_deactivate_network_setup_security_menu, gui_update_basic_menu, gui_render_vertical_scrolling_list_menu, gui_invalidate_network_setup_security_menu, NODE_TYPE_CONTEXT_MENU_NETWORK_SETUP_SECURITY_MENU, network_setup_security_menu_node_data); + + // We failed to create a node for the network setup security menu + if (network_setup_security_menu_node == NULL) + { + // Free the additional menu data + free(network_setup_security_menu_node_data); + } + } + + // Return the created node + return network_setup_security_menu_node; +} + +#endif diff --git a/package/garlicui/src/icons/rect.svg b/package/garlicui/src/icons/rect.svg new file mode 100644 index 0000000000..2412e686b4 --- /dev/null +++ b/package/garlicui/src/icons/rect.svg @@ -0,0 +1,3 @@ + + + diff --git "a/package/garlicui/src/icons/\342\207\246.svg" "b/package/garlicui/src/icons/\342\207\246.svg" new file mode 100644 index 0000000000..6c7aafe542 --- /dev/null +++ "b/package/garlicui/src/icons/\342\207\246.svg" @@ -0,0 +1,2 @@ + + diff --git "a/package/garlicui/src/icons/\342\207\247.svg" "b/package/garlicui/src/icons/\342\207\247.svg" new file mode 100644 index 0000000000..93ed45c5f9 --- /dev/null +++ "b/package/garlicui/src/icons/\342\207\247.svg" @@ -0,0 +1,2 @@ + + diff --git "a/package/garlicui/src/icons/\342\207\251.svg" "b/package/garlicui/src/icons/\342\207\251.svg" new file mode 100644 index 0000000000..c8628ac100 --- /dev/null +++ "b/package/garlicui/src/icons/\342\207\251.svg" @@ -0,0 +1,2 @@ + + diff --git "a/package/garlicui/src/icons/\342\212\225.svg" "b/package/garlicui/src/icons/\342\212\225.svg" new file mode 100644 index 0000000000..464a3ac8d2 --- /dev/null +++ "b/package/garlicui/src/icons/\342\212\225.svg" @@ -0,0 +1,4 @@ + + + + diff --git "a/package/garlicui/src/icons/\342\217\216.svg" "b/package/garlicui/src/icons/\342\217\216.svg" new file mode 100644 index 0000000000..cca3735b26 --- /dev/null +++ "b/package/garlicui/src/icons/\342\217\216.svg" @@ -0,0 +1,2 @@ + + diff --git a/package/garlicui/src/io.h b/package/garlicui/src/io.h index c2e4dd2345..aa74da29d9 100644 --- a/package/garlicui/src/io.h +++ b/package/garlicui/src/io.h @@ -11,15 +11,10 @@ */ typedef char * (*textformatter)(const char *); -/** - * @brief The directory in which all boot related files are stored. - * - * This includes the boot script, rootfs, global configuration file, etc. - */ #ifdef __MACOSX__ -#define FOLDER_CONFIGURATION_BOOT_FOLDER "" +#define MOUNT_POINT "." #else -#define FOLDER_CONFIGURATION_BOOT_FOLDER "/media/boot/" +#define MOUNT_POINT "/media" #endif /** @@ -30,17 +25,28 @@ typedef char * (*textformatter)(const char *); /** * @brief The folder that contains the game library. */ -#define LIBRARY_FOLDER_PATH "/media/Library" +#define LIBRARY_FOLDER_PATH MOUNT_POINT "/Library" /** * @brief The folder that contains the icons. */ #define ICON_FOLDER_TEMPLATE_PATH "/usr/share/icons" +/** + * @brief The directory in which all boot related files are stored. + * + * This includes the boot script, rootfs, global configuration file, etc. + */ +#ifdef __MACOSX__ +#define FOLDER_CONFIGURATION_BOOT_FOLDER MOUNT_POINT +#else +#define FOLDER_CONFIGURATION_BOOT_FOLDER MOUNT_POINT "/boot" +#endif + /** * @brief The folder that contains the icon overrides. */ -#define ICON_FOLDER_PATH FOLDER_CONFIGURATION_BOOT_FOLDER "icons" +#define ICON_FOLDER_PATH FOLDER_CONFIGURATION_BOOT_FOLDER "/icons" /** * @brief The folder icon file name. diff --git a/package/garlicui/src/locale/af_ZA/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/af_ZA/LC_MESSAGES/garlicui.po index 7dcfb5ad43..217d9662a1 100644 --- a/package/garlicui/src/locale/af_ZA/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/af_ZA/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Verander Taal" msgid "No games found!" msgstr "Geen speletjies gevind nie!" + +msgid "Change Network" +msgstr "Verander Netwerk" + +msgid "Manual Configuration" +msgstr "Handleidingkonfigurasie" + +msgid "Security" +msgstr "Sekuriteit" + +msgid "Password" +msgstr "Wagwoord" diff --git a/package/garlicui/src/locale/am_ET/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/am_ET/LC_MESSAGES/garlicui.po index 8b48c387e3..32922d8da7 100644 --- a/package/garlicui/src/locale/am_ET/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/am_ET/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "ቋንቋ ቀይር" msgid "No games found!" msgstr "የጨዋታ አይደለም!" + +msgid "Change Network" +msgstr "የተለያዩ አድራሻ ቀይረዋል" + +msgid "Manual Configuration" +msgstr "መነሻ ቅንፍ" + +msgid "Security" +msgstr "ደህንነት" + +msgid "Password" +msgstr "መለያ" diff --git a/package/garlicui/src/locale/bg_BG/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/bg_BG/LC_MESSAGES/garlicui.po index d45906217b..51144431b9 100644 --- a/package/garlicui/src/locale/bg_BG/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/bg_BG/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Промяна на езика" msgid "No games found!" msgstr "Няма намерени игри!" + +msgid "Change Network" +msgstr "Промяна на мрежата" + +msgid "Manual Configuration" +msgstr "Ръчна конфигурация" + +msgid "Security" +msgstr "Сигурност" + +msgid "Password" +msgstr "Парола" diff --git a/package/garlicui/src/locale/ca_ES/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/ca_ES/LC_MESSAGES/garlicui.po index ab6640a050..7241f7dc9b 100644 --- a/package/garlicui/src/locale/ca_ES/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/ca_ES/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Canvia l'idioma" msgid "No games found!" msgstr "No s'han trobat jocs!" + +msgid "Change Network" +msgstr "Canvia la xarxa" + +msgid "Manual Configuration" +msgstr "Configuració manual" + +msgid "Security" +msgstr "Seguretat" + +msgid "Password" +msgstr "Contrasenya" diff --git a/package/garlicui/src/locale/cs_CZ/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/cs_CZ/LC_MESSAGES/garlicui.po index b3fa387378..d5a5297420 100644 --- a/package/garlicui/src/locale/cs_CZ/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/cs_CZ/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Změnit jazyk" msgid "No games found!" msgstr "Nebyly nalezeny žádné hry!" + +msgid "Change Network" +msgstr "Změnit síť" + +msgid "Manual Configuration" +msgstr "Ruční konfigurace" + +msgid "Security" +msgstr "Bezpečnost" + +msgid "Password" +msgstr "Heslo" diff --git a/package/garlicui/src/locale/da_DK/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/da_DK/LC_MESSAGES/garlicui.po index 67c67b2535..002e743025 100644 --- a/package/garlicui/src/locale/da_DK/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/da_DK/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Skift sprog" msgid "No games found!" msgstr "Ingen spil fundet!" + +msgid "Change Network" +msgstr "Skift netværk" + +msgid "Manual Configuration" +msgstr "Manuel konfiguration" + +msgid "Security" +msgstr "Sikkerhed" + +msgid "Password" +msgstr "Adgangskode" diff --git a/package/garlicui/src/locale/de_DE/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/de_DE/LC_MESSAGES/garlicui.po index 4e9fee6c21..ecf1f2d515 100644 --- a/package/garlicui/src/locale/de_DE/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/de_DE/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Sprache ändern" msgid "No games found!" msgstr "Keine Spiele gefunden!" + +msgid "Change Network" +msgstr "Netzwerk ändern" + +msgid "Manual Configuration" +msgstr "Manuelle Konfiguration" + +msgid "Security" +msgstr "Sicherheit" + +msgid "Password" +msgstr "Passwort" diff --git a/package/garlicui/src/locale/el_GR/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/el_GR/LC_MESSAGES/garlicui.po index 697e889a4a..c8287567f2 100644 --- a/package/garlicui/src/locale/el_GR/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/el_GR/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Αλλαγή γλώσσας" msgid "No games found!" msgstr "Δεν βρέθηκαν παιχνίδια!" + +msgid "Change Network" +msgstr "Αλλαγή δικτύου" + +msgid "Manual Configuration" +msgstr "Χειροκίνητη ρύθμιση" + +msgid "Security" +msgstr "Ασφάλεια" + +msgid "Password" +msgstr "Κωδικός" diff --git a/package/garlicui/src/locale/en_US/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/en_US/LC_MESSAGES/garlicui.po index d0bcaf2c61..e4d784d87c 100644 --- a/package/garlicui/src/locale/en_US/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/en_US/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Change Language" msgid "No games found!" msgstr "No games found!" + +msgid "Change Network" +msgstr "Change Network" + +msgid "Manual Configuration" +msgstr "Manual Configuration" + +msgid "Security" +msgstr "Security" + +msgid "Password" +msgstr "Password" diff --git a/package/garlicui/src/locale/es_ES/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/es_ES/LC_MESSAGES/garlicui.po index 869a6d0626..c09ccbc344 100644 --- a/package/garlicui/src/locale/es_ES/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/es_ES/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Cambiar idioma" msgid "No games found!" msgstr "No se encontraron juegos!" + +msgid "Change Network" +msgstr "Cambiar red" + +msgid "Manual Configuration" +msgstr "Configuración manual" + +msgid "Security" +msgstr "Seguridad" + +msgid "Password" +msgstr "Contraseña" diff --git a/package/garlicui/src/locale/et_EE/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/et_EE/LC_MESSAGES/garlicui.po index 3fc8289790..389d9217c6 100644 --- a/package/garlicui/src/locale/et_EE/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/et_EE/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Muuda keelt" msgid "No games found!" msgstr "Mänge ei leitud!" + +msgid "Change Network" +msgstr "Muuda võrku" + +msgid "Manual Configuration" +msgstr "Käsitsi seadistamine" + +msgid "Security" +msgstr "Turvalisus" + +msgid "Password" +msgstr "Parool" diff --git a/package/garlicui/src/locale/fi_FI/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/fi_FI/LC_MESSAGES/garlicui.po index 2d9c517097..0991b258c3 100644 --- a/package/garlicui/src/locale/fi_FI/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/fi_FI/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Vaihda kieli" msgid "No games found!" msgstr "Ei löytynyt pelejä!" + +msgid "Change Network" +msgstr "Vaihda verkkoa" + +msgid "Manual Configuration" +msgstr "Manuaalinen konfigurointi" + +msgid "Security" +msgstr "Turvallisuus" + +msgid "Password" +msgstr "Salasana" diff --git a/package/garlicui/src/locale/fil_PH/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/fil_PH/LC_MESSAGES/garlicui.po index 14b16f0af9..6e76996a54 100644 --- a/package/garlicui/src/locale/fil_PH/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/fil_PH/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Baguhin ang Wika" msgid "No games found!" msgstr "Walang mga laro na natagpuan!" + +msgid "Change Network" +msgstr "Baguhin ang Network" + +msgid "Manual Configuration" +msgstr "Manual na Konfigurasyon" + +msgid "Security" +msgstr "Seguridad" + +msgid "Password" +msgstr "Password" diff --git a/package/garlicui/src/locale/fr_FR/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/fr_FR/LC_MESSAGES/garlicui.po index f5250e583a..c8727b9a9f 100644 --- a/package/garlicui/src/locale/fr_FR/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/fr_FR/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Changer de langue" msgid "No games found!" msgstr "Aucun jeu trouvé !" + +msgid "Change Network" +msgstr "Changer de réseau" + +msgid "Manual Configuration" +msgstr "Configuration manuelle" + +msgid "Security" +msgstr "Sécurité" + +msgid "Password" +msgstr "Mot de passe" diff --git a/package/garlicui/src/locale/he_IL/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/he_IL/LC_MESSAGES/garlicui.po index bdc1c390c1..4d984c7af2 100644 --- a/package/garlicui/src/locale/he_IL/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/he_IL/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "שינוי שפה" msgid "No games found!" msgstr "לא נמצאו משחקים!" + +msgid "Change Network" +msgstr "שנה רשת" + +msgid "Manual Configuration" +msgstr "הגדרה ידנית" + +msgid "Security" +msgstr "ביטחון" + +msgid "Password" +msgstr "סיסמה" diff --git a/package/garlicui/src/locale/hi_IN/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/hi_IN/LC_MESSAGES/garlicui.po index 2ea8d3dc95..2ec9b31a1b 100644 --- a/package/garlicui/src/locale/hi_IN/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/hi_IN/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "भाषा बदलें" msgid "No games found!" msgstr "कोई खेल नहीं मिला!" + +msgid "Change Network" +msgstr "नेटवर्क बदलें" + +msgid "Manual Configuration" +msgstr "मैनुअल कॉन्फ़िगरेशन" + +msgid "Security" +msgstr "सुरक्षा" + +msgid "Password" +msgstr "पासवर्ड" diff --git a/package/garlicui/src/locale/hr_HR/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/hr_HR/LC_MESSAGES/garlicui.po index 13a2b40b3b..9261e21f21 100644 --- a/package/garlicui/src/locale/hr_HR/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/hr_HR/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Promijeni jezik" msgid "No games found!" msgstr "Nema pronađenih igara!" + +msgid "Change Network" +msgstr "Promijeni mrežu" + +msgid "Manual Configuration" +msgstr "Ručna konfiguracija" + +msgid "Security" +msgstr "Sigurnost" + +msgid "Password" +msgstr "Lozinka" diff --git a/package/garlicui/src/locale/hu_HU/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/hu_HU/LC_MESSAGES/garlicui.po index a508a0a8e8..e172262a62 100644 --- a/package/garlicui/src/locale/hu_HU/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/hu_HU/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Nyelv váltása" msgid "No games found!" msgstr "Nem található játék!" + +msgid "Change Network" +msgstr "Hálózat megváltoztatása" + +msgid "Manual Configuration" +msgstr "Kézi konfiguráció" + +msgid "Security" +msgstr "Biztonság" + +msgid "Password" +msgstr "Jelszó" diff --git a/package/garlicui/src/locale/id_ID/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/id_ID/LC_MESSAGES/garlicui.po index c376a9492b..494ce9d380 100644 --- a/package/garlicui/src/locale/id_ID/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/id_ID/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Ubah Bahasa" msgid "No games found!" msgstr "Tidak ada game yang ditemukan!" + +msgid "Change Network" +msgstr "Ubah Jaringan" + +msgid "Manual Configuration" +msgstr "Konfigurasi Manual" + +msgid "Security" +msgstr "Keamanan" + +msgid "Password" +msgstr "Kata Sandi" diff --git a/package/garlicui/src/locale/is_IS/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/is_IS/LC_MESSAGES/garlicui.po index 6e22c4e1a7..2aa7b069a4 100644 --- a/package/garlicui/src/locale/is_IS/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/is_IS/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Breyta tungumáli" msgid "No games found!" msgstr "Engar leiki fundust!" + +msgid "Change Network" +msgstr "Breyta neti" + +msgid "Manual Configuration" +msgstr "Handstilltu uppsetning" + +msgid "Security" +msgstr "Öryggi" + +msgid "Password" +msgstr "Lykilorð" diff --git a/package/garlicui/src/locale/it_IT/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/it_IT/LC_MESSAGES/garlicui.po index 3a7d5fd85f..84bd2d3492 100644 --- a/package/garlicui/src/locale/it_IT/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/it_IT/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Cambia lingua" msgid "No games found!" msgstr "Nessun gioco trovato!" + +msgid "Change Network" +msgstr "Cambia rete" + +msgid "Manual Configuration" +msgstr "Configurazione manuale" + +msgid "Security" +msgstr "Sicurezza" + +msgid "Password" +msgstr "Password" diff --git a/package/garlicui/src/locale/ja_JP/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/ja_JP/LC_MESSAGES/garlicui.po index 808ed8f10e..028231e6f9 100644 --- a/package/garlicui/src/locale/ja_JP/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/ja_JP/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "言語を変更" msgid "No games found!" msgstr "ゲームが見つかりません!" + +msgid "Change Network" +msgstr "ネットワークを変更" + +msgid "Manual Configuration" +msgstr "手動設定" + +msgid "Security" +msgstr "セキュリティ" + +msgid "Password" +msgstr "パスワード" diff --git a/package/garlicui/src/locale/ko_KR/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/ko_KR/LC_MESSAGES/garlicui.po index 5f06248eb7..f9b0fbd6fe 100644 --- a/package/garlicui/src/locale/ko_KR/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/ko_KR/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "언어 변경" msgid "No games found!" msgstr "게임을 찾을 수 없습니다!" + +msgid "Change Network" +msgstr "네트워크 변경" + +msgid "Manual Configuration" +msgstr "수동 구성" + +msgid "Security" +msgstr "보안" + +msgid "Password" +msgstr "비밀번호" diff --git a/package/garlicui/src/locale/lt_LT/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/lt_LT/LC_MESSAGES/garlicui.po index 050db4e7a7..4062231d2d 100644 --- a/package/garlicui/src/locale/lt_LT/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/lt_LT/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Pakeisti kalbą" msgid "No games found!" msgstr "Žaidimų nerasta!" + +msgid "Change Network" +msgstr "Pakeisti tinklą" + +msgid "Manual Configuration" +msgstr "Rankinis konfigūravimas" + +msgid "Security" +msgstr "Sauga" + +msgid "Password" +msgstr "Slaptažodis" diff --git a/package/garlicui/src/locale/lv_LV/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/lv_LV/LC_MESSAGES/garlicui.po index e4aba1a6fe..b342c17e29 100644 --- a/package/garlicui/src/locale/lv_LV/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/lv_LV/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Mainīt valodu" msgid "No games found!" msgstr "Nav atrastas spēles!" + +msgid "Change Network" +msgstr "Mainīt tīklu" + +msgid "Manual Configuration" +msgstr "Manuālā konfigurācija" + +msgid "Security" +msgstr "Drošība" + +msgid "Password" +msgstr "Parole" diff --git a/package/garlicui/src/locale/ms_MY/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/ms_MY/LC_MESSAGES/garlicui.po index 82d7f00d78..ff315e8c91 100644 --- a/package/garlicui/src/locale/ms_MY/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/ms_MY/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Tukar Bahasa" msgid "No games found!" msgstr "Tiada permaink ditemui!" + +msgid "Change Network" +msgstr "Tukar Rangkaian" + +msgid "Manual Configuration" +msgstr "Konfigurasi Manual" + +msgid "Security" +msgstr "Keselamatan" + +msgid "Password" +msgstr "Kata Laluan" diff --git a/package/garlicui/src/locale/nb_NO/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/nb_NO/LC_MESSAGES/garlicui.po index 03972e558d..3655707866 100644 --- a/package/garlicui/src/locale/nb_NO/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/nb_NO/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Endre språk" msgid "No games found!" msgstr "Ingen spill funnet!" + +msgid "Change Network" +msgstr "Endre nettverk" + +msgid "Manual Configuration" +msgstr "Manuell konfigurasjon" + +msgid "Security" +msgstr "Sikkerhet" + +msgid "Password" +msgstr "Passord" diff --git a/package/garlicui/src/locale/nl_NL/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/nl_NL/LC_MESSAGES/garlicui.po index c82180c578..c1f9517db2 100644 --- a/package/garlicui/src/locale/nl_NL/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/nl_NL/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Taal wijzigen" msgid "No games found!" msgstr "Geen spellen gevonden!" + +msgid "Change Network" +msgstr "Netwerk wijzigen" + +msgid "Manual Configuration" +msgstr "Handmatige configuratie" + +msgid "Security" +msgstr "Beveiliging" + +msgid "Password" +msgstr "Wachtwoord" diff --git a/package/garlicui/src/locale/pl_PL/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/pl_PL/LC_MESSAGES/garlicui.po index c773f3c5f5..852f760e69 100644 --- a/package/garlicui/src/locale/pl_PL/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/pl_PL/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Zmień język" msgid "No games found!" msgstr "Nie znaleziono żadnych gier!" + +msgid "Change Network" +msgstr "Zmień sieć" + +msgid "Manual Configuration" +msgstr "Konfiguracja ręczna" + +msgid "Security" +msgstr "Bezpieczeństwo" + +msgid "Password" +msgstr "Hasło" diff --git a/package/garlicui/src/locale/pt_BR/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/pt_BR/LC_MESSAGES/garlicui.po index 57554b92a0..4ef6ab2952 100644 --- a/package/garlicui/src/locale/pt_BR/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/pt_BR/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Alterar Idioma" msgid "No games found!" msgstr "Nenhum jogo encontrado!" + +msgid "Change Network" +msgstr "Alterar Rede" + +msgid "Manual Configuration" +msgstr "Configuração Manual" + +msgid "Security" +msgstr "Segurança" + +msgid "Password" +msgstr "Senha" diff --git a/package/garlicui/src/locale/pt_PT/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/pt_PT/LC_MESSAGES/garlicui.po index 84a16cf7c9..94887719bd 100644 --- a/package/garlicui/src/locale/pt_PT/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/pt_PT/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Alterar Idioma" msgid "No games found!" msgstr "Não foram encontrados jogos!" + +msgid "Change Network" +msgstr "Alterar Rede" + +msgid "Manual Configuration" +msgstr "Configuração Manual" + +msgid "Security" +msgstr "Segurança" + +msgid "Password" +msgstr "Palavra-passe" diff --git a/package/garlicui/src/locale/ro_RO/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/ro_RO/LC_MESSAGES/garlicui.po index 4790e6693c..bca6a154de 100644 --- a/package/garlicui/src/locale/ro_RO/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/ro_RO/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Schimbă Limba" msgid "No games found!" msgstr "Nu s-au găsit jocuri!" + +msgid "Change Network" +msgstr "Schimbare rețea" + +msgid "Manual Configuration" +msgstr "Configurare manuală" + +msgid "Security" +msgstr "Securitate" + +msgid "Password" +msgstr "Parolă" diff --git a/package/garlicui/src/locale/ru_RU/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/ru_RU/LC_MESSAGES/garlicui.po index 3f25186a4a..e8c1e6a3aa 100644 --- a/package/garlicui/src/locale/ru_RU/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/ru_RU/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Изменить язык" msgid "No games found!" msgstr "Игры не найдены!" + +msgid "Change Network" +msgstr "Изменить сеть" + +msgid "Manual Configuration" +msgstr "Ручная конфигурация" + +msgid "Security" +msgstr "Безопасность" + +msgid "Password" +msgstr "Пароль" diff --git a/package/garlicui/src/locale/sk_SK/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/sk_SK/LC_MESSAGES/garlicui.po index ab9a8fe272..a9f9682d5e 100644 --- a/package/garlicui/src/locale/sk_SK/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/sk_SK/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Zmeniť jazyk" msgid "No games found!" msgstr "Žiadne hry sa nenašli!" + +msgid "Change Network" +msgstr "Zmeniť sieť" + +msgid "Manual Configuration" +msgstr "Manuálna konfigurácia" + +msgid "Security" +msgstr "Bezpečnosť" + +msgid "Password" +msgstr "Heslo" diff --git a/package/garlicui/src/locale/sl_SI/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/sl_SI/LC_MESSAGES/garlicui.po index 94e923416b..97145a6446 100644 --- a/package/garlicui/src/locale/sl_SI/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/sl_SI/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Spremeni jezik" msgid "No games found!" msgstr "Ni najdenih iger!" + +msgid "Change Network" +msgstr "Spremeni omrežje" + +msgid "Manual Configuration" +msgstr "Ročna konfiguracija" + +msgid "Security" +msgstr "Varnost" + +msgid "Password" +msgstr "Geslo" diff --git a/package/garlicui/src/locale/sr_RS/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/sr_RS/LC_MESSAGES/garlicui.po index e6c2a68cb6..2cffddf28d 100644 --- a/package/garlicui/src/locale/sr_RS/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/sr_RS/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Promeni jezik" msgid "No games found!" msgstr "Nisu pronađene igre!" + +msgid "Change Network" +msgstr "Promeni mrežu" + +msgid "Manual Configuration" +msgstr "Ručna konfiguracija" + +msgid "Security" +msgstr "Bezbednost" + +msgid "Password" +msgstr "Lozinka" diff --git a/package/garlicui/src/locale/sv_SE/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/sv_SE/LC_MESSAGES/garlicui.po index 27e8c2c46b..78730a299a 100644 --- a/package/garlicui/src/locale/sv_SE/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/sv_SE/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Byt språk" msgid "No games found!" msgstr "Inga spel hittades!" + +msgid "Change Network" +msgstr "Ändra nätverk" + +msgid "Manual Configuration" +msgstr "Manuell konfiguration" + +msgid "Security" +msgstr "Säkerhet" + +msgid "Password" +msgstr "Lösenord" diff --git a/package/garlicui/src/locale/sw_KE/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/sw_KE/LC_MESSAGES/garlicui.po index 461ec7bdf8..5cfe5ad724 100644 --- a/package/garlicui/src/locale/sw_KE/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/sw_KE/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Badilisha Lugha" msgid "No games found!" msgstr "Hakuna michezo iliyopatikana!" + +msgid "Change Network" +msgstr "Badilisha Mtandao" + +msgid "Manual Configuration" +msgstr "Usanidi wa Manuali" + +msgid "Security" +msgstr "Usalama" + +msgid "Password" +msgstr "Nenosiri" diff --git a/package/garlicui/src/locale/th_TH/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/th_TH/LC_MESSAGES/garlicui.po index 9b62c48636..d718c290f8 100644 --- a/package/garlicui/src/locale/th_TH/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/th_TH/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "เปลี่ยนภาษา" msgid "No games found!" msgstr "ไม่พบเกม!" + +msgid "Change Network" +msgstr "เปลี่ยนเครือข่าย" + +msgid "Manual Configuration" +msgstr "การกำหนดค่าด้วยตนเอง" + +msgid "Security" +msgstr "ความปลอดภัย" + +msgid "Password" +msgstr "รหัสผ่าน" diff --git a/package/garlicui/src/locale/tr_TR/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/tr_TR/LC_MESSAGES/garlicui.po index 55e8b18ada..198cd59316 100644 --- a/package/garlicui/src/locale/tr_TR/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/tr_TR/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Dili Değiştir" msgid "No games found!" msgstr "Oyun bulunamadı!" + +msgid "Change Network" +msgstr "Ağı Değiştir" + +msgid "Manual Configuration" +msgstr "Manuel Yapılandırma" + +msgid "Security" +msgstr "Güvenlik" + +msgid "Password" +msgstr "Parola" diff --git a/package/garlicui/src/locale/uk_UA/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/uk_UA/LC_MESSAGES/garlicui.po index 7fcedaaffe..ab7741c454 100644 --- a/package/garlicui/src/locale/uk_UA/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/uk_UA/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Змінити мову" msgid "No games found!" msgstr "Ігор не знайдено!" + +msgid "Change Network" +msgstr "Змінити мережу" + +msgid "Manual Configuration" +msgstr "Ручна конфігурація" + +msgid "Security" +msgstr "Безпека" + +msgid "Password" +msgstr "Пароль" diff --git a/package/garlicui/src/locale/vi_VN/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/vi_VN/LC_MESSAGES/garlicui.po index f79194980a..34bf341d8f 100644 --- a/package/garlicui/src/locale/vi_VN/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/vi_VN/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Thay đổi ngôn ngữ" msgid "No games found!" msgstr "Không tìm thấy trò chơi!" + +msgid "Change Network" +msgstr "Thay Đổi Mạng" + +msgid "Manual Configuration" +msgstr "Cấu Hình Thủ Công" + +msgid "Security" +msgstr "Bảo mật" + +msgid "Password" +msgstr "Mật khẩu" diff --git a/package/garlicui/src/locale/zh_CN/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/zh_CN/LC_MESSAGES/garlicui.po index b75a25df36..c790da0a04 100644 --- a/package/garlicui/src/locale/zh_CN/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/zh_CN/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "更改语言" msgid "No games found!" msgstr "未找到游戏!" + +msgid "Change Network" +msgstr "更改网络" + +msgid "Manual Configuration" +msgstr "手动配置" + +msgid "Security" +msgstr "安全性" + +msgid "Password" +msgstr "密码" diff --git a/package/garlicui/src/locale/zh_TW/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/zh_TW/LC_MESSAGES/garlicui.po index 0cef47507d..3bbda8eb6d 100644 --- a/package/garlicui/src/locale/zh_TW/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/zh_TW/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "更改語言" msgid "No games found!" msgstr "未找到遊戲!" + +msgid "Change Network" +msgstr "更改網路" + +msgid "Manual Configuration" +msgstr "手動設定" + +msgid "Security" +msgstr "安全性" + +msgid "Password" +msgstr "密碼" diff --git a/package/garlicui/src/locale/zu_ZA/LC_MESSAGES/garlicui.po b/package/garlicui/src/locale/zu_ZA/LC_MESSAGES/garlicui.po index 3ad9b501e4..b0e7611eeb 100644 --- a/package/garlicui/src/locale/zu_ZA/LC_MESSAGES/garlicui.po +++ b/package/garlicui/src/locale/zu_ZA/LC_MESSAGES/garlicui.po @@ -66,3 +66,15 @@ msgstr "Shintsha IsiZulu" msgid "No games found!" msgstr "Awunazwe imidlalo!" + +msgid "Change Network" +msgstr "Hamba i-Network" + +msgid "Manual Configuration" +msgstr "Konfigurasha Yedwala" + +msgid "Security" +msgstr "Isiqinisekiso" + +msgid "Password" +msgstr "Iphasiwedi" diff --git a/package/garlicui/src/network.c b/package/garlicui/src/network.c new file mode 100644 index 0000000000..af0019f3a4 --- /dev/null +++ b/package/garlicui/src/network.c @@ -0,0 +1,175 @@ +#ifndef __linux__ +#include +#endif + +#include "network.h" + +/** + * @brief Changes the application's wifi access point. + */ +void network_change_access_point(struct network_access_point * access_point) +{ + // TODO connect to the access point + // https://networkmanager.dev/docs/libnm/latest/NMClient.html#nm-client-activate-connection-async + // https://networkmanager.dev/docs/libnm/latest/NMClient.html#nm-client-activate-connection-finish + // https://networkmanager.dev/docs/libnm/latest/NMClient.html#nm-client-add-and-activate-connection-async + // https://networkmanager.dev/docs/libnm/latest/NMClient.html#nm-client-add-and-activate-connection-finish +} + +/** + * @brief Returns a list of wifi devices. + * @see https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/31a12ee/examples/C/glib/get-ap-info-libnm.c + * + * The caller must explicitly free the returned item by calling network_free_access_points. + */ +int network_get_access_points(struct network_access_point *** access_points) +{ + int access_point_count = 0; + + *access_points = NULL; + +#ifdef __linux__ + GError * error = NULL; + NMClient * client = nm_client_new(NULL, &error); + + if (!client) + { + goto free_error; + } + + const GPtrArray * devices = nm_client_get_devices(client); + NMDevice * device = NULL; + + for (int i = 0, n = devices->len; i < n; i++) + { + NMDevice * current_device = g_ptr_array_index(devices, i); + + if (!NM_IS_DEVICE_WIFI(current_device)) + { + continue; + } + + device = current_device; + break; + } + + if (device == NULL) + { + goto free_client; + } + + NMAccessPoint * active_ap = nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED ? nm_device_wifi_get_active_access_point(NM_DEVICE_WIFI(device)) : NULL; + const GPtrArray * aps = nm_device_wifi_get_access_points(NM_DEVICE_WIFI(device)); +#else + GPtrArray * aps = g_ptr_array_new_full(2, g_object_unref); + g_ptr_array_add(aps, "Dummy"); +#endif + + for (int i = 0, n = aps->len; i < n; i++) + { +#ifdef __linux__ + NMAccessPoint * ap = g_ptr_array_index(aps, i); + GBytes * ssid_bytes = nm_access_point_get_ssid(ap); + char * ssid = nm_utils_ssid_to_utf8(g_bytes_get_data(ssid_bytes, NULL), g_bytes_get_size(ssid_bytes)); + guint32 flags = nm_access_point_get_flags(ap); + guint32 wpa_flags = nm_access_point_get_wpa_flags(ap); + guint32 rsn_flags = nm_access_point_get_rsn_flags(ap); + char * security = "None"; + bool active = active_ap == ap; + + if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_flags == NM_802_11_AP_SEC_NONE) && (rsn_flags == NM_802_11_AP_SEC_NONE)) + { + security = "WEP"; + } + + if (wpa_flags != NM_802_11_AP_SEC_NONE) + { + security = "WPA"; + } + + if ((rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) || (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) + { + security = "WPA2"; + } + + if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE) + { + security = "WPA3"; + } +#else + char * ssid = g_ptr_array_index(aps, i); + char * security = "WPA2"; + bool active = i == 0; +#endif + + // Allocate memory for the access point + struct network_access_point * access_point = (struct network_access_point *)calloc(1, sizeof(struct network_access_point)); + + // We managed to allocate the memory for the access point + if (access_point != NULL) + { + // Populate the access point + access_point->ssid = ssid; + access_point->active = active; + access_point->security = security; + + // Resize the access points list to accommodate one more access point + void * resized_access_points = realloc(*access_points, sizeof(struct network_access_point *) * (access_point_count + 1)); + + // We managed to resize the playlist access points list + if (resized_access_points) + { + // Override the old pointer (some libc implementations can relocate the buffer) + *access_points = resized_access_points; + + // Add the populated playlist item to the list + (*access_points)[access_point_count++] = access_point; + } + } +#ifdef __linux__ + g_free(ssid); +#endif + } +#ifdef __linux__ +free_client: + g_object_unref(client); + +free_error: + g_error_free(error); +#endif + + return access_point_count; +} + +/** + * @brief Frees the given wifi access points. + */ +void network_free_access_points(struct network_access_point ** access_points, int count) +{ + // We've been given valid parameters + if (access_points != NULL && count != 0) + { + // Iterate all access points + for (int i = 0; i < count; i++) + { + // Free the access point + network_free_access_point(access_points[i]); + } + + // Free the list itself + free(access_points); + } +} + +/** + * @brief Free the given wifi access point. + */ +void network_free_access_point(struct network_access_point * access_point) +{ + // We've been given a seemingly valid access point + if (access_point != NULL) + { + // Free the access point itself + free(access_point); + } +} diff --git a/package/garlicui/src/network.h b/package/garlicui/src/network.h new file mode 100644 index 0000000000..d9e9a980b5 --- /dev/null +++ b/package/garlicui/src/network.h @@ -0,0 +1,45 @@ +#ifndef NETWORK_H +#define NETWORK_H + +#include +#include + +#ifdef __linux__ +#include +#endif + +/** + * @brief A single access point. + */ +struct network_access_point +{ + char * ssid; + char * security; + char * password; + bool active; +}; + +/** + * @brief Changes the application's access point. + */ +void network_change_access_point(struct network_access_point * access_point); + +/** + * @brief Returns a list of access points. + * @see https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/31a12ee/examples/C/glib/get-ap-info-libnm.c + * + * The caller must explicitly free the returned item by calling network_free_access_points. + */ +int network_get_access_points(struct network_access_point *** access_points); + +/** + * @brief Frees the given access points. + */ +void network_free_access_points(struct network_access_point ** access_points, int count); + +/** + * @brief Free the given access point. + */ +void network_free_access_point(struct network_access_point * access_point); + +#endif diff --git a/package/garlicui/src/osk.h b/package/garlicui/src/osk.h new file mode 100644 index 0000000000..8b63824b7c --- /dev/null +++ b/package/garlicui/src/osk.h @@ -0,0 +1,93 @@ +#ifndef OSK_H +#define OSK_H + +#define OSK_COLS_COUNT 11 +#define OSK_ROWS_COUNT 4 +#define OSK_GRID_SIZE OSK_COLS_COUNT * OSK_ROWS_COUNT +#define OSK_GRID_COUNT 3 + +/** + * @brief A data structure representing the on-screen keyboard context. + */ +struct osk_context +{ + int grid; /** The on-screen keyboard grid */ + int index; /** The on-screen keyboard index */ + bool lock; /** Whether to lock the on-screen keyboard buffer */ + bool sync; /** Whether to sync the on-screen keyboard buffer */ + bool apply; /** Whether to apply the on-screen keyboard buffer */ + char * buffer; /** The on-screen keyboard buffer */ +}; + +static const char * symbols_page1_grid[] = { + "1","2","3","4","5","6","7","8","9","0","⇦", + "!","\"","#","$","%","&","'","*","(",")","⏎", + "+",",","-","~","/",":",";","=","<",">","⇩", + "?","@","[","\\","]","^","_","|","{","}","⊕" +}; + +static const char * uppercase_grid[] = { + "1","2","3","4","5","6","7","8","9","0","⇦", + "Q","W","E","R","T","Y","U","I","O","P","⏎", + "A","S","D","F","G","H","J","K","L","+","⇩", + "Z","X","C","V","B","N","M"," ","_","/","⊕" +}; + +static const char * lowercase_grid[] = { + "1","2","3","4","5","6","7","8","9","0","⇦", + "q","w","e","r","t","y","u","i","o","p","⏎", + "a","s","d","f","g","h","j","k","l","@","⇧", + "z","x","c","v","b","n","m"," ","-",".","⊕" +}; + +#ifdef USE_EXTRA_GRIDS +static const char * hiragana_page1_grid[] = { + "あ","い","う","え","お","ら","り","る","れ","ろ","⇦", + "か","き","く","け","こ","が","ぎ","ぐ","げ","ご","⏎", + "さ","し","す","せ","そ","ざ","じ","ず","ぜ","ぞ","⇧", + "た","ち","つ","て","と","だ","ぢ","づ","で","ど","⊕" +}; + +static const char * hiragana_page2_grid[] = { + "な","に","ぬ","ね","の","ば","び","ぶ","べ","ぼ","⇦", + "は","ひ","ふ","へ","ほ","ぱ","ぴ","ぷ","ぺ","ぽ","⏎", + "ま","み","む","め","も","ん","っ","ゃ","ゅ","ょ","⇧", + "や","ゆ","よ","わ","を","ぁ","ぃ","ぅ","ぇ","ぉ","⊕" +}; + +static const char * katakana_page1_grid[] = { + "ア","イ","ウ","エ","オ","ラ","リ","ル","レ","ロ","⇦", + "カ","キ","ク","ケ","コ","ガ","ギ","グ","ゲ","ゴ","⏎", + "サ","シ","ス","セ","ソ","ザ","ジ","ズ","ゼ","ゾ","⇧", + "タ","チ","ツ","テ","ト","ダ","ヂ","ヅ","デ","ド","⊕" +}; + +static const char * katakana_page2_grid[] = { + "ナ","ニ","ヌ","ネ","ノ","バ","ビ","ブ","ベ","ボ","⇦", + "ハ","ヒ","フ","ヘ","ホ","パ","ピ","プ","ペ","ポ","⏎", + "マ","ミ","ム","メ","モ","ン","ッ","ャ","ュ","ョ","⇧", + "ヤ","ユ","ヨ","ワ","ヲ","ァ","ィ","ゥ","ェ","ォ","⊕" +}; + +static const char * korean_page1_grid[] = { + "1","2","3","4","5","6","7","8","9","0","⇦", + "ㅂ","ㅈ","ㄷ","ㄱ","ㅅ","ㅛ","ㅕ","ㅑ","ㅐ","ㅔ","⏎", + "ㅁ","ㄴ","ㅇ","ㄹ","ㅎ","ㅗ","ㅓ","ㅏ","ㅣ","ㅒ","ㅖ", + "ㅋ","ㅌ","ㅊ","ㅍ","ㅠ","ㅜ","ㅡ"," ",",",".","⊕" +}; +#endif // USE_EXTRA_GRIDS + +static const char ** osk_grids[] = { + symbols_page1_grid, + uppercase_grid, + lowercase_grid, +#ifdef USE_EXTRA_GRIDS + hiragana_page1_grid, + hiragana_page2_grid, + katakana_page1_grid, + katakana_page2_grid, + korean_page1_grid, +#endif // USE_EXTRA_GRIDS +}; + +#endif diff --git a/package/garlicui/src/osk_context_menu.h b/package/garlicui/src/osk_context_menu.h new file mode 100644 index 0000000000..1d164f0a1a --- /dev/null +++ b/package/garlicui/src/osk_context_menu.h @@ -0,0 +1,648 @@ +#ifndef OSK_CONTEXT_MENU_H +#define OSK_CONTEXT_MENU_H + +#include + +#include "osk.h" +#include "io.h" + +// The current scroll repeat interval +static Uint32 scroll_repeat_interval = SCROLL_EVENT_INITIAL_REPEAT_INTERVAL; + +/** + * @brief Gets the Unicode glyph for the given UTF-8 string. + */ +static Uint16 osk_get_glyph(const char * utf8_str) +{ + UChar u_str[1]; + UErrorCode errorCode = U_ZERO_ERROR; + + u_strFromUTF8(u_str, sizeof(u_str) / sizeof(u_str[0]), NULL, utf8_str, -1, &errorCode); + + if (U_FAILURE(errorCode)) { + fprintf(stderr, "Error converting UTF-8 to Unicode: %s\n", u_errorName(errorCode)); + exit(EXIT_FAILURE); + } + + return u_str[0]; +} + +/** + * @brief Renders the on-screen keyboard key surface. + */ +static SDL_Surface * osk_render_key(struct gui_context * context, const char * key, int index) +{ + SDL_Surface * text_surface; + + if (TTF_GlyphIsProvided(context->fonts.small, osk_get_glyph(key)) > 0) + { + text_surface = TTF_RenderUTF8_Blended(context->fonts.small, key, SDL_ToSDLColor(context->colors.osk.foreground)); + } + else + { + char * fallback = malloc(strlen(key) + 4 + 1); + strcpy(fallback, key); + strcat(fallback, ".svg"); + text_surface = icon_load(fallback, 20 * context->surfaces.scale_factor, 20 * context->surfaces.scale_factor, &context->colors.osk.foreground); + free(fallback); + if (text_surface == NULL) + { + return NULL; + } + } + + SDL_Surface * surface = context->surfaces.overlays.osk.surface; + + int x = surface->w / OSK_COLS_COUNT; + int y = surface->h / OSK_ROWS_COUNT; + + // Render the description text onto the surface + SDL_Rect text_position; + text_position.x = x * (index % OSK_COLS_COUNT) + ((x - text_surface->w) >> 1); + text_position.y = y * (index / OSK_COLS_COUNT | 0) + ((y - text_surface->h) >> 1); + text_position.w = text_surface->w; + text_position.h = text_surface->h; + SDL_BlitSurfaceAlpha(text_surface, NULL, surface, &text_position); + + // Free the description text surface + SDL_FreeSurface(text_surface); + + return surface; +} + +/** + * @brief Renders the on-screen keyboard grid. + */ +static void osk_render_grid(struct gui_context * context) +{ + const char ** grid = osk_grids[context->osk.grid]; + + for (int i = 0, n = OSK_GRID_SIZE; i < n; i++) + { + osk_render_key(context, grid[i], i); + } +} + +/** + * @brief Modulo operation that always returns a positive result. + */ +static int osk_mod(int a, int b) +{ + int result = a % b; + return result < 0 ? result + b : result; +} + +/** + * @brief Compute the on-screen keyboard index + */ +static int osk_index(int index, int x, int y) +{ + if (x != 0) + { + int delta = OSK_COLS_COUNT * (index / OSK_COLS_COUNT | 0); + + index = osk_mod(index + (x < 0 ? -1 : 1) - delta, OSK_COLS_COUNT) + delta; + } + + if (y != 0) + { + index = osk_mod(index + (y < 0 ? - OSK_COLS_COUNT : OSK_COLS_COUNT), OSK_GRID_SIZE); + } + + return index; +} + +/** + * @brief Initializes the on-screen keyboard menu. + */ +static int osk_initialize_menu(struct gui_context * context) +{ + // The RGB color components + Uint8 r, g, b; + + // Set the on-screen keyboard background color + GET_RGB_COMPONENTS(context->colors.osk.background, r, g, b); + context->colors.osk.background = SDL_MapRGB(context->surfaces.screen->format, r, g, b); + + // Set the on-screen keyboard foreground color + GET_RGB_COMPONENTS(context->colors.osk.foreground, r, g, b); + context->colors.osk.foreground = SDL_MapRGB(context->surfaces.screen->format, r, g, b); + + // Render the on-screen keyboard overlay + context->surfaces.overlays.osk.surface = icon_rounded_rectangle(context->surfaces.screen->w, context->surfaces.screen->h >> 1, 1, context->colors.osk.background); + + // We failed to render the on-screen keyboard overlay + if (context->surfaces.overlays.osk.surface == NULL) + { + return -1; + } + + // Initialize the on-screen keyboard grid index + context->osk.grid = OSK_GRID_COUNT - 1; + + // Render the on-screen keyboard grids + osk_render_grid(context); + + // Render the on-screen keyboard active key overlay + context->surfaces.overlays.osk_active_key.surface = icon_load("rect.svg", context->surfaces.screen->w / OSK_COLS_COUNT, (context->surfaces.screen->h >> 1) / OSK_ROWS_COUNT, &context->colors.osk.foreground); + + // We failed to render the on-screen keyboard active key overlay + if (context->surfaces.overlays.osk_active_key.surface == NULL) + { + return -1; + } + + // Calculate the on-screen keyboard overlay position + context->surfaces.overlays.osk.position.x = 0; + context->surfaces.overlays.osk.position.y = context->surfaces.screen->h - context->surfaces.overlays.osk.surface->h; + context->surfaces.overlays.osk.position.w = context->surfaces.overlays.osk.surface->w; + context->surfaces.overlays.osk.position.h = context->surfaces.overlays.osk.surface->h; + + // Calculate the on-screen keyboard active key overlay position + context->surfaces.overlays.osk_active_key.position.x = 0; + context->surfaces.overlays.osk_active_key.position.y = 0; + context->surfaces.overlays.osk_active_key.position.w = context->surfaces.overlays.osk_active_key.surface->w; + context->surfaces.overlays.osk_active_key.position.h = context->surfaces.overlays.osk_active_key.surface->h; + + // Calculate the on-screen keyboard buffer overlay position + context->surfaces.overlays.osk_buffer.position.x = 0; + context->surfaces.overlays.osk_buffer.position.y = context->surfaces.screen->h >> 2; + context->surfaces.overlays.osk_buffer.position.w = context->surfaces.screen->w; + context->surfaces.overlays.osk_buffer.position.h = context->surfaces.screen->h >> 1; + + return 0; +} + +/** + * @brief Configures the on-screen keyboard menu. + */ +static void osk_configure_menu(struct gui_context * context, xmlDocPtr document) +{ + context->colors.osk.background = 0x000000; + context->colors.osk.foreground = 0xffffff; + + if (document != NULL) + { + // Get the on-screen keyboard background color + context->colors.osk.background = io_get_skin_color(document, "Keyboard", "Background", 0x000000); + + // Get the on-screen keyboard foreground color + context->colors.osk.foreground = io_get_skin_color(document, "Keyboard", "Foreground", 0xffffff); + } +} + +/** + * @brief Activates the on-screen keyboard menu. + */ +static void osk_activate_menu(struct gui_node * this) +{ + // Get the on-screen keyboard menu node data + struct gui_osk_menu_node_data * osk_menu_node_data = (struct gui_osk_menu_node_data *)this->data; + + // Render the title text surface + gui_activate_menu(this, osk_menu_node_data->title); + + // Reset the on-screen keyboard grid index + this->context->osk.grid = OSK_GRID_COUNT - 1; + + // Lock the on-screen keyboard if buttons are pressed + this->context->osk.lock = this->context->inputs.internal.current.east || this->context->inputs.internal.current.south; + int lock = this->context->osk.lock; + + // Reset the on-screen keyboard sync flag + this->context->osk.sync = true; + + // Reset the on-screen keyboard apply flag + this->context->osk.apply = false; + + // Initialize the on-screen keyboard buffer + this->context->osk.buffer = malloc(osk_menu_node_data->buffer != NULL ? strlen(osk_menu_node_data->buffer) : sizeof(char)); + + // We have a buffer to copy + if (osk_menu_node_data->buffer != NULL) + { + // Copy the on-screen keyboard buffer + strcpy(this->context->osk.buffer, osk_menu_node_data->buffer); + } + + // Make this node take focus on screen + this->context->menu.active = this; +} + +/** + * @brief Deactivates the on-screen keyboard menu. + */ +static void osk_deactivate_menu(struct gui_node * this) +{ + // The menu has been activated (aka. its in focus, this check is necessary because deactivate gets called on parent node disposal as well) + if (this->context->menu.active == this) + { + // Get the on-screen keyboard menu node data + struct gui_osk_menu_node_data * osk_menu_node_data = (struct gui_osk_menu_node_data *)this->data; + + // We have an on-screen keyboard buffer to apply + if (this->context->osk.apply) + { + // Save the on-screen keyboard buffer + osk_menu_node_data->buffer = strdup(this->context->osk.buffer); + } + + // Free the on-screen keyboard buffer + free(this->context->osk.buffer); + + // NULL the on-screen keyboard buffer reference + this->context->osk.buffer = NULL; + + // We have a parent node to return to + if (this->parent != NULL) + { + // And return on screen focus to the parent node + this->context->menu.active = this->parent; + } + } +} + +/** + * @brief Updates the on-screen keyboard menu. + */ +static void osk_update_menu(struct gui_node * this) +{ + // The current state + static int state = 0; + + // The time of the last state change + static Uint32 last_state_change = 0; + + // The time of the last scroll event + static Uint32 last_scroll_event = 0; + + // The current on-screen keyboard index + int index = this->context->osk.index; + + // The current on-screen keyboard grid + int grid = this->context->osk.grid; + + // The current on-screen keyboard buffer + char * buffer = strdup(this->context->osk.buffer); + + // The east-facing face button is pressed + bool add = this->context->inputs.internal.current.east; + + // The south-facing face button is pressed + bool delete = this->context->inputs.internal.current.south; + + // The left bumper button is pressed + bool previous_grid = this->context->inputs.internal.current.left_bumper; + + // The right bumper button is pressed + bool next_grid = this->context->inputs.internal.current.right_bumper; + + // The select button was pressed + bool clear = this->context->inputs.internal.previous.select != this->context->inputs.internal.current.select && this->context->inputs.internal.current.select; + + // The start button was pressed + bool apply = this->context->inputs.internal.previous.start != this->context->inputs.internal.current.start && this->context->inputs.internal.current.start; + + // The on-screen keyboard is locked and buttons are released + if (this->context->osk.lock && this->context->inputs.internal.current.east == 0 && this->context->inputs.internal.current.south == 0) + { + // Unlock the on-screen keyboard + this->context->osk.lock = false; + } + + int new_state = 0; + + // The on-screen keyboard is unlocked + if (!this->context->osk.lock) + { + if (new_state == 0) + { + // Try using the horizontal direction pad axis + new_state = this->context->inputs.internal.current.dpad_x; + } + + // The horizontal direction pad axis is in its center-state + if (new_state == 0) + { + // Try using the vertical direction pad axis + new_state = this->context->inputs.internal.current.dpad_y; + } + + // The vertical direction pad axis is in its center-state + if (new_state == 0) + { + // Try using the east-facing face button + new_state = this->context->inputs.internal.current.east; + } + + // The east-facing face button is in its unpressed-state + if (new_state == 0) + { + // Try using the south-facing face button + new_state = this->context->inputs.internal.current.south; + } + + // The south-facing face button is in its unpressed-state + if (new_state == 0) + { + // Try using the left bumper button + new_state = this->context->inputs.internal.current.left_bumper; + } + + // The left bumper button is in its unpressed-state + if (new_state == 0) + { + // Try using the right bumper button + new_state = this->context->inputs.internal.current.right_bumper; + } + } + + // The state has changed + if (state != new_state) + { + // Override the current state + state = new_state; + + // Keep track of when the state changed + last_state_change = SDL_GetTicks(); + + // Reset the repeat event timer + last_scroll_event = 0; + + // Reset the scroll repeat interval + scroll_repeat_interval = SCROLL_EVENT_INITIAL_REPEAT_INTERVAL; + } + + // We're still scrolling in the same state and have room for acceleration + else if (state != 0 && scroll_repeat_interval > SCROLL_EVENT_MINIMUM_REPEAT_INTERVAL) + { + // Calculate the acceleration multiplier + Uint32 acceleration_multiplier = (SDL_GetTicks() - last_state_change) / SCROLL_EVENT_ACCELERATION_TIMESPAN; + + // Calculate the new scroll repeat interval + int new_scroll_repeat_interval = SCROLL_EVENT_INITIAL_REPEAT_INTERVAL - (SCROLL_EVENT_ACCELERATION * acceleration_multiplier); + + // Set the new scroll repeat interval + scroll_repeat_interval = new_scroll_repeat_interval > SCROLL_EVENT_MINIMUM_REPEAT_INTERVAL ? new_scroll_repeat_interval : SCROLL_EVENT_MINIMUM_REPEAT_INTERVAL; + } + + // We fulfill all scroll requirements + if (state != 0 && (last_scroll_event == 0 || (SDL_GetTicks() - last_scroll_event) > scroll_repeat_interval)) + { + // The direction pad axis is pressed + if (this->context->inputs.internal.current.dpad_x != 0 || this->context->inputs.internal.current.dpad_y != 0) + { + // Compute the new on-screen keyboard index + this->context->osk.index = osk_index(this->context->osk.index, this->context->inputs.internal.current.dpad_x, this->context->inputs.internal.current.dpad_y); + } + + if (add) + { + const char * key = osk_grids[this->context->osk.grid][this->context->osk.index]; + + if (strcmp(key, "⇦") == 0) + { + delete = true; + } + else if (strcmp(key, "⇧") == 0) + { + this->context->osk.grid = 1; + } + else if (strcmp(key, "⇩") == 0) + { + this->context->osk.grid = 2; + } + else if (strcmp(key, "⊕") == 0) + { + this->context->osk.grid = 0; + } + else if (strcmp(key, "⏎") == 0) + { + apply = true; + } + else + { + size_t size = strlen(this->context->osk.buffer); + char * resized_buffer = realloc(this->context->osk.buffer, sizeof(char) * (size + 1)); + resized_buffer[size] = *key; + resized_buffer[size + 1] = '\0'; + this->context->osk.buffer = resized_buffer; + } + } + + if (delete) + { + size_t size = strlen(this->context->osk.buffer); + if (size > 0) + { + char * resized_buffer = realloc(this->context->osk.buffer, sizeof(char) * (size - 1)); + resized_buffer[size - 1] = '\0'; + this->context->osk.buffer = resized_buffer; + } + } + + if (previous_grid) + { + this->context->osk.grid = osk_mod(this->context->osk.grid - 1, OSK_GRID_COUNT); + } + + if (next_grid) + { + this->context->osk.grid = osk_mod(this->context->osk.grid + 1, OSK_GRID_COUNT); + } + + // Set the scroll event timestamp + last_scroll_event = SDL_GetTicks(); + } + + if (clear) + { + // Free the on-screen keyboard buffer + free(this->context->osk.buffer); + + // Get the on-screen keyboard menu node data + struct gui_osk_menu_node_data * osk_menu_node_data = (struct gui_osk_menu_node_data *)this->data; + + // Initialize the on-screen keyboard buffer + this->context->osk.buffer = malloc(osk_menu_node_data->buffer != NULL ? strlen(osk_menu_node_data->buffer) : sizeof(char)); + } + + if (apply) + { + // Apply the on-screen keyboard buffer + this->context->osk.apply = true; + } + + // The current on-screen keyboard index has changed or the grid has changed + if (index != this->context->osk.index || grid != this->context->osk.grid) + { + // We are holding onto a no longer valid on-screen keyboard overlay + if (this->context->surfaces.overlays.osk.surface != NULL) + { + // Free the on-screen keyboard overlay surface + SDL_FreeSurface(this->context->surfaces.overlays.osk.surface); + + // And NULL its reference so it can be re-rendered + this->context->surfaces.overlays.osk.surface = NULL; + } + } + + // The current on-screen keyboard index has changed + if (index != this->context->osk.index) + { + // We are holding onto a no longer valid on-screen keyboard active key overlay + if (this->context->surfaces.overlays.osk_active_key.surface != NULL) + { + // Free the on-screen keyboard active key overlay surface + SDL_FreeSurface(this->context->surfaces.overlays.osk_active_key.surface); + + // And NULL its reference so it can be re-rendered + this->context->surfaces.overlays.osk_active_key.surface = NULL; + } + } + + // The current on-screen keyboard buffer has changed or we need to sync the on-screen keyboard + if (strcmp(buffer, this->context->osk.buffer) != 0 || this->context->osk.sync) + { + // We are holding onto a no longer valid on-screen keyboard buffer overlay + if (this->context->surfaces.overlays.osk_buffer.surface != NULL) + { + // Free the on-screen keyboard buffer overlay surface + SDL_FreeSurface(this->context->surfaces.overlays.osk_buffer.surface); + + // And NULL its reference so it can be re-rendered + this->context->surfaces.overlays.osk_buffer.surface = NULL; + } + } + + // Sync the on-screen keyboard once + if (this->context->osk.sync) + { + // Reset the on-screen keyboard sync flag + this->context->osk.sync = false; + } + + // Verify if we should apply changes + if (this->context->osk.apply) + { + // Deactivate the on-screen keyboard menu + osk_deactivate_menu(this); + } +} + +/** + * @brief Renders the on-screen keyboard menu. + */ +static void osk_render_menu(struct gui_node * this) +{ + // Cast the type specific node data + struct gui_menu_node_data * data = (struct gui_menu_node_data *)this->data; + + // We have a valid menu node data structure + if (data != NULL) + { + // Blit the menu title surface + SDL_BlitSurface(data->title.surface, NULL, this->context->surfaces.screen, &data->title.position); + } + + // We need to re-render the on-screen keyboard + if (this->context->surfaces.overlays.osk.surface == NULL) + { + // Render the on-screen keyboard + this->context->surfaces.overlays.osk.surface = icon_rounded_rectangle(this->context->surfaces.screen->w, this->context->surfaces.screen->h >> 1, 1, this->context->colors.osk.background); + + // We managed to render the on-screen keyboard + if (this->context->surfaces.overlays.osk.surface != NULL) + { + // Render the on-screen keyboard grids + osk_render_grid(this->context); + } + } + + // We need to re-render the on-screen keyboard active key + if (this->context->surfaces.overlays.osk_active_key.surface == NULL) + { + // Render the on-screen keyboard active key + this->context->surfaces.overlays.osk_active_key.surface = icon_load("rect.svg", this->context->surfaces.screen->w / OSK_COLS_COUNT, (this->context->surfaces.screen->h >> 1) / OSK_ROWS_COUNT, &this->context->colors.osk.foreground); + + // We managed to render the on-screen keyboard active key + if (this->context->surfaces.overlays.osk_active_key.surface != NULL) + { + this->context->surfaces.overlays.osk_active_key.position.x = (this->context->osk.index % OSK_COLS_COUNT) * this->context->surfaces.overlays.osk_active_key.surface->w; + this->context->surfaces.overlays.osk_active_key.position.y = (this->context->osk.index / OSK_COLS_COUNT | 0) * this->context->surfaces.overlays.osk_active_key.surface->h; + this->context->surfaces.overlays.osk_active_key.position.w = this->context->surfaces.overlays.osk_active_key.surface->w; + this->context->surfaces.overlays.osk_active_key.position.h = this->context->surfaces.overlays.osk_active_key.surface->h; + } + } + + // We need to re-render the on-screen keyboard buffer + if (this->context->surfaces.overlays.osk_buffer.surface == NULL) + { + // Render the on-screen keyboard buffer + this->context->surfaces.overlays.osk_buffer.surface = TTF_RenderUTF8_Blended(this->context->fonts.small, this->context->osk.buffer, SDL_ToSDLColor(this->context->colors.osk.foreground)); + + // We managed to render the on-screen keyboard buffer + if (this->context->surfaces.overlays.osk_buffer.surface != NULL) + { + this->context->surfaces.overlays.osk_buffer.position.x = (this->context->surfaces.screen->w >> 1) - (this->context->surfaces.overlays.osk_buffer.surface->w >> 1); + } + } + + // We need to render the on-screen keyboard + if (this->context->surfaces.overlays.osk.surface != NULL) + { + // Render the on-screen keyboard + SDL_BlitSurface(this->context->surfaces.overlays.osk.surface, NULL, this->context->surfaces.screen, &this->context->surfaces.overlays.osk.position); + } + + // We need to render the on-screen keyboard active key + if (this->context->surfaces.overlays.osk_active_key.surface != NULL) + { + // Render the on-screen keyboard active key + SDL_BlitSurfaceAlpha(this->context->surfaces.overlays.osk_active_key.surface, NULL, this->context->surfaces.overlays.osk.surface, &this->context->surfaces.overlays.osk_active_key.position); + } + + // We need to render the on-screen keyboard buffer + if (this->context->surfaces.overlays.osk_buffer.surface != NULL) + { + // Render the on-screen keyboard buffer + SDL_BlitSurface(this->context->surfaces.overlays.osk_buffer.surface, NULL, this->context->surfaces.screen, &this->context->surfaces.overlays.osk_buffer.position); + } +} + +/** + * @brief Creates a on-screen keyboard context menu. + */ +static struct gui_node * osk_create_menu(struct gui_node * this, struct gui_node ** first_node, struct gui_node ** previous_node, const char * title, const char * buffer) +{ + // The created node + struct gui_node * osk_menu_node = NULL; + + // Allocate the additional on-screen keyboard menu data + struct gui_osk_menu_node_data * osk_menu_node_data = calloc(1, sizeof(struct gui_osk_menu_node_data)); + + // We managed to allocate the additional on-screen keyboard menu data + if (osk_menu_node_data != NULL) + { + // Assign the title + osk_menu_node_data->title = title; + + // Assign the buffer + osk_menu_node_data->buffer = buffer; + + // Create a node for the on-screen keyboard menu + osk_menu_node = gui_create_context_menu_item(this, first_node, previous_node, title, osk_activate_menu, osk_deactivate_menu, osk_update_menu, osk_render_menu, NULL, NODE_TYPE_CONTEXT_MENU_OSK_MENU, osk_menu_node_data); + + // We failed to create a node for the on-screen keyboard menu + if (osk_menu_node == NULL) + { + // Free the additional menu data + free(osk_menu_node_data); + } + } + + // Return the created node + return osk_menu_node; +} + +#endif