#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; }