rts/src/menu/src/join_menu.c

361 lines
8.8 KiB
C

#include <menu.h>
#include <menu_private.h>
#include <gui.h>
#include <gui/button.h>
#include <gui/container.h>
#include <gui/checkbox.h>
#include <gui/label.h>
#include <gui/line_edit.h>
#include <gui/rounded_rect.h>
#include <net.h>
#include <net/serial.h>
#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;
enum
{
IDLE,
CONNECT,
CONNECTING,
CONNECTED,
CONNECT_FAILED
} state;
struct net_socket *socket;
size_t domain_i, speed_i, device_i, n_devices;
const char *const *devices;
};
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)
{
struct join_menu *const m = arg;
m->state = CONNECTED;
}
static void on_disconnected(void *const arg)
{
struct join_menu *const m = arg;
m->state = CONNECT_FAILED;
m->socket = NULL;
}
static int on_connect(struct join_menu *const m)
{
const enum net_domain d = domains[m->domain_i];
union net_connect c =
{
.common =
{
.domain = d,
.ev =
{
.connected = on_connected,
.disconnected = on_disconnected,
.arg = m
}
}
};
switch (d)
{
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;
}
if (!(m->socket = net_connect(&c)))
{
fprintf(stderr, "%s: net_connect failed\n", __func__);
on_disconnected(m);
return -1;
}
return 0;
}
static int update(struct menu_common *const c, void *const arg)
{
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))
return -1;
switch (m->state)
{
case CONNECT:
if (on_connect(m))
{
m->state = CONNECT_FAILED;
break;
}
m->state = CONNECTING;
m->connecting.text = "Connecting...";
m->connecting.common.hidden = false;
break;
case CONNECT_FAILED:
net_close(m->socket);
m->connecting.text = "Failed to connect";
m->connecting.common.hidden = false;
m->socket = NULL;
m->state = IDLE;
break;
default:
break;
}
return 0;
}
static int render(const struct menu_common *const c, void *const arg)
{
const struct join_menu *const m = arg;
if (gui_render(&m->cnt.common)
|| gui_render(&m->bcnt.common))
return -1;
return 0;
}
static int update_domain(struct join_menu *const m)
{
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;
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;
}
return 0;
}
static void on_type_pressed(void *const arg)
{
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]));
m->type_btn.u.type1.label.text = net_domain_str(domains[m->domain_i]);
update_domain(m);
}
static void on_device_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];
}
static void on_speed_pressed(void *const arg)
{
struct join_menu *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 on_connect_pressed(void *const arg)
{
struct join_menu *const m = arg;
if (m->state == IDLE)
m->state = CONNECT;
}
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);
m.cnt.common.hcentered = true;
m.cnt.common.vcentered = true;
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))
return -1;
while (!back && !c->p.common.exit && !connect)
{
if (menu_update(c, update, render, &m))
goto end;
}
if (connect)
{
}
ret = 0;
end:
gui_deinit(&m.cnt.common, &c->in);
return ret;
}