diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-09-27 17:03:06 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-11-01 16:26:16 +0100 |
| commit | 980858186149651df5543b6fc99a4f7db0cdd089 (patch) | |
| tree | d347200b0a562d84df505097651ad0642f207fdd /src/menu | |
| parent | 39f50e601d395bbd2d78d0147ac530b756da2fff (diff) | |
| download | jancity-980858186149651df5543b6fc99a4f7db0cdd089.tar.gz | |
WIP
Diffstat (limited to 'src/menu')
| -rw-r--r-- | src/menu/CMakeLists.txt | 16 | ||||
| -rw-r--r-- | src/menu/privinc/menu_net.h | 62 | ||||
| -rw-r--r-- | src/menu/privinc/menu_private.h | 11 | ||||
| -rw-r--r-- | src/menu/src/common.c | 11 | ||||
| -rw-r--r-- | src/menu/src/gamecfg_menu.c | 735 | ||||
| -rw-r--r-- | src/menu/src/host_menu.c | 159 | ||||
| -rw-r--r-- | src/menu/src/hostjoin_menu.c | 129 | ||||
| -rw-r--r-- | src/menu/src/join_menu.c | 334 | ||||
| -rw-r--r-- | src/menu/src/main_menu.c | 24 | ||||
| -rw-r--r-- | src/menu/src/menu.c | 10 | ||||
| -rw-r--r-- | src/menu/src/menu_net.c | 447 | ||||
| -rw-r--r-- | src/menu/src/settings_menu.c | 396 |
12 files changed, 1977 insertions, 357 deletions
diff --git a/src/menu/CMakeLists.txt b/src/menu/CMakeLists.txt index 465250f..9780148 100644 --- a/src/menu/CMakeLists.txt +++ b/src/menu/CMakeLists.txt @@ -1,9 +1,23 @@ add_library(menu "src/gamecfg_menu.c" "src/hostjoin_menu.c" + "src/host_menu.c" "src/join_menu.c" "src/menu.c" + "src/menu_net.c" "src/main_menu.c" + "src/settings_menu.c" ) target_include_directories(menu PUBLIC "inc" PRIVATE "privinc") -target_link_libraries(menu PRIVATE camera game gfx gui input system) +target_link_libraries(menu PRIVATE + camera + game + gfx + gui + input + net + packet + settings + system + util +) diff --git a/src/menu/privinc/menu_net.h b/src/menu/privinc/menu_net.h new file mode 100644 index 0000000..1980917 --- /dev/null +++ b/src/menu/privinc/menu_net.h @@ -0,0 +1,62 @@ +#ifndef MENU_NETCFG_H +#define MENU_NETCFG_H + +#include <gui.h> +#include <gui/button.h> +#include <gui/checkbox.h> +#include <gui/container.h> +#include <gui/label.h> +#include <gui/line_edit.h> +#include <net.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct menu_net_gui +{ + char device[32], address[32], port[sizeof "65535"]; + bool update_domain; + struct gui_container cnt; + struct gui_label type; + struct gui_button type_btn; + enum net_domain domain; + + union + { + struct gui_container cnt; + + struct menu_ipv4_gui + { + struct gui_container cnt, relay_cnt, announce_cnt; + struct gui_button next_page_btn; + struct gui_label address, port, relay, announce; + struct gui_line_edit address_le, port_le, announce_le; + struct gui_checkbox relay_ch, announce_ch; + } ipv4; + + struct menu_serial_gui + { + struct gui_container cnt, hw_ctrl_cnt; + struct gui_label device, speed, hw_ctrl; + struct gui_button speed_btn; + struct gui_line_edit device_le; + struct gui_checkbox hw_ctrl_ch; + size_t speed_i; + } serial; + } gui; +}; + +int menu_net_gui_init(struct menu_net_gui *m); +void menu_net_gui_update(struct menu_net_gui *m); +int menu_net_server(const struct menu_net_gui *m, union net_server *s); +int menu_net_connect(const struct menu_net_gui *m, union net_connect *c); + +#ifdef __cplusplus +} +#endif + +#endif /* MENU_NETCFG_H */ diff --git a/src/menu/privinc/menu_private.h b/src/menu/privinc/menu_private.h index 181825c..d8aa089 100644 --- a/src/menu/privinc/menu_private.h +++ b/src/menu/privinc/menu_private.h @@ -3,8 +3,10 @@ #include <camera.h> #include <input.h> +#include <net.h> #include <peripheral.h> -#include <stdbool.h> +#include <settings.h> +#include <stddef.h> #ifdef __cplusplus extern "C" @@ -16,6 +18,7 @@ struct menu_common struct camera cam; union peripheral p; struct input in; + struct settings s; }; int menu_update(struct menu_common *c, @@ -23,9 +26,11 @@ int menu_update(struct menu_common *c, int (*render)(const struct menu_common *, void *), void *arg); int menu_main(struct menu_common *c); -int menu_hostjoin(struct menu_common *c, bool *back); +int menu_hostjoin(struct menu_common *c); +int menu_settings(struct menu_common *c); int menu_join(struct menu_common *c); -int menu_gamecfg(struct menu_common *c); +int menu_host(struct menu_common *c); +int menu_gamecfg(struct menu_common *c, struct net_host *h, net_peer p); void menu_on_pressed(void *arg); #ifdef __cplusplus diff --git a/src/menu/src/common.c b/src/menu/src/common.c new file mode 100644 index 0000000..494c047 --- /dev/null +++ b/src/menu/src/common.c @@ -0,0 +1,11 @@ +#include <menu_private.h> +#include <net.h> +#include <stddef.h> + +const enum net_domain menu_domains[] = +{ + NET_DOMAIN_IPV4, + NET_DOMAIN_SERIAL +}; + +const size_t menu_domains_len = sizeof menu_domains / sizeof *menu_domains; diff --git a/src/menu/src/gamecfg_menu.c b/src/menu/src/gamecfg_menu.c index 12184f5..ac01718 100644 --- a/src/menu/src/gamecfg_menu.c +++ b/src/menu/src/gamecfg_menu.c @@ -3,27 +3,476 @@ #include <game.h> #include <gui.h> #include <gui/button.h> +#include <gui/checkbox.h> #include <gui/container.h> +#include <gui/label.h> +#include <gui/line_edit.h> #include <gui/rounded_rect.h> +#include <net.h> +#include <packet.h> +#include <util.h> #include <stdbool.h> #include <stddef.h> +#include <string.h> + +enum +{ + CHAT_ENTRIES = 6, + TOTAL_MESSAGE_LEN = GAME_PLAYER_NAME_LEN + + sizeof ": " - 1 + + PACKET_MAX_MESSAGE_LEN +}; struct gamecfg_menu { - struct gui_container cnt, bcnt; - struct gui_rounded_rect r; + char chat_in[PACKET_MAX_MESSAGE_LEN], + chat[CHAT_ENTRIES][TOTAL_MESSAGE_LEN]; + char chat_label[sizeof "Chat (6)"]; + size_t chat_i, unread_messages; + struct gui_container bcnt; struct gui_button start, back; + struct net_host *host; + struct packet_ctx ctx; + bool update_page; + + enum + { + PAGE_1, + PAGE_2, + + N_PAGES + } page_i; + + struct player_info + { + struct player_info_net + { + net_peer peer; + struct packet_input in; + } *net; + + char name[GAME_PLAYER_NAME_LEN]; + + enum + { + OPEN, + CLOSED, + CONNECTING, + NOT_READY, + READY + } state; + } pl[GAME_MAX_PLAYERS]; + + union + { + struct gui_container root; + + struct gamecfg_page1 + { + struct gui_container root, cnt, ready_cnt; + struct gui_rounded_rect r; + struct gui_label ready; + struct gui_checkbox ready_ch; + struct gui_button chat_page; + + struct player_info_gui + { + struct gui_container cnt; + struct gui_label name, state_label; + } pl[GAME_MAX_PLAYERS]; + } page1; + + struct gamecfg_page2 + { + struct gui_container root, chat_cnt, bcnt; + struct gui_rounded_rect r; + struct gui_line_edit chat_le; + struct gui_button send, player_page; + struct gui_label entries[CHAT_ENTRIES]; + } page2; + } u; }; +static void update_players(struct gamecfg_menu *const m) +{ + if (m->page_i != PAGE_1) + return; + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + struct player_info_gui *const g = &m->u.page1.pl[i]; + + static const char *const states[] = + { + [OPEN] = "Open", + [CLOSED] = "Closed", + [NOT_READY] = "Not ready", + [CONNECTING] = "Connecting", + [READY] = "Ready" + }; + + g->state_label.text = states[pi->state]; + } +} + +static void on_next_page_pressed(void *const arg) +{ + struct gamecfg_menu *const m = arg; + + m->update_page = true; +} + +static int send_ready(const net_peer p, const struct packet_ctx *const c, + const struct player_info *const pi) +{ + const struct packet_ready r = + { + .common = + { + .type = PACKET_TYPE_READY + }, + + .ready = pi->state == READY + }; + + return packet_send(c, p, (const union packet *)&r); +} + +static void on_ready_pressed(const bool active, void *const arg) +{ + struct gamecfg_menu *const m = arg; + struct player_info *const self = m->pl; + + self->state = active ? READY : NOT_READY; + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info_net *const net = m->pl[i].net; + + if (net && net->peer) + send_ready(net->peer, &m->ctx, self); + } +} + +static void init_page1(struct gamecfg_menu *const m) +{ + struct gamecfg_page1 *const p = &m->u.page1; + + { + struct gui_rounded_rect *const r = &p->r; + + gui_rounded_rect_init(r); + r->adjust = true; + r->common.hcentered = true; + gui_add_child(&p->root.common, &r->common); + } + + { + struct gui_container *const c = &p->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 4; + gui_add_child(&p->r.common, &c->common); + } + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + struct player_info_gui *const g = &p->pl[i]; + + { + struct gui_container *const c = &g->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 8; + gui_add_child(&p->cnt.common, &c->common); + } + + { + struct gui_label *const l = &g->name; + + gui_label_init(l); + l->text = pi->name; + l->common.vcentered = true; + gui_add_child(&g->cnt.common, &l->common); + } + + { + struct gui_label *const l = &g->state_label; + + gui_label_init(l); + l->common.vcentered = true; + gui_add_child(&g->cnt.common, &l->common); + } + } + + { + struct gui_container *const c = &p->ready_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 4; + c->common.hcentered = true; + gui_add_child(&p->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &p->ready_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->on_pressed = on_ready_pressed; + c->arg = m; + gui_add_child(&p->ready_cnt.common, &c->common); + } + + { + struct gui_label *const l = &p->ready; + + gui_label_init(l); + l->font = FONT; + l->text = "Ready"; + l->common.vcentered = true; + gui_add_child(&p->ready_cnt.common, &l->common); + } + + { + struct gui_button *const b = &p->chat_page; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.w = 140; + b->common.hcentered = true; + b->on_pressed = on_next_page_pressed; + b->arg = m; + b->u.type1.label.text = "Chat"; + gui_add_child(&m->u.root.common, &b->common); + } +} + +static int broadcast(struct gamecfg_menu *const m, const union packet *const p) +{ + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info_net *const net = m->pl[i].net; + + if (net) + { + const net_peer peer = net->peer; + + if (peer && packet_send(&m->ctx, peer, p)) + { + fprintf(stderr, "%s: net_write failed\n", __func__); + return -1; + } + } + } + + return 0; +} + +static void append_msg(struct gamecfg_menu *const m, + const char *const player, const char *const msg) +{ + static const size_t sz = sizeof m->chat / sizeof *m->chat; + char *dst; + + if (m->chat_i < sz) + dst = m->chat[m->chat_i++]; + else + { + for (size_t i = 0; i < sz - 1; i++) + strcpy(m->chat[i], m->chat[i + 1]); + + dst = m->chat[sz - 1]; + *dst = '\0'; + } + + strcat(dst, player); + strcat(dst, ": "); + strcat(dst, msg); +} + +static void on_send_pressed(void *const arg) +{ + struct gamecfg_menu *const m = arg; + struct gui_line_edit *const le = &m->u.page2.chat_le; + char *const src = le->text; + + if (!*src) + return; + + const struct packet_chat c = + { + .common = + { + .type = PACKET_TYPE_CHAT + }, + + .msg = src + }; + + if (broadcast(m, (const union packet *)&c)) + return; + + append_msg(m, m->pl->name, src); + *src = '\0'; + le->i = 0; +} + +static void init_page2(struct gamecfg_menu *const m) +{ + struct gamecfg_page2 *const p = &m->u.page2; + + { + struct gui_rounded_rect *const r = &p->r; + + gui_rounded_rect_init(r); + r->adjust = true; + r->common.hcentered = true; + gui_add_child(&m->u.root.common, &r->common); + } + + { + struct gui_container *const c = &p->chat_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 2; + gui_add_child(&p->r.common, &c->common); + } + + for (size_t i = 0; i < sizeof p->entries / sizeof *p->entries; i++) + { + struct gui_label *const l = &p->entries[i]; + + gui_label_init(l); + l->text = m->chat[i]; + l->font = FONT; + gui_add_child(&p->chat_cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &p->chat_le; + + gui_line_edit_init(le, m->chat_in, sizeof m->chat_in); + le->common.hcentered = true; + le->w = 240; + gui_add_child(&m->u.root.common, &le->common); + } + + { + struct gui_container *const c = &p->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 2; + gui_add_child(&m->u.root.common, &c->common); + } + + { + struct gui_button *const b = &p->send; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.vcentered = true; + b->u.type1.w = 100; + b->u.type1.label.text = "Send"; + b->on_pressed = on_send_pressed; + b->arg = m; + gui_add_child(&p->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &p->player_page; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.vcentered = true; + b->u.type1.w = 150; + b->u.type1.label.text = "Player info"; + b->on_pressed = on_next_page_pressed; + b->arg = m; + gui_add_child(&p->bcnt.common, &b->common); + } + + m->unread_messages = 0; +} + +static void init_page_gui(struct gamecfg_menu *const m) +{ + { + struct gui_container *const c = &m->u.root; + + gui_container_init(c); + c->common.hcentered = true; + c->common.vcentered = true; + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 4; + } + + static void (*const init[])(struct gamecfg_menu *) = + { + [PAGE_1] = init_page1, + [PAGE_2] = init_page2 + }; + + init[m->page_i](m); +} + +static void update_send(struct menu_common *const c, + struct gamecfg_menu *const m) +{ + if (m->page_i != PAGE_2) + return; + + switch (c->p.common.type) + { + case PERIPHERAL_TYPE_KEYBOARD_MOUSE: + /* Fall through. */ + case PERIPHERAL_TYPE_TOUCH: + { + struct keyboard *const k = &c->p.kbm.keyboard; + + if (m->u.page2.chat_le.focus + && keyboard_justreleased(k, + &KEYBOARD_COMBO(KEYBOARD_KEY_RETURN))) + on_send_pressed(m); + } + break; + + case PERIPHERAL_TYPE_PAD: + break; + } +} + +static void update_page(struct gamecfg_menu *const m) +{ + if (m->update_page) + { + if (++m->page_i >= N_PAGES) + m->page_i = 0; + + init_page_gui(m); + m->update_page = false; + } +} + static int update(struct menu_common *const c, void *const arg) { struct gamecfg_menu *const m = arg; m->bcnt.common.y = screen_h - 40; - m->r.w = screen_w / 2; - m->r.h = screen_h / 2; - if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) + if (m->host) + net_update(m->host); + + update_players(m); + update_send(c, m); + update_page(m); + + if (gui_update(&m->u.root.common, &c->p, &c->cam, &c->in) || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) return -1; @@ -34,54 +483,264 @@ static int render(const struct menu_common *const c, void *const arg) { const struct gamecfg_menu *const m = arg; - if (gui_render(&m->cnt.common) + if (gui_render(&m->u.root.common) || gui_render(&m->bcnt.common)) return -1; return 0; } -int menu_gamecfg(struct menu_common *const c) +static int add_peer(struct gamecfg_menu *const m, const net_peer p) +{ + bool found = false; + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + struct player_info_net *const net = pi->net; + + if (net && !net->peer) + { + net->peer = p; + pi->state = CONNECTING; + strcpy(pi->name, "<new player>"); + found = true; + break; + } + } + + return !found; +} + +static int send_name(const net_peer p, const struct packet_ctx *const c, + const struct player_info *const pi) +{ + const struct packet_name n = + { + .common = + { + .type = PACKET_TYPE_NAME + }, + + .name = pi->name + }; + + return packet_send(c, p, (const union packet *)&n); +} + +static int broadcast_player_info(const net_peer p, + const struct gamecfg_menu *const m) +{ + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + const struct player_info *const pi = &m->pl[i]; + + if (*pi->name) + { + const struct packet_ctx *const c = &m->ctx; + + if (send_name(p, c, pi) || send_ready(p, c, pi)) + return -1; + } + } + + return 0; +} + +static void on_connected(const net_peer p, void *const arg) +{ + struct gamecfg_menu *const m = arg; + + if (net_role(m->host) == NET_ROLE_SERVER) + broadcast_player_info(p, m); + + add_peer(m, p); +} + +static void on_disconnected(const net_peer p, void *const arg) { - struct gamecfg_menu m; +} + +static struct player_info *get_pl_from_peer(struct gamecfg_menu *const m, + const net_peer peer) +{ + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + + if (pi->net && pi->net->peer == peer) + return pi; + } + + return NULL; +} + +static void on_received(const net_peer p, const void *const buf, + const size_t n, void *const arg) +{ + struct gamecfg_menu *const m = arg; + struct player_info *const pi = get_pl_from_peer(m, p); + + if (pi && pi->net && packet_feed(&m->ctx, p, &pi->net->in, buf, n)) + fprintf(stderr, "%s: packet_feed failed\n", __func__); +} + +static void init_footer_gui(struct gamecfg_menu *const m, + bool *const start, bool *const back) +{ + { + struct gui_container *const c = &m->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 8; + } + + if (net_role(m->host) == NET_ROLE_SERVER) + { + struct gui_button *const b = &m->start; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Start"; + b->u.type1.w = 140; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = start; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->u.type1.w = 100; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = back; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } +} + +static void init_own(const struct menu_common *const c, + struct player_info *const pi) +{ + strcpy(pi->name, c->s.name); + pi->state = NOT_READY; +} + +static void on_packet_received(const net_peer peer, + const union packet *const pkt, void *const arg) +{ + struct gamecfg_menu *const m = arg; + + switch (pkt->common.type) + { + case PACKET_TYPE_CHAT: + { + struct player_info *const pi = get_pl_from_peer(m, peer); + + if (pi) + { + append_msg(m, pi->name, pkt->chat.msg); + + if (m->page_i == PAGE_1) + { + if (m->unread_messages < CHAT_ENTRIES) + m->unread_messages++; + + snprintf(m->chat_label, sizeof m->chat_label, + "Chat (%zu)", m->unread_messages); + m->u.page1.chat_page.u.type1.label.text = m->chat_label; + } + } + } + break; + + case PACKET_TYPE_NAME: + { + struct player_info *const pi = get_pl_from_peer(m, peer); + + if (pi) + strcpy(pi->name, pkt->name.name); + } + break; + + case PACKET_TYPE_READY: + { + struct player_info *const pi = get_pl_from_peer(m, peer); + + if (pi) + pi->state = pkt->ready.ready ? READY : NOT_READY; + } + break; + + default: + break; + } +} + +int menu_gamecfg(struct menu_common *const c, struct net_host *const h, + const net_peer p) +{ + struct gamecfg_menu m = + { + .host = h, + .ctx = + { + .arg = &m, + .cb = on_packet_received, + .host = h + } + }; + + const struct net_event ev = + { + .connected = on_connected, + .disconnected = on_disconnected, + .received = on_received, + .arg = &m + }; + + struct player_info_net n[sizeof m.pl / sizeof *m.pl - 1] = {0}; bool start = false, back = false; - gui_container_init(&m.cnt); - m.cnt.common.hcentered = true; - m.cnt.common.vcentered = true; - - gui_rounded_rect_init(&m.r); - gui_add_child(&m.cnt.common, &m.r.common); - - gui_container_init(&m.bcnt); - m.bcnt.common.hcentered = true; - m.bcnt.mode = GUI_CONTAINER_MODE_H; - m.bcnt.spacing = 8; - - gui_button_init(&m.start, GUI_BUTTON_TYPE_1); - m.start.u.type1.label.text = "Start"; - m.start.u.type1.w = 100; - m.start.common.vcentered = true; - m.start.on_pressed = menu_on_pressed; - m.start.arg = &start; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.start.common); - - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.u.type1.w = 100; - m.back.common.vcentered = true; - m.back.on_pressed = menu_on_pressed; - m.back.arg = &back; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.back.common); + for (size_t i = 0; i < sizeof n / sizeof *n; i++) + m.pl[i + 1].net = &n[i]; + + if (net_set_event(m.host, &ev)) + return -1; + + init_own(c, m.pl); + init_page_gui(&m); + init_footer_gui(&m, &start, &back); + + if (p) + { + const struct packet_ctx *const c = &m.ctx; + + if (add_peer(&m, p) + || send_name(p, c, m.pl) + || send_ready(p, c, m.pl)) + return -1; + } while (!back && !c->p.common.exit && !start) if (menu_update(c, update, render, &m)) return -1; if (start) - return game(NULL); + { + struct game_cfg cfg = + { + .p = &c->p + }; + + return game(&cfg); + } return 0; } diff --git a/src/menu/src/host_menu.c b/src/menu/src/host_menu.c new file mode 100644 index 0000000..102275e --- /dev/null +++ b/src/menu/src/host_menu.c @@ -0,0 +1,159 @@ +#include <menu.h> +#include <menu_net.h> +#include <menu_private.h> +#include <game.h> +#include <gui.h> +#include <gui/button.h> +#include <gui/checkbox.h> +#include <gui/container.h> +#include <gui/label.h> +#include <gui/line_edit.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +struct host_menu +{ + struct gui_container cnt, bcnt; + struct gui_label error; + struct menu_net_gui net; + struct gui_button host, back; +}; + +static void hide_elements(struct menu_net_gui *const m) +{ + if (m->domain == NET_DOMAIN_IPV4) + { + struct menu_ipv4_gui *const g = &m->gui.ipv4; + + g->address.common.hidden = true; + g->address_le.common.hidden = true; + } +} + +static int update(struct menu_common *const c, void *const arg) +{ + struct host_menu *const m = arg; + + m->bcnt.common.y = screen_h - 40; + menu_net_gui_update(&m->net); + m->net.cnt.common.parent = &m->cnt.common; + hide_elements(&m->net); + + if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) + || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) + return -1; + + return 0; +} + +static int render(const struct menu_common *const c, void *const arg) +{ + const struct host_menu *const m = arg; + + if (gui_render(&m->cnt.common) + || gui_render(&m->bcnt.common)) + return -1; + + return 0; +} + +static void init_footer_gui(struct host_menu *const m, + bool *const back, bool *const host) +{ + { + struct gui_label *const l = &m->error; + + gui_label_init(l); + l->font = FONT; + l->common.hcentered = true; + l->common.hidden = true; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_container *const c = &m->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + } + + { + struct gui_button *const b = &m->host; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Host"; + b->u.type1.w = 140; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = host; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->u.type1.w = 100; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = back; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } +} + +static struct net_host *init_server(const struct host_menu *const m) +{ + union net_server s; + + if (menu_net_server(&m->net, &s)) + return NULL; + + return net_server(&s); +} + +int menu_host(struct menu_common *const c) +{ + int ret = -1; + bool back = false; + + do + { + struct host_menu m = {0}; + struct net_host *h = NULL; + bool host = false; + + gui_container_init(&m.cnt); + m.cnt.common.hcentered = true; + m.cnt.common.vcentered = true; + m.cnt.mode = GUI_CONTAINER_MODE_V; + m.cnt.spacing = 2; + + menu_net_gui_init(&m.net); + m.net.cnt.common.hcentered = true; + gui_add_child(&m.cnt.common, &m.net.cnt.common); + + init_footer_gui(&m, &back, &host); + + while (!back && !c->p.common.exit && !host) + if (menu_update(c, update, render, &m)) + goto end; + + if (host && (h = init_server(&m)) && menu_gamecfg(c, h, NULL)) + goto end; + + net_close(h); + gui_deinit(&m.cnt.common, &c->in); + } while (!back && !c->p.common.exit); + + ret = 0; + +end: + return ret; +} diff --git a/src/menu/src/hostjoin_menu.c b/src/menu/src/hostjoin_menu.c index 9ce93b1..121efd6 100644 --- a/src/menu/src/hostjoin_menu.c +++ b/src/menu/src/hostjoin_menu.c @@ -1,15 +1,16 @@ #include <menu.h> #include <menu_private.h> -#include <game.h> #include <gui.h> #include <gui/button.h> #include <gui/container.h> +#include <gui/checkbox.h> +#include <gui/label.h> #include <stdbool.h> struct menu_hostjoin { struct gui_container cnt; - struct gui_button host, join, back; + struct gui_button local, host, join, back; }; static int update(struct menu_common *const c, void *const arg) @@ -32,61 +33,91 @@ static int render(const struct menu_common *const c, void *const arg) return 0; } -int menu_hostjoin(struct menu_common *const c, bool *const back) +static void init_header_gui(struct menu_hostjoin *const m, bool *const local, + bool *const host, bool *const join) { + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->common.hcentered = true; + c->common.vcentered = true; + c->spacing = 4; + c->mode = GUI_CONTAINER_MODE_V; + } + + { + struct gui_button *const b = &m->local; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Local game"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = local; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_button *const b = &m->host; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Host game"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = host; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_button *const b = &m->join; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Join game"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = join; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); + } +} + +static void init_footer_gui(struct menu_hostjoin *const m, bool *const back) +{ + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = back; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); +} + +int menu_hostjoin(struct menu_common *const c) +{ + bool back = false; + do { struct menu_hostjoin m; - bool host = false, join = false; - - gui_container_init(&m.cnt); - m.cnt.common.hcentered = true; - m.cnt.common.vcentered = true; - m.cnt.spacing = 4; - m.cnt.mode = GUI_CONTAINER_MODE_V; - - gui_button_init(&m.host, GUI_BUTTON_TYPE_1); - m.host.u.type1.label.text = "Host game"; - m.host.common.hcentered = true; - m.host.u.type1.w = 140; - m.host.arg = &host; - m.host.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.host.common); - - gui_button_init(&m.join, GUI_BUTTON_TYPE_1); - m.join.u.type1.label.text = "Join game"; - m.join.common.hcentered = true; - m.join.u.type1.w = 140; - m.join.arg = &join; - m.join.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.join.common); - - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.common.hcentered = true; - m.back.u.type1.w = 140; - m.back.arg = back; - m.back.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.back.common); - - while (!*back && !c->p.common.exit && !host && !join) - { + bool local = false, host = false, join = false; + + init_header_gui(&m, &local, &host, &join); + init_footer_gui(&m, &back); + + while (!back && !c->p.common.exit && !local && !host && !join) if (menu_update(c, update, render, &m)) return -1; - } - if (host) - { - if (menu_gamecfg(c)) - return -1; - } - else if (join) - { - if (menu_join(c)) - return -1; - } + if (host && menu_host(c)) + return -1; + else if (join && menu_join(c)) + return -1; - } while (!*back && !c->p.common.exit); + } while (!back && !c->p.common.exit); return 0; } diff --git a/src/menu/src/join_menu.c b/src/menu/src/join_menu.c index 4755b5c..66a3fdc 100644 --- a/src/menu/src/join_menu.c +++ b/src/menu/src/join_menu.c @@ -1,5 +1,6 @@ #include <menu.h> #include <menu_private.h> +#include <menu_net.h> #include <gui.h> #include <gui/button.h> #include <gui/container.h> @@ -12,14 +13,13 @@ #include <errno.h> #include <stdbool.h> #include <stddef.h> -#include <stdlib.h> struct join_menu { struct gui_container cnt, bcnt; - struct gui_label type_label, address_device, port, connecting, speed; - struct gui_button type_btn, back, connect, device_btn, speed_btn; - struct gui_line_edit address_le, port_le; + struct menu_net_gui net; + struct gui_label connecting; + struct gui_button back, connect; enum { @@ -30,120 +30,77 @@ struct join_menu CONNECT_FAILED } state; - struct net_socket *socket; - size_t domain_i, speed_i, device_i, n_devices; - const char *const *devices; + struct net_host *host; + net_peer peer; }; -static const enum net_domain domains[] = -{ - NET_DOMAIN_IPV4, - NET_DOMAIN_SERIAL -}; - -static const char *const speeds[] = -{ - "19200", "38400", "57600", "115200" -}; - -static void on_connected(void *const arg) +static void on_connected(const net_peer p, void *const arg) { struct join_menu *const m = arg; m->state = CONNECTED; + m->peer = p; } -static void on_disconnected(void *const arg) +static void on_disconnected(const net_peer p, void *const arg) { struct join_menu *const m = arg; m->state = CONNECT_FAILED; - m->socket = NULL; + m->host = NULL; } static int on_connect(struct join_menu *const m) { - const enum net_domain d = domains[m->domain_i]; + union net_connect c; - union net_connect c = + if (menu_net_connect(&m->net, &c)) { - .common = - { - .domain = d, - .ev = - { - .connected = on_connected, - .disconnected = on_disconnected, - .arg = m - } - } - }; + fprintf(stderr, "%s: menu_net_connect failed\n", __func__); + return -1; + } - switch (d) + c.common.ev = (const struct net_event) { - case NET_DOMAIN_IPV4: - { - errno = 0; - - const unsigned long port = strtoul(m->port_le.text, NULL, 10); - - if (errno) - { - m->connecting.text = "Invalid port"; - return -1; - } - - c.ipv4.addr = m->address_le.text; - c.ipv4.port = port; - } - break; - - case NET_DOMAIN_SERIAL: - break; - } + .connected = on_connected, + .disconnected = on_disconnected, + .arg = m + }; - if (!(m->socket = net_connect(&c))) + if (!(m->host = net_connect(&c))) { fprintf(stderr, "%s: net_connect failed\n", __func__); - on_disconnected(m); + on_disconnected(NULL, m); return -1; } return 0; } -static int update(struct menu_common *const c, void *const arg) +static int update_socket(struct join_menu *const m) { - struct join_menu *const m = arg; - - m->bcnt.common.y = screen_h - 40; - - if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) - || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) - return -1; - - if (m->socket && net_update(m->socket)) + if (m->host && net_update(m->host)) return -1; switch (m->state) { case CONNECT: if (on_connect(m)) - { m->state = CONNECT_FAILED; + else + { + m->state = CONNECTING; + m->connecting.text = "Connecting..."; + m->connecting.common.hidden = false; break; } - m->state = CONNECTING; - m->connecting.text = "Connecting..."; - m->connecting.common.hidden = false; - break; - + /* Fall through. */ case CONNECT_FAILED: - net_close(m->socket); + net_close(m->host); m->connecting.text = "Failed to connect"; m->connecting.common.hidden = false; - m->socket = NULL; + m->host = NULL; m->state = IDLE; break; @@ -154,98 +111,101 @@ static int update(struct menu_common *const c, void *const arg) return 0; } -static int render(const struct menu_common *const c, void *const arg) +static void hide_elements(struct menu_net_gui *const m) { - const struct join_menu *const m = arg; - - if (gui_render(&m->cnt.common) - || gui_render(&m->bcnt.common)) - return -1; + if (m->domain == NET_DOMAIN_IPV4) + { + struct menu_ipv4_gui *const g = &m->gui.ipv4; - return 0; + g->announce_cnt.common.hidden = true; + } } -static int update_domain(struct join_menu *const m) +static int update(struct menu_common *const c, void *const arg) { - switch (domains[m->domain_i]) - { - case NET_DOMAIN_IPV4: - m->port.common.hidden = false; - m->port_le.common.hidden = false; - m->address_le.common.hidden = false; - m->device_btn.common.hidden = true; - m->speed.common.hidden = true; - m->speed_btn.common.hidden = true; - m->address_device.text = "Address:"; - break; + struct join_menu *const m = arg; - case NET_DOMAIN_SERIAL: - m->port.common.hidden = true; - m->port_le.common.hidden = true; - m->address_le.common.hidden = true; - m->device_btn.common.hidden = false; - m->speed.common.hidden = false; - m->speed_btn.common.hidden = false; - m->address_device.text = "Device:"; - m->speed_btn.u.type1.label.text = speeds[m->speed_i = 0]; - m->devices = net_serial_devices(&m->n_devices); - - if (!m->devices) - return -1; - - m->device_btn.u.type1.label.text = m->devices[m->device_i = 0]; - break; - } + m->bcnt.common.y = screen_h - 40; + menu_net_gui_update(&m->net); + update_socket(m); + hide_elements(&m->net); + + if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) + || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) + return -1; return 0; } -static void on_type_pressed(void *const arg) +static int render(const struct menu_common *const c, void *const arg) { - struct join_menu *const m = arg; + const struct join_menu *const m = arg; - do - if (++m->domain_i >= sizeof domains / sizeof *domains) - m->domain_i = 0; - while (!net_available(domains[m->domain_i])); + if (gui_render(&m->cnt.common) + || gui_render(&m->bcnt.common)) + return -1; - m->type_btn.u.type1.label.text = net_domain_str(domains[m->domain_i]); - update_domain(m); + return 0; } -static void on_device_pressed(void *const arg) +static void on_connect_pressed(void *const arg) { struct join_menu *const m = arg; - if (++m->device_i >= m->n_devices) - m->device_i = 0; - - m->device_btn.u.type1.label.text = m->devices[m->device_i]; + if (m->state == IDLE) + m->state = CONNECT; } -static void on_speed_pressed(void *const arg) +static void init_footer_gui(struct join_menu *const m, bool *const back) { - struct join_menu *const m = arg; + { + struct gui_label *const l = &m->connecting; - if (++m->speed_i >= sizeof speeds / sizeof *speeds) - m->speed_i = 0; + gui_label_init(l); + l->common.hcentered = true; + l->common.hidden = true; + gui_add_child(&m->cnt.common, &l->common); + } - m->speed_btn.u.type1.label.text = speeds[m->speed_i]; -} + { + struct gui_container *const c = &m->bcnt; -static void on_connect_pressed(void *const arg) -{ - struct join_menu *const m = arg; + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + } - if (m->state == IDLE) - m->state = CONNECT; + { + struct gui_button *const b = &m->connect; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Connect"; + b->u.type1.w = 140; + b->common.vcentered = true; + b->on_pressed = on_connect_pressed; + b->arg = m; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->u.type1.w = 100; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = back; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } } int menu_join(struct menu_common *const c) { int ret = -1; struct join_menu m = {0}; - bool connect = false; bool back = false; gui_container_init(&m.cnt); @@ -254,107 +214,23 @@ int menu_join(struct menu_common *const c) m.cnt.mode = GUI_CONTAINER_MODE_V; m.cnt.spacing = 2; - gui_label_init(&m.type_label); - m.type_label.common.hcentered = true; - m.type_label.text = "Type:"; - gui_add_child(&m.cnt.common, &m.type_label.common); - - while (!net_available(domains[m.domain_i])) - if (++m.domain_i >= sizeof domains / sizeof *domains) - return -1; - - gui_button_init(&m.type_btn, GUI_BUTTON_TYPE_1); - m.type_btn.arg = &m; - m.type_btn.u.type1.w = 140; - m.type_btn.common.hcentered = true; - m.type_btn.on_pressed = on_type_pressed; - m.type_btn.u.type1.label.text = net_domain_str(domains[m.domain_i]); - gui_add_child(&m.cnt.common, &m.type_btn.common); - - gui_label_init(&m.address_device); - m.address_device.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.address_device.common); - - gui_button_init(&m.device_btn, GUI_BUTTON_TYPE_1); - m.device_btn.common.hcentered = true; - m.device_btn.on_pressed = on_device_pressed; - m.device_btn.arg = &m; - m.device_btn.u.type1.w = 140; - gui_add_child(&m.cnt.common, &m.device_btn.common); - - char address[sizeof "255.255.255.255"]; - gui_line_edit_init(&m.address_le, address, sizeof address); - m.address_le.w = 140; - m.address_le.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.address_le.common); - - gui_label_init(&m.port); - m.port.common.hcentered = true; - m.port.text = "Port:"; - gui_add_child(&m.cnt.common, &m.port.common); - - char port[sizeof "65535"]; - gui_line_edit_init(&m.port_le, port, sizeof port); - m.port_le.w = 80; - m.port_le.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.port_le.common); - - gui_label_init(&m.speed); - m.speed.font = FONT; - m.speed.text = "Baud rate:"; - m.speed.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.speed.common); - - gui_button_init(&m.speed_btn, GUI_BUTTON_TYPE_1); - m.speed_btn.arg = &m; - m.speed_btn.on_pressed = on_speed_pressed; - m.speed_btn.u.type1.w = 140; - gui_add_child(&m.cnt.common, &m.speed_btn.common); - - gui_label_init(&m.connecting); - m.connecting.common.hcentered = true; - m.connecting.common.hidden = true; - gui_add_child(&m.cnt.common, &m.connecting.common); - - gui_container_init(&m.bcnt); - m.bcnt.common.hcentered = true; - m.bcnt.mode = GUI_CONTAINER_MODE_H; - - gui_button_init(&m.connect, GUI_BUTTON_TYPE_1); - m.connect.u.type1.label.text = "Connect"; - m.connect.u.type1.w = 140; - m.connect.common.vcentered = true; - m.connect.on_pressed = on_connect_pressed; - m.connect.arg = &m; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.connect.common); - - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.u.type1.w = 100; - m.back.common.vcentered = true; - m.back.on_pressed = menu_on_pressed; - m.back.arg = &back; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.back.common); - - if (update_domain(&m)) + if (menu_net_gui_init(&m.net)) return -1; - while (!back && !c->p.common.exit && !connect) - { + gui_add_child(&m.cnt.common, &m.net.cnt.common); + init_footer_gui(&m, &back); + + while (!back && !c->p.common.exit && m.state != CONNECTED) if (menu_update(c, update, render, &m)) goto end; - } - if (connect) - { - - } + if (m.state == CONNECTED && menu_gamecfg(c, m.host, m.peer)) + goto end; ret = 0; end: - gui_deinit(&m.cnt.common, &c->in); + net_close(m.host); + gui_deinit(&m.net.cnt.common, &c->in); return ret; } diff --git a/src/menu/src/main_menu.c b/src/menu/src/main_menu.c index 0c9ddb0..49e0fd8 100644 --- a/src/menu/src/main_menu.c +++ b/src/menu/src/main_menu.c @@ -9,8 +9,8 @@ struct main_menu { - bool start, exit; - struct gui_button play, exit_btn; + bool start, settings, exit; + struct gui_button play, settings_btn, exit_btn; struct gui_container cnt; }; @@ -36,14 +36,10 @@ static int render(const struct menu_common *const c, void *const arg) int menu_main(struct menu_common *const c) { - bool back; - do { struct main_menu m = {0}; - back = false; - gui_container_init(&m.cnt); m.cnt.mode = GUI_CONTAINER_MODE_V; m.cnt.common.hcentered = true; @@ -58,6 +54,14 @@ int menu_main(struct menu_common *const c) m.play.u.type1.label.text = "Play"; gui_add_child(&m.cnt.common, &m.play.common); + gui_button_init(&m.settings_btn, GUI_BUTTON_TYPE_1); + m.settings_btn.on_pressed = menu_on_pressed; + m.settings_btn.arg = &m.settings; + m.settings_btn.u.type1.w = 140; + m.settings_btn.common.hcentered = true; + m.settings_btn.u.type1.label.text = "Settings"; + gui_add_child(&m.cnt.common, &m.settings_btn.common); + if (system_can_exit()) { gui_button_init(&m.exit_btn, GUI_BUTTON_TYPE_1); @@ -69,7 +73,7 @@ int menu_main(struct menu_common *const c) gui_add_child(&m.cnt.common, &m.exit_btn.common); } - while (!m.start) + while (!m.start && !m.settings && !c->p.common.exit) { if (menu_update(c, update, render, &m)) return -1; @@ -78,10 +82,12 @@ int menu_main(struct menu_common *const c) return 0; } - if (menu_hostjoin(c, &back)) + if (m.start && menu_hostjoin(c)) + return -1; + else if (m.settings && menu_settings(c)) return -1; - } while (back && !c->p.common.exit); + } while (!c->p.common.exit); return 0; } diff --git a/src/menu/src/menu.c b/src/menu/src/menu.c index ba90708..eb99619 100644 --- a/src/menu/src/menu.c +++ b/src/menu/src/menu.c @@ -5,6 +5,7 @@ #include <gfx.h> #include <input.h> #include <peripheral.h> +#include <settings.h> #include <system.h> #include <stdbool.h> @@ -62,18 +63,17 @@ int menu_update(struct menu_common *const c, int menu(void) { - const struct peripheral_cfg cfg = - { - .type = PERIPHERAL_TYPE_KEYBOARD_MOUSE - }; - + struct peripheral_cfg cfg; struct menu_common c = {0}; + peripheral_get_default(&cfg); + if (game_resinit()) return -1; cursor_init(&c.cam.cursor); peripheral_init(&cfg, &c.p); + settings_load("settings.ini", &c.s); return menu_main(&c); } diff --git a/src/menu/src/menu_net.c b/src/menu/src/menu_net.c new file mode 100644 index 0000000..5b8eedc --- /dev/null +++ b/src/menu/src/menu_net.c @@ -0,0 +1,447 @@ +#include <menu.h> +#include <menu_net.h> +#include <game.h> +#include <gui.h> +#include <gui/container.h> +#include <gui/label.h> +#include <gui/button.h> +#include <gui/line_edit.h> +#include <gui/checkbox.h> +#include <net.h> +#include <net/serial.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static const enum net_domain domains[] = +{ + NET_DOMAIN_IPV4, + NET_DOMAIN_SERIAL +}; + +static void on_relay_pressed(const bool active, void *const arg) +{ + struct menu_ipv4_gui *const m = arg; + + m->next_page_btn.common.hidden = !active; +} + +static void init_ipv4_gui(struct menu_ipv4_gui *const m, + char *const addr, const size_t addrlen, + char *const port, const size_t portlen) +{ + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 2; + } + + { + struct gui_label *const l = &m->address; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Address:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->address_le; + + gui_line_edit_init(le, addr, addrlen); + le->w = 280; + le->common.hcentered = true; + gui_add_child(&m->cnt.common, &le->common); + } + + { + struct gui_label *const l = &m->port; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Port:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->port_le; + + gui_line_edit_init(le, port, portlen); + le->w = 80; + le->common.hcentered = true; + le->filters = GUI_LINE_EDIT_FILTER_NUM; + gui_add_child(&m->cnt.common, &le->common); + } + + { + struct gui_container *const c = &m->relay_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 4; + c->common.hcentered = true; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &m->relay_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->on_pressed = on_relay_pressed; + c->arg = m; + gui_add_child(&m->relay_cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->relay; + + gui_label_init(l); + l->common.vcentered = true; + l->font = FONT; + l->text = "Relay to serial"; + gui_add_child(&m->relay_cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->next_page_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.vcentered = true; + b->u.type1.label.text = "Next page"; + b->u.type1.w = 140; + b->common.hidden = true; + gui_add_child(&m->relay_cnt.common, &b->common); + } + + { + struct gui_container *const c = &m->announce_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 4; + c->common.hcentered = true; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &m->announce_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->arg = m; + gui_add_child(&m->announce_cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->announce; + + gui_label_init(l); + l->common.vcentered = true; + l->font = FONT; + l->text = "Announce server"; + gui_add_child(&m->announce_cnt.common, &l->common); + } +} + +static const char *const speeds[] = +{ + "19200", "38400", "57600", "115200" +}; + +static void on_speed_pressed(void *const arg) +{ + struct menu_serial_gui *const m = arg; + + if (++m->speed_i >= sizeof speeds / sizeof *speeds) + m->speed_i = 0; + + m->speed_btn.u.type1.label.text = speeds[m->speed_i]; +} + +static void init_serial_gui(struct menu_serial_gui *const m, + char *const buf, const size_t n) +{ + m->speed_i = sizeof speeds / sizeof *speeds - 1; + + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 2; + } + + { + struct gui_label *const l = &m->device; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Device:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->device_le; + + gui_line_edit_init(le, buf, n); + le->w = 280; + le->common.hcentered = true; + gui_add_child(&m->cnt.common, &le->common); + } + + { + struct gui_label *const l = &m->speed; + + gui_label_init(l); + l->font = FONT; + l->text = "Baud rate:"; + l->common.hcentered = true; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->speed_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->arg = m; + b->on_pressed = on_speed_pressed; + b->u.type1.w = 140; + b->u.type1.label.text = speeds[m->speed_i]; + b->common.hcentered = true; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_container *const c = &m->hw_ctrl_cnt; + + gui_container_init(c); + c->spacing = 8; + c->mode = GUI_CONTAINER_MODE_H; + c->common.hcentered = true; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->hw_ctrl; + + gui_label_init(l); + l->text = "HW control"; + l->font = FONT; + l->common.vcentered = true; + gui_add_child(&m->hw_ctrl_cnt.common, &l->common); + } + + { + struct gui_checkbox *const c = &m->hw_ctrl_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + gui_add_child(&m->hw_ctrl_cnt.common, &c->common); + } +} + +static void on_type_pressed(void *const arg) +{ + struct menu_net_gui *const m = arg; + + do + if (++m->domain >= sizeof domains / sizeof *domains) + m->domain = 0; + while (!net_available(domains[m->domain])); + + m->update_domain = true; +} + +static void update_domain(struct menu_net_gui *const m) +{ + const enum net_domain d = domains[m->domain]; + + switch (d) + { + case NET_DOMAIN_IPV4: + init_ipv4_gui(&m->gui.ipv4, + m->address, sizeof m->address, + m->port, sizeof m->port); + break; + + case NET_DOMAIN_SERIAL: + { + struct menu_serial_gui *const s = &m->gui.serial; + + init_serial_gui(s, m->device, sizeof m->device); + + if (net_serial_unique()) + { + s->device.common.hidden = true; + s->device_le.common.hidden = true; + } + else + s->device.common.hidden = false; + + break; + } + } + + m->type_btn.u.type1.label.text = net_domain_str(d); + m->cnt.common.hcentered = true; + /* Restore parent removed by init function. */ + m->gui.cnt.common.parent = &m->cnt.common; +} + +static int set_common(const struct menu_net_gui *const m, + const char **const addr, short *const port, + const char **const dev, unsigned long *const baud) +{ + switch (m->domain) + { + case NET_DOMAIN_IPV4: + { + const struct menu_ipv4_gui *const g = &m->gui.ipv4; + + if (addr) + *addr = g->address_le.text; + + if (port) + { + const char *const t = g->port_le.text; + + if (!*t) + return -1; + + errno = 0; + + const unsigned long port_ul = strtoul(t, NULL, 10); + + if (errno) + { + fprintf(stderr, "%s: strtoul(3) failed: %s\n", + __func__, strerror(errno)); + return -1; + } + + *port = port_ul; + } + } + break; + + case NET_DOMAIN_SERIAL: + { + const struct menu_serial_gui *const g = &m->gui.serial; + + if (dev) + *dev = g->device_le.label.text; + + if (baud) + { + const char *const t = g->speed_btn.u.type1.label.text; + + if (!*t) + return -1; + + errno = 0; + *baud = strtoul(t, NULL, 10); + + if (errno) + { + fprintf(stderr, "%s: strtoul(3) failed: %s\n", + __func__, strerror(errno)); + return -1; + } + } + } + break; + } + + return 0; +} + +int menu_net_server(const struct menu_net_gui *const m, + union net_server *const s) +{ + *s = (const union net_server) + { + .common = + { + .domain = m->domain, + .max_players = GAME_MAX_PLAYERS - 1 + } + }; + + struct net_serial_cfg *const scfg = &s->serial.cfg; + + return set_common(m, NULL, &s->ipv4.port, &scfg->dev, &scfg->baud); +} + +int menu_net_connect(const struct menu_net_gui *const m, + union net_connect *const c) +{ + *c = (const union net_connect) + { + .common = + { + .domain = m->domain + } + }; + + struct net_serial_cfg *const scfg = &c->serial.cfg; + + return set_common(m, &c->ipv4.addr, &c->ipv4.port, &scfg->dev, &scfg->baud); +} + +void menu_net_gui_update(struct menu_net_gui *const m) +{ + if (m->update_domain) + { + m->update_domain = false; + update_domain(m); + } +} + +int menu_net_gui_init(struct menu_net_gui *const m) +{ + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 1; + } + + { + struct gui_label *const l = &m->type; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Type:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->type_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.hcentered = true; + b->u.type1.w = 140; + b->u.type1.label.text = net_domain_str(domains[m->domain]); + b->on_pressed = on_type_pressed; + b->arg = m; + gui_add_child(&m->cnt.common, &b->common); + } + + /* Set default port. */ + strcpy(m->port, "7822"); + + while (!net_available(domains[m->domain])) + if (++m->domain >= sizeof domains / sizeof *domains) + return -1; + + update_domain(m); + gui_add_child(&m->cnt.common, &m->gui.cnt.common); + return 0; +} diff --git a/src/menu/src/settings_menu.c b/src/menu/src/settings_menu.c index 0e782bd..72154fb 100644 --- a/src/menu/src/settings_menu.c +++ b/src/menu/src/settings_menu.c @@ -1,22 +1,77 @@ #include <menu_private.h> +#include <game.h> +#include <gfx.h> #include <gui.h> #include <gui/container.h> +#include <gui/checkbox.h> #include <gui/label.h> #include <gui/line_edit.h> +#include <gui/rounded_rect.h> #include <gui/button.h> +#include <errno.h> #include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> struct menu_settings { - struct gui_container cnt; - struct gui_button back; + struct gui_container cnt, bcnt, fs_cnt, res_cnt; + struct gui_rounded_rect r; + struct gui_checkbox fs_ch; + struct gui_label fs, xres, yres, periph, pad, name; + struct gui_button apply, back, periph_btn, pad_btn; + struct gui_line_edit name_le, xres_le, yres_le; + int pad_i; + size_t n_pads; + enum peripheral_type type; +}; + +static bool keyboard_mouse_available(void) +{ + return keyboard_available() && mouse_available(); +} + +static bool pad_available(void) +{ + return pad_count(); +} + +static const struct type +{ + const char *str; + bool (*available)(void); +} types[] = +{ + [PERIPHERAL_TYPE_KEYBOARD_MOUSE] = + { + .str = "Keyboard + mouse", + .available = keyboard_mouse_available + }, + + [PERIPHERAL_TYPE_TOUCH] = + { + .str = "Touch", + .available = keyboard_mouse_available + }, + + [PERIPHERAL_TYPE_PAD] = + { + .str = "Gamepad", + .available = pad_available + } }; static int update(struct menu_common *const c, void *const arg) { struct menu_settings *const m = arg; - if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in)) + m->bcnt.common.y = screen_h - 40; + m->res_cnt.common.hidden = !m->fs_ch.active; + + if (gui_update(&m->r.common, &c->p, &c->cam, &c->in) + || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) return -1; return 0; @@ -26,37 +81,332 @@ static int render(const struct menu_common *const c, void *const arg) { const struct menu_settings *const m = arg; - if (gui_render(&m->cnt.common)) + if (gui_render(&m->r.common) + || gui_render(&m->bcnt.common)) return -1; return 0; } -int menu_settings(struct menu_common *const c, bool *const back) +static void update_device(struct menu_settings *const m) +{ + const struct type *const t = &types[m->type]; + + m->periph_btn.u.type1.label.text = t->str; + + if (m->type == PERIPHERAL_TYPE_PAD) + { + m->pad_btn.common.hidden = false; + m->pad.common.hidden = false; + m->pad_btn.u.type1.label.text = pad_name(m->pad_i = 0); + } + else + { + m->pad_btn.common.hidden = true; + m->pad.common.hidden = true; + } +} + +static void on_input_pressed(void *const arg) { + struct menu_settings *const m = arg; + enum peripheral_type i = m->type; + do { - struct menu_settings m; + if (++i >= sizeof types / sizeof *types) + i = 0; + + const struct type *const t = &types[i]; + + if (t->available()) + { + m->type = i; + update_device(m); + break; + } + + } while (i != m->type); +} + +static void init_settings_gui(struct menu_settings *const m, + char *const name, const size_t name_len) +{ + { + struct gui_label *const l = &m->name; - gui_container_init(&m.cnt); - m.cnt.common.hcentered = true; - m.cnt.common.vcentered = true; - m.cnt.spacing = 4; - m.cnt.mode = GUI_CONTAINER_MODE_V; + gui_label_init(l); + l->font = FONT; + l->common.hcentered = true; + l->text = "Name:"; + gui_add_child(&m->cnt.common, &l->common); + } - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.common.hcentered = true; - m.back.u.type1.w = 140; - m.back.arg = back; - m.back.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.back.common); + { + struct gui_line_edit *const le = &m->name_le; - while (!*back && !c->p.common.exit) - if (menu_update(c, update, render, &m)) - return -1; + gui_line_edit_init(le, name, name_len); + le->common.hcentered = true; + le->w = 140; + gui_add_child(&m->cnt.common, &le->common); + } - } while (!*back && !c->p.common.exit); + { + struct gui_label *const l = &m->periph; - return 0; + gui_label_init(l); + l->common.hcentered = true; + l->font = FONT; + l->text = "Input method:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->periph_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.hcentered = true; + b->u.type1.w = 200; + b->on_pressed = on_input_pressed; + b->arg = m; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_label *const l = &m->pad; + + gui_label_init(l); + l->common.hcentered = true; + l->font = FONT; + l->text = "Pad:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->pad_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.hcentered = true; + b->u.type1.w = 200; + gui_add_child(&m->cnt.common, &b->common); + } + + update_device(m); +} + +static void init_display_gui(struct menu_settings *const m, + char *const xres, const size_t xres_len, char *const yres, + const size_t yres_len) +{ + enum {WIDTH = 60}; + + { + struct gui_container *const c = &m->res_cnt; + + gui_container_init(c); + c->common.hcentered = true; + c->spacing = 2; + c->mode = GUI_CONTAINER_MODE_H; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->xres; + + gui_label_init(l); + l->font = FONT; + l->text = "x:"; + l->common.vcentered = true; + gui_add_child(&m->res_cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->xres_le; + + gui_line_edit_init(le, xres, xres_len); + le->common.vcentered = true; + le->w = WIDTH; + le->filters = GUI_LINE_EDIT_FILTER_NUM; + gui_add_child(&m->res_cnt.common, &le->common); + } + + { + struct gui_label *const l = &m->yres; + + gui_label_init(l); + l->font = FONT; + l->text = "y:"; + l->common.vcentered = true; + gui_add_child(&m->res_cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->yres_le; + + gui_line_edit_init(le, yres, yres_len); + le->common.vcentered = true; + le->w = WIDTH; + le->filters = GUI_LINE_EDIT_FILTER_NUM; + gui_add_child(&m->res_cnt.common, &le->common); + } +} + +static void init_fullscreen_gui(struct menu_settings *const m) +{ + { + struct gui_container *const c = &m->fs_cnt; + + gui_container_init(c); + c->common.hcentered = true; + c->spacing = 8; + c->mode = GUI_CONTAINER_MODE_H; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &m->fs_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->active = gfx_fullscreen(); + gui_add_child(&m->fs_cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->fs; + + gui_label_init(l); + l->font = FONT; + l->text = "Fullscreen"; + l->common.vcentered = true; + gui_add_child(&m->fs_cnt.common, &l->common); + } +} + +static void init_footer_gui(struct menu_settings *const m, bool *const back, + bool *const apply) +{ + { + struct gui_container *const c = &m->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + } + + { + struct gui_button *const b = &m->apply; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Apply"; + b->common.vcentered = true; + b->u.type1.w = 140; + b->on_pressed = menu_on_pressed; + b->arg = apply; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->common.vcentered = true; + b->u.type1.w = 100; + b->arg = back; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->bcnt.common, &b->common); + } +} + +static int apply_settings(struct menu_common *const c, + struct menu_settings *const m, + struct settings *const s) +{ + if (gfx_fullscreen_available() + && (s->fullscreen = m->fs_ch.active)) + { + const unsigned long w = strtoul(m->xres_le.text, NULL, 10), + h = strtoul(m->yres_le.text, NULL, 10); + + if (!w) + { + fprintf(stderr, "%s: unexpected X resolution %lu\n", + __func__, w); + return -1; + } + else if (!h) + { + fprintf(stderr, "%s: unexpected Y resolution %lu\n", + __func__, h); + return -1; + } + + s->screen_w = w; + s->screen_h = h; + } + + s->periph_type = m->type; + s->pad_i = m->pad_i; + strcpy(s->name, m->name_le.text); + return settings_apply(s, &(const struct settings_rt){.p = &c->p}); +} + +int menu_settings(struct menu_common *const c) +{ + int ret = -1; + struct menu_settings m = {0}; + + { + struct gui_rounded_rect *const r = &m.r; + + gui_rounded_rect_init(r); + r->adjust = true; + r->common.hcentered = true; + r->common.vcentered = true; + } + + { + struct gui_container *const c = &m.cnt; + + gui_container_init(c); + c->spacing = 4; + c->common.hcentered = true; + c->common.vcentered = true; + c->mode = GUI_CONTAINER_MODE_V; + gui_add_child(&m.r.common, &c->common); + } + + struct settings *const s = &c->s; + char name[sizeof s->name], xres[sizeof "65535"], yres[sizeof xres]; + bool back = false; + + strcpy(name, s->name); + m.type = c->p.common.type; + init_settings_gui(&m, name, sizeof name); + + if (gfx_fullscreen_available()) + { + init_fullscreen_gui(&m); + snprintf(xres, sizeof xres, "%hd", s->screen_w); + snprintf(yres, sizeof yres, "%hd", s->screen_h); + init_display_gui(&m, xres, sizeof xres, yres, sizeof yres); + } + + bool apply = false; + + init_footer_gui(&m, &back, &apply); + + while (!back && !c->p.common.exit && !apply) + if (menu_update(c, update, render, &m)) + goto end; + + if (apply && apply_settings(c, &m, s)) + goto end; + + ret = 0; + +end: + gui_deinit(&m.cnt.common, &c->in); + return ret; } |
