aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-09-27 17:03:06 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-11-01 16:26:16 +0100
commit980858186149651df5543b6fc99a4f7db0cdd089 (patch)
treed347200b0a562d84df505097651ad0642f207fdd
parent39f50e601d395bbd2d78d0147ac530b756da2fff (diff)
WIP
-rw-r--r--CMakeLists.txt3
-rw-r--r--README.md4
-rw-r--r--cmake/host.cmake2
-rw-r--r--cmake/win9x.cmake11
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/camera/src/mouse.c3
-rw-r--r--src/camera/src/pad.c4
-rw-r--r--src/game/CMakeLists.txt2
-rw-r--r--src/game/inc/game.h22
-rw-r--r--src/game/src/game.c73
-rw-r--r--src/gfx/inc/gfx.h4
-rw-r--r--src/gfx/ps1/src/env.c15
-rw-r--r--src/gfx/sdl-1.2/src/env.c17
-rw-r--r--src/gfx/sdl-1.2/src/sprite.c1
-rw-r--r--src/gui/inc/gui/rounded_rect.h7
-rw-r--r--src/gui/src/button_type1.c5
-rw-r--r--src/gui/src/line_edit.c7
-rw-r--r--src/gui/src/rounded_rect.c58
-rw-r--r--src/input/inc/input.h2
-rw-r--r--src/input/src/input.c50
-rw-r--r--src/keyboard/CMakeLists.txt2
-rw-r--r--src/keyboard/inc/keyboard.h27
-rw-r--r--src/keyboard/inc/keyboard/combo.h15
-rw-r--r--src/keyboard/inc/keyboard/key.h (renamed from src/keyboard/inc/keyboard_key.h)1
-rw-r--r--src/keyboard/ps1/inc/keyboard/port.h18
-rw-r--r--src/keyboard/ps1/src/keyboard.c5
-rw-r--r--src/keyboard/sdl-1.2/inc/keyboard/port.h21
-rw-r--r--src/keyboard/sdl-1.2/src/keyboard.c184
-rw-r--r--src/keyboard/src/keyboard.c112
-rw-r--r--src/menu/CMakeLists.txt16
-rw-r--r--src/menu/privinc/menu_net.h62
-rw-r--r--src/menu/privinc/menu_private.h11
-rw-r--r--src/menu/src/common.c11
-rw-r--r--src/menu/src/gamecfg_menu.c735
-rw-r--r--src/menu/src/host_menu.c159
-rw-r--r--src/menu/src/hostjoin_menu.c129
-rw-r--r--src/menu/src/join_menu.c334
-rw-r--r--src/menu/src/main_menu.c24
-rw-r--r--src/menu/src/menu.c10
-rw-r--r--src/menu/src/menu_net.c447
-rw-r--r--src/menu/src/settings_menu.c396
-rw-r--r--src/mouse/inc/mouse.h1
-rw-r--r--src/mouse/ps1/src/mouse.c4
-rw-r--r--src/mouse/sdl-1.2/src/mouse.c5
-rw-r--r--src/mouse/src/mouse.c5
-rw-r--r--src/net/CMakeLists.txt5
-rw-r--r--src/net/inc/net.h65
-rw-r--r--src/net/inc/net/serial.h4
-rw-r--r--src/net/posix/src/serial.c276
-rw-r--r--src/net/privinc/net_private.h34
-rw-r--r--src/net/privinc/net_serial_private.h19
-rw-r--r--src/net/ps1/src/net.c238
-rw-r--r--src/net/src/enet/ipv4.c111
-rw-r--r--src/net/src/net.c99
-rw-r--r--src/net/src/serial.c37
-rw-r--r--src/net/win9x/src/serial.c404
-rw-r--r--src/packet/CMakeLists.txt3
-rw-r--r--src/packet/inc/packet.h91
-rw-r--r--src/packet/src/packet.c251
-rw-r--r--src/pad/inc/pad.h8
-rw-r--r--src/pad/ps1/src/pad.c26
-rw-r--r--src/pad/sdl-1.2/inc/pad/port.h4
-rw-r--r--src/pad/sdl-1.2/src/pad.c48
-rw-r--r--src/peripheral/inc/peripheral.h1
-rw-r--r--src/peripheral/src/peripheral.c11
-rw-r--r--src/player/inc/human_player.h5
-rw-r--r--src/player/src/human_player.c30
-rw-r--r--src/player/src/human_player_gui.c5
-rw-r--r--src/settings/CMakeLists.txt13
-rw-r--r--src/settings/inc/settings.h36
-rw-r--r--src/settings/ps1/inc/settings/port.h16
-rw-r--r--src/settings/sdl-1.2/inc/settings/port.h15
-rw-r--r--src/settings/src/settings.c48
-rw-r--r--src/system/CMakeLists.txt9
-rw-r--r--src/system/sdl-1.2/privinc/system_private.h15
-rw-r--r--src/system/sdl-1.2/src/stubs.c6
-rw-r--r--src/system/sdl-1.2/src/system.c11
-rw-r--r--src/system/win9x/src/system.c36
-rw-r--r--src/transport/CMakeLists.txt10
-rw-r--r--src/transport/inc/transport.h66
-rw-r--r--src/transport/privinc/transport_private.h66
-rw-r--r--src/transport/src/heap.c93
-rw-r--r--src/transport/src/static.c71
-rw-r--r--src/transport/src/transport.c613
84 files changed, 5151 insertions, 777 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c2a1339..86037b4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,12 +16,13 @@ if(CMAKE_TOOLCHAIN_FILE MATCHES "ps1")
elseif(CMAKE_TOOLCHAIN_FILE MATCHES "win9x")
set(WIN9X_BUILD 1)
set(SDL1_2_BUILD 1)
+ set(exec_flags WIN32)
else()
set(HOST_BUILD 1)
set(SDL1_2_BUILD 1)
endif()
-add_executable(${PROJECT_NAME} "src/main.c")
+add_executable(${PROJECT_NAME} ${exec_flags} "src/main.c")
if(SDL1_2_BUILD)
find_package(SDL 1.2 REQUIRED)
diff --git a/README.md b/README.md
index b8a685e..9564516 100644
--- a/README.md
+++ b/README.md
@@ -83,6 +83,10 @@ This will generate a `.bin`/`.cue` file pair in `build` that can be
played on an emulator or burnt into a CD-r in order to play the game
on real hardware.
+A custom version of [PSXSDK](https://git.disroot.org/xavi92/psxsdk) is
+required. Once built, the installation prefix must be stored into an
+environment variable called `PSXSDK_PATH`.
+
#### Microsoft® Win9x
```sh
diff --git a/cmake/host.cmake b/cmake/host.cmake
index 6fa47b9..36d7d70 100644
--- a/cmake/host.cmake
+++ b/cmake/host.cmake
@@ -1,5 +1,5 @@
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
- set(cflags ${cflags} -Og)
+ set(cflags ${cflags} -Og)
else()
set(cflags ${cflags} -O3)
endif()
diff --git a/cmake/win9x.cmake b/cmake/win9x.cmake
index 0d91520..c66ed5e 100644
--- a/cmake/win9x.cmake
+++ b/cmake/win9x.cmake
@@ -1,12 +1,13 @@
-add_custom_command(OUTPUT ${cdroot}/${PROJECT_NAME}
- COMMAND i386-mingw32-strip ${PROJECT_NAME} -o ${cdroot}/${PROJECT_NAME}
+add_custom_command(OUTPUT ${cdroot}/${PROJECT_NAME}.exe
+ COMMAND i386-mingw32-strip ${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}
+ -o ${cdroot}/${PROJECT_NAME}.exe
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PROJECT_NAME}
VERBATIM)
-add_custom_target(stripped-exe ALL DEPENDS ${cdroot}/${PROJECT_NAME})
+add_custom_target(stripped-exe ALL DEPENDS ${cdroot}/${PROJECT_NAME}.exe)
target_link_libraries(SDL::SDL INTERFACE gdi32 user32 winmm dxguid)
-target_link_libraries(ENET INTERFACE ws2_32)
+target_link_libraries(ENET INTERFACE wsock32)
add_compile_options(-march=i386)
@@ -14,7 +15,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# i386-mingw32-gcc 3.4.5 does not support -Og.
set(cflags ${cflags} -O0)
else()
- set(cflags ${cflags} -O2)
+ set(cflags ${cflags} -Os)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/fetch-libfixmath.cmake")
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7a35715..bb05f29 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,5 @@
# Avoid C11 since it is not supported by the i386-mingw32 toolchain.
-set(cflags -Wall -g3 -ffunction-sections -fdata-sections -pedantic -std=c99)
+set(cflags ${cflags} -Wall -g3 -ffunction-sections -fdata-sections -pedantic -std=c99)
set(components
building
@@ -16,13 +16,16 @@ set(components
menu
mouse
net
+ packet
pad
peripheral
player
resource
+ settings
sfx
system
terrain
+ transport
unit
util
)
diff --git a/src/camera/src/mouse.c b/src/camera/src/mouse.c
index 22fc3ba..30fedb9 100644
--- a/src/camera/src/mouse.c
+++ b/src/camera/src/mouse.c
@@ -13,6 +13,9 @@ static void cursor_update(struct cursor *const c, const struct mouse *const m)
c->x = m->x;
c->y = m->y;
+ c->state = mouse_pressed(m, MOUSE_BUTTON_LEFT) ||
+ mouse_pressed(m, MOUSE_BUTTON_RIGHT) ?
+ CURSOR_STATE_PRESSED: CURSOR_STATE_IDLE;
}
static void update_speed(struct camera *const cam, const struct mouse *const m)
diff --git a/src/camera/src/pad.c b/src/camera/src/pad.c
index 13cb43e..f3af527 100644
--- a/src/camera/src/pad.c
+++ b/src/camera/src/pad.c
@@ -36,8 +36,8 @@ static void cursor_update(struct camera *const cam,
&& (c->y != c->y_init || cam->y <= -cam->dim.h))
c->y += STEP;
- c->state = input_pad_pressed(in, p, PAD_KEY_A) ||
- input_pad_pressed(in, p, PAD_KEY_B) ?
+ c->state = pad_pressed(p, PAD_KEY_A) ||
+ pad_pressed(p, PAD_KEY_B) ?
CURSOR_STATE_PRESSED: CURSOR_STATE_IDLE;
}
diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt
index 0c21b0c..ce76968 100644
--- a/src/game/CMakeLists.txt
+++ b/src/game/CMakeLists.txt
@@ -1,6 +1,6 @@
add_library(game "src/game.c" "src/res.c")
target_include_directories(game PUBLIC "inc" PRIVATE "privinc")
-target_link_libraries(game PRIVATE
+target_link_libraries(game PUBLIC peripheral PRIVATE
building
container
font
diff --git a/src/game/inc/game.h b/src/game/inc/game.h
index 5298b9f..66e5e79 100644
--- a/src/game/inc/game.h
+++ b/src/game/inc/game.h
@@ -1,15 +1,35 @@
#ifndef GAME_H
#define GAME_H
-#include <stddef.h>
+#include <peripheral.h>
#ifdef __cplusplus
extern "C"
{
#endif
+enum
+{
+ GAME_MAX_PLAYERS = 4,
+ GAME_PLAYER_NAME_LEN = 16
+};
+
struct game_cfg
{
+ struct game_cfg_player
+ {
+ enum game_cfg_player_type
+ {
+ GAME_CFG_PLAYER_TYPE_HUMAN,
+ GAME_CFG_PLAYER_TYPE_BOT,
+ GAME_CFG_PLAYER_TYPE_NET
+ } type;
+
+ char name[GAME_PLAYER_NAME_LEN];
+ } *players;
+
+ size_t n;
+ union peripheral *p;
};
int game_resinit(void);
diff --git a/src/game/src/game.c b/src/game/src/game.c
index 83be2fd..baf9a86 100644
--- a/src/game/src/game.c
+++ b/src/game/src/game.c
@@ -11,36 +11,32 @@
int game(const struct game_cfg *const cfg)
{
int ret = -1;
- enum {HUMAN_PLAYERS = 1, MAP_RESOURCES = 3};
- struct human_player humans[HUMAN_PLAYERS];
+ enum {MAP_RESOURCES = 3};
+ struct human_player human;
struct terrain_map map;
terrain_init(&map);
building_set_alive_cb(terrain_block_update, &map);
- for (size_t i = 0; i < sizeof humans / sizeof *humans; i++)
+ const struct human_player_cfg hcfg =
{
- const struct human_player_cfg cfg =
+ .p = cfg->p,
+ .dim =
{
- .sel_periph = PERIPHERAL_TYPE_KEYBOARD_MOUSE,
- .padn = i,
- .dim =
- {
- .w = MAP_W,
- .h = MAP_H,
- },
+ .w = MAP_W,
+ .h = MAP_H,
+ },
- .pl =
- {
- .team = PLAYER_COLOR_BLUE,
- .x = 80,
- .y = 40
- }
- };
+ .pl =
+ {
+ .team = PLAYER_COLOR_BLUE,
+ .x = 80,
+ .y = 40
+ }
+ };
- if (human_player_init(&cfg, &humans[i]))
- goto end;
- }
+ if (human_player_init(&hcfg, &human))
+ goto end;
struct resource res[MAP_RESOURCES] = {0};
@@ -72,28 +68,23 @@ int game(const struct game_cfg *const cfg)
{
system_loop();
- for (size_t i = 0; i < sizeof humans / sizeof *humans; i++)
+ if (human.pl.alive)
{
- const struct human_player *const h = &humans[i];
-
- if (h->pl.alive)
+ struct player_others o =
{
- struct player_others o =
- {
- .res = res,
- .n_res = sizeof res / sizeof *res
- };
-
- human_player_update(&humans[i], &o);
- exit |= humans[i].periph.common.exit;
- terrain_update(&map);
-
- if (terrain_render(&map, &h->cam)
- || human_player_render(h, &o))
- goto end;
-
- /* TODO: render AI players. */
- }
+ .res = res,
+ .n_res = sizeof res / sizeof *res
+ };
+
+ human_player_update(&human, &o);
+ exit |= human.periph->common.exit;
+ terrain_update(&map);
+
+ if (terrain_render(&map, &human.cam)
+ || human_player_render(&human, &o))
+ goto end;
+
+ /* TODO: render AI players. */
}
instance_cyclic();
diff --git a/src/gfx/inc/gfx.h b/src/gfx/inc/gfx.h
index 9c4eccd..a6a16bc 100644
--- a/src/gfx/inc/gfx.h
+++ b/src/gfx/inc/gfx.h
@@ -13,8 +13,10 @@ extern "C"
int gfx_init(void);
int gfx_draw(void);
int gfx_toggle_fullscreen(void);
-bool gfx_toggle_fullscreen_available(void);
+int gfx_set_fullscreen(short w, short h);
+bool gfx_fullscreen_available(void);
bool gfx_fullscreen(void);
+int gfx_display_size(short *w, short *h);
void sprite_sort(struct sprite *s);
int sprite_clone(const struct sprite *src, struct sprite *dst);
void rect_init(struct rect *r);
diff --git a/src/gfx/ps1/src/env.c b/src/gfx/ps1/src/env.c
index 41f490e..a373b9c 100644
--- a/src/gfx/ps1/src/env.c
+++ b/src/gfx/ps1/src/env.c
@@ -38,7 +38,13 @@ int gfx_toggle_fullscreen(void)
return -1;
}
-bool gfx_toggle_fullscreen_available(void)
+int gfx_set_fullscreen(const short w, const short h)
+{
+ /* Not supported. */
+ return -1;
+}
+
+bool gfx_fullscreen_available(void)
{
return false;
}
@@ -48,6 +54,13 @@ bool gfx_fullscreen(void)
return true;
}
+int gfx_display_size(short *const w, short *const h)
+{
+ *w = SCREEN_W;
+ *h = SCREEN_H;
+ return 0;
+}
+
bool gfx_inside_drawenv(const short x, const short y, const short w,
const short h)
{
diff --git a/src/gfx/sdl-1.2/src/env.c b/src/gfx/sdl-1.2/src/env.c
index 27ed55d..4bd174a 100644
--- a/src/gfx/sdl-1.2/src/env.c
+++ b/src/gfx/sdl-1.2/src/env.c
@@ -102,6 +102,13 @@ SDL_Surface *gfx_screen(void)
return screen;
}
+int gfx_display_size(short *const w, short *const h)
+{
+ *w = display.w;
+ *h = display.h;
+ return 0;
+}
+
int gfx_init(void)
{
if (SDL_InitSubSystem(SDL_INIT_VIDEO))
@@ -157,13 +164,15 @@ int gfx_toggle_fullscreen(void)
const int w = fullscreen ? display.w : windowed.w;
const int h = fullscreen ? display.h : windowed.h;
- if ((resize_screen(w, h, fullscreen)))
- return -1;
+ return resize_screen(w, h, fullscreen);
+}
- return 0;
+int gfx_set_fullscreen(const short w, const short h)
+{
+ return resize_screen(display.w = w, display.h = h, fullscreen = true);
}
-bool gfx_toggle_fullscreen_available(void)
+bool gfx_fullscreen_available(void)
{
return true;
}
diff --git a/src/gfx/sdl-1.2/src/sprite.c b/src/gfx/sdl-1.2/src/sprite.c
index 4c1ecbc..011b2ec 100644
--- a/src/gfx/sdl-1.2/src/sprite.c
+++ b/src/gfx/sdl-1.2/src/sprite.c
@@ -37,7 +37,6 @@ int sprite_screen_resize_ev(struct sprite *const s)
int ret = -1;
SDL_Surface *const old = s->s, *const old_x = s->s_x;
- /* Magenta as transparent. */
if (s->transparent && (set_transparent(old) || set_transparent(old_x)))
goto end;
else if (!(s->s = SDL_DisplayFormat(old)))
diff --git a/src/gui/inc/gui/rounded_rect.h b/src/gui/inc/gui/rounded_rect.h
index e1203e4..13242f4 100644
--- a/src/gui/inc/gui/rounded_rect.h
+++ b/src/gui/inc/gui/rounded_rect.h
@@ -15,11 +15,9 @@ struct gui_rounded_rect
{
struct gui_common common;
unsigned short w, h;
+ bool adjust;
};
-UTIL_STATIC_ASSERT(!offsetof(struct gui_rounded_rect, common),
- "unexpected offset for struct gui_rounded_rect");
-
void gui_rounded_rect_init(struct gui_rounded_rect *r);
enum
@@ -36,6 +34,9 @@ enum
extern struct sprite gui_rounded_rect_sprites[MAX_GUI_ROUNDED_RECT_SPRITES];
+UTIL_STATIC_ASSERT(!offsetof(struct gui_rounded_rect, common),
+ "unexpected offset for struct gui_rounded_rect");
+
#ifdef __cplusplus
}
#endif
diff --git a/src/gui/src/button_type1.c b/src/gui/src/button_type1.c
index 16038b5..f07f2f3 100644
--- a/src/gui/src/button_type1.c
+++ b/src/gui/src/button_type1.c
@@ -2,6 +2,7 @@
#include <gui/button.h>
#include <gui_private.h>
#include <gui_button_private.h>
+#include <stdio.h>
struct sprite gui_button_sprites[MAX_GUI_BUTTON_SPRITES];
@@ -63,7 +64,11 @@ static int render_mid(const struct gui_button *const b,
}
}
else
+ {
+ fprintf(stderr, "%s: unexpected negative size for button %p\n",
+ __func__, (void *)b);
return -1;
+ }
return 0;
}
diff --git a/src/gui/src/line_edit.c b/src/gui/src/line_edit.c
index ef37708..df52fd0 100644
--- a/src/gui/src/line_edit.c
+++ b/src/gui/src/line_edit.c
@@ -130,7 +130,10 @@ static void on_char(const char ch, void *const user)
allowed = true;
if (allowed && l->i + 1 < l->sz)
+ {
l->text[l->i++] = ch;
+ l->text[l->i] = '\0';
+ }
}
static void on_erase(void *const user)
@@ -196,11 +199,11 @@ void gui_line_edit_init(struct gui_line_edit *const l, char *const buf,
},
.text = buf,
- .sz = sz
+ .sz = sz,
+ .i = strlen(buf)
};
gui_label_init(&l->label);
- memset(l->text, '\0', l->sz);
l->label.common.hcentered = true;
l->label.common.vcentered = true;
l->label.text = l->text;
diff --git a/src/gui/src/rounded_rect.c b/src/gui/src/rounded_rect.c
index cd2bbff..b60a05a 100644
--- a/src/gui/src/rounded_rect.c
+++ b/src/gui/src/rounded_rect.c
@@ -222,11 +222,69 @@ static void get_dim(const struct gui_common *const g, short *const w,
*h = r->h;
}
+static void add_child(struct gui_common *const parent,
+ struct gui_common *const child)
+{
+ struct gui_rounded_rect *const r = (struct gui_rounded_rect *)parent;
+
+ if (!r->adjust || child->hidden || !child->cb || !child->cb->get_dim)
+ return;
+
+ short w, h;
+ const short ref_w = refs[GUI_ROUNDED_RECT_MID_VERT].w,
+ ref_h = refs[GUI_ROUNDED_RECT_MID].h,
+ min_w = ref_w * 2 + refs[GUI_ROUNDED_RECT_MID].w,
+ min_h = ref_h * 2 + refs[GUI_ROUNDED_RECT_MID_VERT].h;
+
+ child->cb->get_dim(child, &w, &h);
+
+ if (w > r->w - (ref_w * 2))
+ r->w = w + (ref_w * 2);
+
+ if (h > r->h - (ref_h * 2))
+ r->h = h + (ref_h * 2);
+
+ if (r->w < min_w)
+ r->w = min_w;
+
+ if (r->h < min_h)
+ r->h = min_h;
+
+ if (!child->hcentered)
+ child->xoff = ref_w;
+
+ if (!child->vcentered)
+ child->yoff = ref_h;
+}
+
+static int update(struct gui_common *const g,
+ const union peripheral *const p, const struct camera *const cam,
+ struct input *const in)
+{
+ struct gui_rounded_rect *const r = (struct gui_rounded_rect *)g;
+
+ if (r->adjust)
+ {
+ r->w = r->h = 0;
+
+ struct gui_common *const child = r->common.child;
+
+ if (child && child->cb && child->cb->get_dim)
+ add_child(&r->common, child);
+
+ for (struct gui_common *s = child->sibling; s; s = s->sibling)
+ add_child(&r->common, s);
+ }
+
+ return 0;
+}
+
void gui_rounded_rect_init(struct gui_rounded_rect *const r)
{
static const struct gui_common_cb cb =
{
.get_dim = get_dim,
+ .update = update,
.render = render
};
diff --git a/src/input/inc/input.h b/src/input/inc/input.h
index 2dd401b..5780000 100644
--- a/src/input/inc/input.h
+++ b/src/input/inc/input.h
@@ -22,7 +22,7 @@ struct input
void *user;
unsigned char t;
bool repeat;
- struct keyboard_combo prev;
+ struct keyboard_input prev;
};
void input_update(struct input *in, const union peripheral *p);
diff --git a/src/input/src/input.c b/src/input/src/input.c
index 26116c8..ee6d4b7 100644
--- a/src/input/src/input.c
+++ b/src/input/src/input.c
@@ -8,8 +8,7 @@
#include <stddef.h>
#include <string.h>
-static void send_input(struct input *const in, const struct keyboard *const k,
- const enum keyboard_key key)
+static void send_key(struct input *const in, const enum keyboard_key key)
{
if (key != KEYBOARD_KEY_NONE)
{
@@ -24,28 +23,44 @@ static void send_input(struct input *const in, const struct keyboard *const k,
break;
default:
- {
- const char ch = keyboard_to_char(k, key);
-
- if (isprint(ch))
- in->cb(ch, in->user);
- }
break;
}
}
}
+static void send_input(struct input *const in,
+ const struct keyboard_input *const ki)
+{
+ {
+ const struct keyboard_combo *const c = &ki->combo;
+
+ for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++)
+ send_key(in, c->keys[i]);
+ }
+
+ {
+ const struct keyboard_chars *const c = &ki->chars;
+
+ for (size_t i = 0; i < sizeof c->c / sizeof *c->c; i++)
+ {
+ const char ch = c->c[i];
+
+ if (ch)
+ in->cb(ch, in->user);
+ }
+ }
+}
+
static void update_keyboard(struct input *const in,
const struct keyboard *const k)
{
- struct keyboard_combo c;
+ struct keyboard_input ki;
- if (keyboard_any_justpressed(k, &c))
- for (size_t i = 0; i < sizeof c.keys / sizeof *c.keys; i++)
- send_input(in, k, c.keys[i]);
- else if (keyboard_any_pressed(k, &c))
+ if (keyboard_any_justpressed(k, &ki))
+ send_input(in, &ki);
+ else if (keyboard_any_pressed(k, &ki))
{
- if (memcmp(&c, &in->prev, sizeof c))
+ if (memcmp(&ki, &in->prev, sizeof ki))
{
in->repeat = false;
in->t = 0;
@@ -61,15 +76,12 @@ static void update_keyboard(struct input *const in,
}
else if (++in->t >= SHORT_INTERVAL)
{
- for (size_t i = 0;
- i < sizeof c.keys / sizeof *c.keys; i++)
- send_input(in, k, c.keys[i]);
-
+ send_input(in, &ki);
in->t = 0;
}
}
- in->prev = c;
+ in->prev = ki;
}
else
{
diff --git a/src/keyboard/CMakeLists.txt b/src/keyboard/CMakeLists.txt
index 5fa8892..2cdf85c 100644
--- a/src/keyboard/CMakeLists.txt
+++ b/src/keyboard/CMakeLists.txt
@@ -3,9 +3,11 @@ set(inc "inc")
if(PS1_BUILD)
set(src ${src} "ps1/src/keyboard.c")
+ set(inc ${inc} "ps1/inc")
set(privdeps ${privdeps} PSXSDK::PSXSDK)
elseif(SDL1_2_BUILD)
set(src ${src} "sdl-1.2/src/keyboard.c")
+ set(inc ${inc} "sdl-1.2/inc")
set(privdeps ${privdeps} SDL::SDL)
endif()
diff --git a/src/keyboard/inc/keyboard.h b/src/keyboard/inc/keyboard.h
index 0fb0179..8bae4d3 100644
--- a/src/keyboard/inc/keyboard.h
+++ b/src/keyboard/inc/keyboard.h
@@ -1,7 +1,9 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
-#include <keyboard_key.h>
+#include <keyboard/key.h>
+#include <keyboard/combo.h>
+#include <keyboard/port.h>
#include <stdbool.h>
#include <stddef.h>
@@ -12,26 +14,33 @@ extern "C"
#define KEYBOARD_COMBO(...) (const struct keyboard_combo){.keys = {__VA_ARGS__}}
-enum {KEYBOARD_MAX_COMBO_KEYS = 3};
-
struct keyboard
{
- struct keyboard_combo
+ struct keyboard_input
{
- enum keyboard_key keys[KEYBOARD_MAX_COMBO_KEYS];
- } combo, oldcombo;
+ struct keyboard_combo
+ {
+ enum keyboard_key keys[KEYBOARD_MAX_COMBO_KEYS];
+ } combo;
+
+ struct keyboard_chars
+ {
+ char c[KEYBOARD_MAX_COMBO_KEYS];
+ } chars;
+ } input, oldinput;
size_t i;
+ struct keyboard_port port;
};
void keyboard_init(struct keyboard *k);
void keyboard_update(struct keyboard *k);
+bool keyboard_available(void);
bool keyboard_justpressed(const struct keyboard *k, const struct keyboard_combo *c);
bool keyboard_pressed(const struct keyboard *k, const struct keyboard_combo *c);
bool keyboard_justreleased(const struct keyboard *k, const struct keyboard_combo *c);
-bool keyboard_any_justpressed(const struct keyboard *k, struct keyboard_combo *c);
-bool keyboard_any_pressed(const struct keyboard *k, struct keyboard_combo *c);
-char keyboard_to_char(const struct keyboard *k, enum keyboard_key key);
+bool keyboard_any_justpressed(const struct keyboard *k, struct keyboard_input *in);
+bool keyboard_any_pressed(const struct keyboard *k, struct keyboard_input *in);
const char *keyboard_key_str(enum keyboard_key k);
#ifdef __cplusplus
diff --git a/src/keyboard/inc/keyboard/combo.h b/src/keyboard/inc/keyboard/combo.h
new file mode 100644
index 0000000..e0ef759
--- /dev/null
+++ b/src/keyboard/inc/keyboard/combo.h
@@ -0,0 +1,15 @@
+#ifndef KEYBOARD_COMBO_H
+#define KEYBOARD_COMBO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum {KEYBOARD_MAX_COMBO_KEYS = 3};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KEYBOARD_COMBO_H */
diff --git a/src/keyboard/inc/keyboard_key.h b/src/keyboard/inc/keyboard/key.h
index 49c31d2..0a25a97 100644
--- a/src/keyboard/inc/keyboard_key.h
+++ b/src/keyboard/inc/keyboard/key.h
@@ -59,6 +59,7 @@ extern "C"
X(KEYBOARD_KEY_SPACE) \
X(KEYBOARD_KEY_MINUS) \
X(KEYBOARD_KEY_DOT) \
+ X(KEYBOARD_KEY_RETURN) \
X(KEYBOARD_KEY_SLASH)
enum keyboard_key
diff --git a/src/keyboard/ps1/inc/keyboard/port.h b/src/keyboard/ps1/inc/keyboard/port.h
new file mode 100644
index 0000000..d0d5ffd
--- /dev/null
+++ b/src/keyboard/ps1/inc/keyboard/port.h
@@ -0,0 +1,18 @@
+#ifndef KEYBOARD_PS1_H
+#define KEYBOARD_PS1_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct keyboard_port
+{
+ int dummy;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KEYBOARD_PS1_H */
diff --git a/src/keyboard/ps1/src/keyboard.c b/src/keyboard/ps1/src/keyboard.c
index 69e7a90..ea31cd3 100644
--- a/src/keyboard/ps1/src/keyboard.c
+++ b/src/keyboard/ps1/src/keyboard.c
@@ -3,3 +3,8 @@
void keyboard_update(struct keyboard *const k)
{
}
+
+bool keyboard_available(void)
+{
+ return false;
+}
diff --git a/src/keyboard/sdl-1.2/inc/keyboard/port.h b/src/keyboard/sdl-1.2/inc/keyboard/port.h
new file mode 100644
index 0000000..69628f1
--- /dev/null
+++ b/src/keyboard/sdl-1.2/inc/keyboard/port.h
@@ -0,0 +1,21 @@
+#ifndef KEYBOARD_SDL_1_2_H
+#define KEYBOARD_SDL_1_2_H
+
+#include <keyboard.h>
+#include <SDL_keyboard.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct keyboard_port
+{
+ Uint8 scancodes[KEYBOARD_MAX_COMBO_KEYS];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KEYBOARD_SDL_1_2_H */
diff --git a/src/keyboard/sdl-1.2/src/keyboard.c b/src/keyboard/sdl-1.2/src/keyboard.c
index 78056e1..87ae808 100644
--- a/src/keyboard/sdl-1.2/src/keyboard.c
+++ b/src/keyboard/sdl-1.2/src/keyboard.c
@@ -1,38 +1,67 @@
#include <keyboard.h>
-#include <keyboard_key.h>
+#include <keyboard/port.h>
+#include <keyboard/key.h>
#include <SDL.h>
+#include <ctype.h>
+#include <stddef.h>
#include <stdio.h>
-static void append_key(const enum keyboard_key key, struct keyboard *const k)
+static void append_key(const enum keyboard_key key, const char ch,
+ const Uint8 scancode, struct keyboard *const k)
{
- struct keyboard_combo *const c = &k->combo;
+ struct keyboard_combo *const c = &k->input.combo;
for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++)
{
enum keyboard_key *const sel = &c->keys[i];
+ char *const pch = &k->input.chars.c[i];
+ Uint8 *const sc = &k->port.scancodes[i];
- if (*sel == KEYBOARD_KEY_NONE)
+ if (*sel == KEYBOARD_KEY_NONE && !*pch)
{
*sel = key;
+ *pch = ch;
+ *sc = scancode;
break;
}
}
}
-static void remove_key(const enum keyboard_key key, struct keyboard *const k)
+static enum keyboard_key char_to_key(const char ch)
{
- struct keyboard_combo *const c = &k->combo;
-
- for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++)
+ if (ch >= 'a' && ch <= 'b')
+ return KEYBOARD_KEY_A + (ch - 'a');
+ else if (ch >= 'A' && ch <= 'Z')
+ return KEYBOARD_KEY_A + (ch - 'A');
+ else if (ch >= '0' && ch <= '9')
+ return KEYBOARD_KEY_0 + (ch - '0');
+ else
{
- enum keyboard_key *const sel = &c->keys[i];
+ static const struct map
+ {
+ enum keyboard_key key;
+ char ch;
+ } map[] =
+ {
+ {.key = KEYBOARD_KEY_DOT, .ch = '.'},
+ {.key = KEYBOARD_KEY_SPACE, .ch = ' '},
+ {.key = KEYBOARD_KEY_MINUS, .ch = '-'},
+ {.key = KEYBOARD_KEY_SLASH, .ch = '/'}
+ };
- if (*sel == key)
- *sel = KEYBOARD_KEY_NONE;
+ for (size_t i = 0; i < sizeof map / sizeof *map; i++)
+ {
+ const struct map *const m = &map[i];
+
+ if (ch == m->ch)
+ return m->key;
+ }
}
+
+ return KEYBOARD_KEY_NONE;
}
-static void key_event(const SDL_KeyboardEvent *const ev,
+static void on_pressed(const SDL_KeyboardEvent *const ev,
struct keyboard *const k)
{
static const struct keymap
@@ -46,86 +75,79 @@ static void key_event(const SDL_KeyboardEvent *const ev,
{.key = KEYBOARD_KEY_RIGHT, .sdl_key = SDLK_RIGHT},
{.key = KEYBOARD_KEY_UP, .sdl_key = SDLK_UP},
{.key = KEYBOARD_KEY_DOWN, .sdl_key = SDLK_DOWN},
+ {.key = KEYBOARD_KEY_ESC, .sdl_key = SDLK_ESCAPE},
+ {.key = KEYBOARD_KEY_F11, .sdl_key = SDLK_F11},
{.key = KEYBOARD_KEY_LCTRL, .sdl_key = SDLK_LCTRL},
{.key = KEYBOARD_KEY_RCTRL, .sdl_key = SDLK_RCTRL},
{.key = KEYBOARD_KEY_LSHIFT, .sdl_key = SDLK_LSHIFT},
{.key = KEYBOARD_KEY_RSHIFT, .sdl_key = SDLK_RSHIFT},
- {.key = KEYBOARD_KEY_ESC, .sdl_key = SDLK_ESCAPE},
- {.key = KEYBOARD_KEY_F11, .sdl_key = SDLK_F11},
- {.key = KEYBOARD_KEY_SPACE, .sdl_key = SDLK_SPACE},
- {.key = KEYBOARD_KEY_MINUS, .sdl_key = SDLK_MINUS},
- {.key = KEYBOARD_KEY_SLASH, .sdl_key = SDLK_SLASH},
- {.key = KEYBOARD_KEY_SLASH, .sdl_key = SDLK_KP_DIVIDE},
- {.key = KEYBOARD_KEY_A, .sdl_key = SDLK_a},
- {.key = KEYBOARD_KEY_B, .sdl_key = SDLK_b},
- {.key = KEYBOARD_KEY_C, .sdl_key = SDLK_c},
- {.key = KEYBOARD_KEY_D, .sdl_key = SDLK_d},
- {.key = KEYBOARD_KEY_E, .sdl_key = SDLK_e},
- {.key = KEYBOARD_KEY_F, .sdl_key = SDLK_f},
- {.key = KEYBOARD_KEY_G, .sdl_key = SDLK_g},
- {.key = KEYBOARD_KEY_H, .sdl_key = SDLK_h},
- {.key = KEYBOARD_KEY_I, .sdl_key = SDLK_i},
- {.key = KEYBOARD_KEY_J, .sdl_key = SDLK_j},
- {.key = KEYBOARD_KEY_K, .sdl_key = SDLK_k},
- {.key = KEYBOARD_KEY_L, .sdl_key = SDLK_l},
- {.key = KEYBOARD_KEY_M, .sdl_key = SDLK_m},
- {.key = KEYBOARD_KEY_N, .sdl_key = SDLK_n},
- {.key = KEYBOARD_KEY_O, .sdl_key = SDLK_o},
- {.key = KEYBOARD_KEY_P, .sdl_key = SDLK_p},
- {.key = KEYBOARD_KEY_Q, .sdl_key = SDLK_q},
- {.key = KEYBOARD_KEY_R, .sdl_key = SDLK_r},
- {.key = KEYBOARD_KEY_S, .sdl_key = SDLK_s},
- {.key = KEYBOARD_KEY_T, .sdl_key = SDLK_t},
- {.key = KEYBOARD_KEY_U, .sdl_key = SDLK_u},
- {.key = KEYBOARD_KEY_V, .sdl_key = SDLK_v},
- {.key = KEYBOARD_KEY_W, .sdl_key = SDLK_w},
- {.key = KEYBOARD_KEY_X, .sdl_key = SDLK_x},
- {.key = KEYBOARD_KEY_Y, .sdl_key = SDLK_y},
- {.key = KEYBOARD_KEY_Z, .sdl_key = SDLK_z},
- {.key = KEYBOARD_KEY_0, .sdl_key = SDLK_0},
- {.key = KEYBOARD_KEY_1, .sdl_key = SDLK_1},
- {.key = KEYBOARD_KEY_2, .sdl_key = SDLK_2},
- {.key = KEYBOARD_KEY_3, .sdl_key = SDLK_3},
- {.key = KEYBOARD_KEY_4, .sdl_key = SDLK_4},
- {.key = KEYBOARD_KEY_5, .sdl_key = SDLK_5},
- {.key = KEYBOARD_KEY_6, .sdl_key = SDLK_6},
- {.key = KEYBOARD_KEY_7, .sdl_key = SDLK_7},
- {.key = KEYBOARD_KEY_8, .sdl_key = SDLK_8},
- {.key = KEYBOARD_KEY_9, .sdl_key = SDLK_9},
- {.key = KEYBOARD_KEY_0, .sdl_key = SDLK_KP0},
- {.key = KEYBOARD_KEY_1, .sdl_key = SDLK_KP1},
- {.key = KEYBOARD_KEY_2, .sdl_key = SDLK_KP2},
- {.key = KEYBOARD_KEY_3, .sdl_key = SDLK_KP3},
- {.key = KEYBOARD_KEY_4, .sdl_key = SDLK_KP4},
- {.key = KEYBOARD_KEY_5, .sdl_key = SDLK_KP5},
- {.key = KEYBOARD_KEY_6, .sdl_key = SDLK_KP6},
- {.key = KEYBOARD_KEY_7, .sdl_key = SDLK_KP7},
- {.key = KEYBOARD_KEY_8, .sdl_key = SDLK_KP8},
- {.key = KEYBOARD_KEY_9, .sdl_key = SDLK_KP9},
- {.key = KEYBOARD_KEY_DOT, .sdl_key = SDLK_PERIOD},
- {.key = KEYBOARD_KEY_DOT, .sdl_key = SDLK_KP_PERIOD}
+ {.key = KEYBOARD_KEY_RETURN, .sdl_key = SDLK_KP_ENTER},
+ {.key = KEYBOARD_KEY_RETURN, .sdl_key = SDLK_RETURN}
};
- for (size_t i = 0; i < sizeof keymap / sizeof *keymap; i++)
+ const char ch = ev->keysym.unicode;
+
+ /* According to the man page for isprint(3):
+ * The standards require that the argument c for these functions is
+ * either EOF or a value that is representable in the type unsigned
+ * char. If the argument is of type char, it must be cast to
+ * unsigned char. */
+ if (isprint((unsigned char)ch))
+ append_key(char_to_key(ch), ch, ev->keysym.scancode, k);
+ else
{
- const struct keymap *const km = &keymap[i];
+ for (size_t i = 0; i < sizeof keymap / sizeof *keymap; i++)
+ {
+ const struct keymap *const km = &keymap[i];
- if (ev->keysym.sym == km->sdl_key)
+ if (ev->keysym.sym == km->sdl_key)
+ {
+ append_key(km->key, '\0', ev->keysym.scancode, k);
+ break;
+ }
+ }
+ }
+}
+
+static void remove_key(const Uint8 scancode, struct keyboard *const k)
+{
+ struct keyboard_port *const p = &k->port;
+
+ for (size_t i = 0; i < sizeof p->scancodes / sizeof *p->scancodes; i++)
+ {
+ Uint8 *const sc = &p->scancodes[i];
+
+ if (*sc == scancode)
{
- if (ev->state == SDL_PRESSED)
- append_key(km->key, k);
- else
- remove_key(km->key, k);
+ struct keyboard_combo *const c = &k->input.combo;
+ struct keyboard_chars *const ch = &k->input.chars;
+
+ c->keys[i] = KEYBOARD_KEY_NONE;
+ ch->c[i] = '\0';
+ *sc = 0;
+ break;
}
}
}
+static void key_event(const SDL_KeyboardEvent *const ev,
+ struct keyboard *const k)
+{
+ if (ev->state == SDL_PRESSED)
+ on_pressed(ev, k);
+ else
+ /* SDL allows reading Unicode only for pressed events.
+ * In order to know whether a Unicode was released, its
+ * corresponding scancode must be inspected. */
+ remove_key(ev->keysym.scancode, k);
+}
+
void keyboard_update(struct keyboard *const k)
{
SDL_Event ev;
int n;
- k->oldcombo = k->combo;
+ k->oldinput = k->input;
while ((n = SDL_PeepEvents(&ev, 1, SDL_GETEVENT,
SDL_KEYEVENTMASK | SDL_QUITMASK)) > 0)
@@ -139,7 +161,7 @@ void keyboard_update(struct keyboard *const k)
break;
case SDL_QUIT:
- append_key(KEYBOARD_KEY_EXIT, k);
+ append_key(KEYBOARD_KEY_EXIT, '\0', 0, k);
break;
default:
@@ -150,9 +172,11 @@ void keyboard_update(struct keyboard *const k)
}
if (n < 0)
- {
- fprintf(stderr, "%s: SDL_PeepEvents: %s\n",
- __func__, SDL_GetError());
- return;
- }
+ fprintf(stderr, "%s: SDL_PeepEvents: %s\n", __func__, SDL_GetError());
+}
+
+bool keyboard_available(void)
+{
+ /* Keyboard availability is always assumed in SDL. */
+ return true;
}
diff --git a/src/keyboard/src/keyboard.c b/src/keyboard/src/keyboard.c
index 3d6f496..21d6898 100644
--- a/src/keyboard/src/keyboard.c
+++ b/src/keyboard/src/keyboard.c
@@ -1,24 +1,49 @@
#include <keyboard.h>
-#include <keyboard_key.h>
+#include <keyboard/key.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
-bool keyboard_any_justpressed(const struct keyboard *const k,
+static bool chars_justpressed(const struct keyboard *const k,
+ struct keyboard_chars *const c)
+{
+ bool ret = false;
+ const struct keyboard_chars *const ref = &k->input.chars;
+
+ for (size_t i = 0; i < sizeof c->c / sizeof *c->c; i++)
+ c->c[i] = '\0';
+
+ for (size_t i = 0, j = 0; i < sizeof ref->c / sizeof *ref->c; i++)
+ {
+ const char ch = ref->c[i];
+
+ if (ch && k->oldinput.chars.c[i] != ch)
+ {
+ c->c[j++] = ch;
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+static bool keys_justpressed(const struct keyboard *const k,
struct keyboard_combo *const c)
{
bool ret = false;
- for (size_t i = 0; i < sizeof c->keys / sizeof c->keys; i++)
+ for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++)
c->keys[i] = KEYBOARD_KEY_NONE;
+ const struct keyboard_combo *const ref = &k->input.combo;
+
for (size_t i = 0, j = 0;
- i < sizeof k->combo.keys / sizeof *k->combo.keys; i++)
+ i < sizeof ref->keys / sizeof *ref->keys; i++)
{
- const enum keyboard_key key = k->combo.keys[i];
+ const enum keyboard_key key = ref->keys[i];
if (key != KEYBOARD_KEY_NONE
- && k->oldcombo.keys[i] == KEYBOARD_KEY_NONE)
+ && k->oldinput.combo.keys[i] != key)
{
c->keys[j++] = key;
ret = true;
@@ -28,16 +53,24 @@ bool keyboard_any_justpressed(const struct keyboard *const k,
return ret;
}
-bool keyboard_any_pressed(const struct keyboard *const k,
+bool keyboard_any_justpressed(const struct keyboard *const k,
+ struct keyboard_input *const in)
+{
+ /* Use bitwise OR since both functions must be always executed. */
+ return keys_justpressed(k, &in->combo)
+ | chars_justpressed(k, &in->chars);
+}
+
+static bool keys_pressed(const struct keyboard *const k,
struct keyboard_combo *const c)
{
- for (size_t i = 0; i < sizeof k->combo.keys / sizeof *k->combo.keys; i++)
- {
- const enum keyboard_key key = k->combo.keys[i];
+ const struct keyboard_combo *const ref = &k->input.combo;
- if (key != KEYBOARD_KEY_NONE)
+ for (size_t i = 0; i < sizeof ref->keys / sizeof *ref->keys; i++)
+ {
+ if (ref->keys[i] != KEYBOARD_KEY_NONE)
{
- *c = k->combo;
+ *c = *ref;
return true;
}
}
@@ -45,43 +78,28 @@ bool keyboard_any_pressed(const struct keyboard *const k,
return false;
}
-char keyboard_to_char(const struct keyboard *const k,
- const enum keyboard_key key)
+static bool chars_pressed(const struct keyboard *const k,
+ struct keyboard_chars *const ch)
{
- if (key >= KEYBOARD_KEY_0 && key <= KEYBOARD_KEY_9)
- return '0' + key - KEYBOARD_KEY_0;
- else if (key >= KEYBOARD_KEY_A && key <= KEYBOARD_KEY_Z)
- {
- if (keyboard_pressed(k, &KEYBOARD_COMBO(KEYBOARD_KEY_LSHIFT))
- || keyboard_pressed(k, &KEYBOARD_COMBO(KEYBOARD_KEY_RSHIFT)))
- return 'A' + key - KEYBOARD_KEY_A;
- else
- return 'a' + key - KEYBOARD_KEY_A;
- }
- else
- {
- static const struct map
- {
- enum keyboard_key key;
- char ch;
- } map[] =
- {
- {.key = KEYBOARD_KEY_DOT, .ch = '.'},
- {.key = KEYBOARD_KEY_SPACE, .ch = ' '},
- {.key = KEYBOARD_KEY_MINUS, .ch = '-'},
- {.key = KEYBOARD_KEY_SLASH, .ch = '/'}
- };
+ const struct keyboard_chars *const ref = &k->input.chars;
- for (size_t i = 0; i < sizeof map / sizeof *map; i++)
+ for (size_t i = 0; i < sizeof ref->c / sizeof *ref->c; i++)
+ {
+ if (ref->c[i])
{
- const struct map *const m = &map[i];
-
- if (key == m->key)
- return m->ch;
+ *ch = *ref;
+ return true;
}
}
- return -1;
+ return false;
+}
+
+bool keyboard_any_pressed(const struct keyboard *const k,
+ struct keyboard_input *const in)
+{
+ /* Use bitwise OR since both functions must be always executed. */
+ return keys_pressed(k, &in->combo) | chars_pressed(k, &in->chars);
}
static bool combo_pressed(const struct keyboard_combo *const ref,
@@ -117,19 +135,21 @@ static bool combo_pressed(const struct keyboard_combo *const ref,
bool keyboard_pressed(const struct keyboard *const k,
const struct keyboard_combo *const c)
{
- return combo_pressed(&k->combo, c);
+ return combo_pressed(&k->input.combo, c);
}
bool keyboard_justreleased(const struct keyboard *const k,
const struct keyboard_combo *const c)
{
- return !combo_pressed(&k->combo, c) && combo_pressed(&k->oldcombo, c);
+ return !combo_pressed(&k->input.combo, c)
+ && combo_pressed(&k->oldinput.combo, c);
}
bool keyboard_justpressed(const struct keyboard *const k,
const struct keyboard_combo *const c)
{
- return combo_pressed(&k->combo, c) && !combo_pressed(&k->oldcombo, c);
+ return combo_pressed(&k->input.combo, c)
+ && !combo_pressed(&k->oldinput.combo, c);
}
void keyboard_init(struct keyboard *const k)
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;
}
diff --git a/src/mouse/inc/mouse.h b/src/mouse/inc/mouse.h
index 60b63d3..55d0227 100644
--- a/src/mouse/inc/mouse.h
+++ b/src/mouse/inc/mouse.h
@@ -22,6 +22,7 @@ struct mouse
void mouse_init(struct mouse *m);
void mouse_update(struct mouse *m);
+bool mouse_available(void);
bool mouse_pressed(const struct mouse *m, enum mouse_button b);
bool mouse_justpressed(const struct mouse *m, enum mouse_button b);
bool mouse_justreleased(const struct mouse *m, enum mouse_button b);
diff --git a/src/mouse/ps1/src/mouse.c b/src/mouse/ps1/src/mouse.c
index 78ea425..865d4c1 100644
--- a/src/mouse/ps1/src/mouse.c
+++ b/src/mouse/ps1/src/mouse.c
@@ -1,9 +1,11 @@
#include <mouse.h>
+#include <stdbool.h>
void mouse_update(struct mouse *const m)
{
}
-void mouse_init(struct mouse *const m)
+bool mouse_available(void)
{
+ return false;
}
diff --git a/src/mouse/sdl-1.2/src/mouse.c b/src/mouse/sdl-1.2/src/mouse.c
index 56b22f3..35fc030 100644
--- a/src/mouse/sdl-1.2/src/mouse.c
+++ b/src/mouse/sdl-1.2/src/mouse.c
@@ -85,7 +85,8 @@ end:
}
}
-void mouse_init(struct mouse *const m)
+bool mouse_available(void)
{
- *m = (const struct mouse){0};
+ /* Mouse availability is always assumed in SDL. */
+ return true;
}
diff --git a/src/mouse/src/mouse.c b/src/mouse/src/mouse.c
index bec978d..b0fab77 100644
--- a/src/mouse/src/mouse.c
+++ b/src/mouse/src/mouse.c
@@ -17,3 +17,8 @@ bool mouse_justreleased(const struct mouse *const m,
{
return !(m->mask & (1 << b)) && m->oldmask & (1 << b);
}
+
+void mouse_init(struct mouse *const m)
+{
+ *m = (const struct mouse){0};
+}
diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt
index 79bd653..03329c9 100644
--- a/src/net/CMakeLists.txt
+++ b/src/net/CMakeLists.txt
@@ -1,4 +1,6 @@
-set(src "src/common.c")
+set(src "src/common.c" "src/serial.c")
+set(priv_deps transport)
+set(privinc "privinc")
if(PS1_BUILD)
set(src ${src}
@@ -24,7 +26,6 @@ else()
endif()
set(priv_deps ${priv_deps} util ENET)
- set(privinc ${privinc} "privinc")
endif()
add_library(net ${src})
diff --git a/src/net/inc/net.h b/src/net/inc/net.h
index bbbd4ce..47bd0f8 100644
--- a/src/net/inc/net.h
+++ b/src/net/inc/net.h
@@ -3,7 +3,6 @@
#include <stdbool.h>
#include <stddef.h>
-#include <stdint.h>
#ifdef __cplusplus
extern "C"
@@ -16,39 +15,54 @@ enum net_domain
NET_DOMAIN_SERIAL
};
+enum net_role
+{
+ NET_ROLE_SERVER,
+ NET_ROLE_CLIENT
+};
+
+typedef void *net_peer;
+
+struct net_event
+{
+ void (*connected)(net_peer p, void *arg);
+ void (*disconnected)(net_peer p, void *arg);
+ void (*received)(net_peer p, const void *buf, size_t n, void *arg);
+ void *arg;
+};
+
union net_connect
{
struct net_connect_common
{
enum net_domain domain;
-
- struct net_connect_ev
- {
- void (*connected)(void *arg);
- void (*disconnected)(void *arg);
- void *arg;
- } ev;
+ struct net_event ev;
} common;
struct net_connect_ipv4
{
struct net_connect_common common;
const char *addr;
- uint16_t port;
+ short port;
} ipv4;
struct net_connect_serial
{
struct net_connect_common common;
- const char *dev;
- unsigned long baud;
- enum
+ struct net_serial_cfg
{
- NET_PARITY_NONE,
- NET_PARITY_ODD,
- NET_PARITY_EVEN
- } parity;
+ const char *dev;
+ unsigned long baud;
+ bool hw_ctrl;
+
+ enum
+ {
+ NET_PARITY_NONE,
+ NET_PARITY_ODD,
+ NET_PARITY_EVEN
+ } parity;
+ } cfg;
} serial;
};
@@ -58,31 +72,34 @@ union net_server
{
enum net_domain domain;
unsigned max_players;
+ struct net_event ev;
} common;
struct net_server_ipv4
{
struct net_server_common common;
- uint16_t port;
+ short port;
} ipv4;
struct net_server_serial
{
struct net_server_common common;
+ struct net_serial_cfg cfg;
} serial;
};
-struct net_socket;
+struct net_host;
int net_init(void);
void net_deinit(void);
-int net_update(struct net_socket *s);
+int net_update(struct net_host *s);
bool net_available(enum net_domain d);
-struct net_socket *net_server(const union net_server *srv);
-struct net_socket *net_connect(const union net_connect *c);
-int net_read(struct net_socket *s, void *buf, size_t n);
-int net_write(struct net_socket *s, const void *buf, size_t n);
-int net_close(struct net_socket *s);
+struct net_host *net_server(const union net_server *srv);
+struct net_host *net_connect(const union net_connect *c);
+int net_write(struct net_host *h, net_peer p, const void *buf, size_t n);
+int net_set_event(struct net_host *h, const struct net_event *ev);
+int net_close(struct net_host *s);
+enum net_role net_role(const struct net_host *s);
const char *net_domain_str(enum net_domain d);
#ifdef __cplusplus
diff --git a/src/net/inc/net/serial.h b/src/net/inc/net/serial.h
index ee81e9f..06cbc95 100644
--- a/src/net/inc/net/serial.h
+++ b/src/net/inc/net/serial.h
@@ -1,14 +1,14 @@
#ifndef NET_SERIAL_H
#define NET_SERIAL_H
-#include <stddef.h>
+#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
-const char *const *net_serial_devices(size_t *n);
+bool net_serial_unique(void);
#ifdef __cplusplus
}
diff --git a/src/net/posix/src/serial.c b/src/net/posix/src/serial.c
index b0357b9..852fa0b 100644
--- a/src/net/posix/src/serial.c
+++ b/src/net/posix/src/serial.c
@@ -1,40 +1,290 @@
#include <net.h>
#include <net/serial.h>
#include <net_private.h>
+#include <transport.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-int net_read_serial(struct net_socket_domain *const h, void *const buf,
- const size_t n)
+/* Based on https://en.wikibooks.org/wiki/Serial_Programming/termios */
+
+struct net_host_domain
{
- return -1;
+ int fd;
+ enum net_role role;
+ struct transport_handle h;
+ struct net_event ev;
+};
+
+int net_write_serial(struct net_host_domain *const h, const net_peer p,
+ const void *const buf, const size_t n)
+{
+ return transport_send(&h->h, buf, n);
}
-int net_write_serial(struct net_socket_domain *const h, const void *const buf,
- const size_t n)
+int net_close_serial(struct net_host_domain *const h)
{
- return -1;
+ if (h && h->fd >= 0)
+ close(h->fd);
+
+ free(h);
+ return 0;
}
-int net_close_serial(struct net_socket_domain *const h)
+int net_update_serial(struct net_host_domain *const h)
{
- return -1;
+ return transport_update(&h->h);
+}
+
+static void on_received(const struct transport_event *const ev,
+ void *const arg)
+{
+ struct net_host_domain *const h = arg;
+
+ net_serial_on_received(ev, &h->ev);
+}
+
+static int on_read(void *const buf, const size_t n, void *const arg)
+{
+ struct net_host_domain *const h = arg;
+ const ssize_t res = read(h->fd, buf, n);
+
+ if (res < 0)
+ switch (errno)
+ {
+ case EAGAIN:
+ /* Fall through. */
+ case EINTR:
+ return 0;
+
+ default:
+ return -1;
+ }
+
+ return res;
+}
+
+static int on_write(const void *const buf, const size_t n, void *const arg)
+{
+ struct net_host_domain *const h = arg;
+ const ssize_t res = write(h->fd, buf, n);
+
+ if (res < 0)
+ switch (errno)
+ {
+ case EAGAIN:
+ /* Fall through. */
+ case EINTR:
+ return 0;
+
+ default:
+ return -1;
+ }
+
+ return res;
}
-int net_update_serial(struct net_socket_domain *const h)
+static struct transport_cfg get_transport_cfg(void *const arg)
{
+ return (const struct transport_cfg)
+ {
+ .arg = arg,
+ .received = on_received,
+ .read = on_read,
+ .write = on_write
+ };
+}
+
+static int open_dev(const char *const dev)
+{
+ return open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
+}
+
+static int get_speed(const unsigned long baud, speed_t *const out_s)
+{
+#define BAUD(x) {.baud = x, .s = B##x}
+
+ static const struct speed
+ {
+ unsigned long baud;
+ speed_t s;
+ } speeds[] =
+ {
+ BAUD(19200),
+ BAUD(38400),
+ BAUD(57600),
+ BAUD(115200)
+ };
+
+#undef BAUD
+
+ for (size_t i = 0; i < sizeof speeds / sizeof *speeds; i++)
+ {
+ const struct speed *const s = &speeds[i];
+
+ if (baud == s->baud)
+ {
+ *out_s = s->s;
+ return 0;
+ }
+ }
+
return -1;
}
-struct net_socket_domain *net_connect_serial(const union net_connect *const srv)
+static int config_fd(const int fd, const unsigned long baud)
+{
+ struct termios t;
+ int ret = -1;
+
+ if (tcgetattr(fd, &t))
+ {
+ fprintf(stderr, "%s: tcgetattr(3) failed: %s\n", __func__,
+ strerror(errno));
+ goto end;
+ }
+
+ /* cfmakeraw(3) is non-portable, but its man page states the
+ * following attributes are set. */
+ t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+ | INLCR | IGNCR | ICRNL | IXON);
+ t.c_oflag &= ~OPOST;
+ t.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ t.c_cflag &= ~(CSIZE | PARENB);
+ t.c_cflag |= CS8;
+
+ /* Make read(2) immediately regardless how many data is available. */
+ t.c_cc[VMIN] = 0;
+ t.c_cc[VTIME] = 0;
+
+ speed_t s;
+
+ if (get_speed(baud, &s))
+ {
+ fprintf(stderr, "%s: get_speed failed\n", __func__);
+ goto end;
+ }
+ else if (cfsetispeed(&t, s))
+ {
+ fprintf(stderr, "%s: cfsetispeed(3) failed: %s\n", __func__,
+ strerror(errno));
+ goto end;
+ }
+ else if (cfsetospeed(&t, s))
+ {
+ fprintf(stderr, "%s: cfsetospeed(3) failed: %s\n", __func__,
+ strerror(errno));
+ goto end;
+ }
+ else if (tcsetattr(fd, TCSANOW, &t))
+ {
+ fprintf(stderr, "%s: tcsetattr(3) failed: %s\n", __func__,
+ strerror(errno));
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+ return ret;
+}
+
+struct net_host_domain *net_connect_serial(const union net_connect *const c)
{
+ struct net_host_domain *const h = malloc(sizeof *h);
+ const struct net_serial_cfg *const cfg = &c->serial.cfg;
+
+ if (!h)
+ goto failure;
+
+ *h = (const struct net_host_domain)
+ {
+ .role = NET_ROLE_CLIENT,
+ .fd = open_dev(cfg->dev),
+ .h =
+ {
+ .cfg = get_transport_cfg(h)
+ }
+ };
+
+ if (h->fd < 0)
+ {
+ fprintf(stderr, "%s: open(2) failed: %s\n", __func__,
+ strerror(errno));
+ goto failure;
+ }
+ else if (config_fd(h->fd, cfg->baud))
+ {
+ fprintf(stderr, "%s: config_gf failed\n", __func__);
+ goto failure;
+ }
+ else if (transport_connect(&h->h))
+ {
+ fprintf(stderr, "%s: transport_connect failed\n", __func__);
+ goto failure;
+ }
+
+ return h;
+
+failure:
+ net_close_serial(h);
return NULL;
}
-struct net_socket_domain *net_server_serial(const union net_server *const srv)
+int net_set_event_serial(struct net_host_domain *const h,
+ const struct net_event *const ev)
+{
+ h->ev = *ev;
+ return 0;
+}
+
+struct net_host_domain *net_server_serial(const union net_server *const s)
{
+ struct net_host_domain *const h = malloc(sizeof *h);
+ const struct net_serial_cfg *const cfg = &s->serial.cfg;
+
+ if (!h)
+ goto failure;
+
+ *h = (const struct net_host_domain)
+ {
+ .role = NET_ROLE_SERVER,
+ .fd = open_dev(cfg->dev),
+ .h =
+ {
+ .cfg = get_transport_cfg(h)
+ }
+ };
+
+ if (h->fd < 0)
+ {
+ fprintf(stderr, "%s: open(2) failed: %s\n", __func__,
+ strerror(errno));
+ goto failure;
+ }
+ else if (config_fd(h->fd, cfg->baud))
+ {
+ fprintf(stderr, "%s: config_gf failed\n", __func__);
+ goto failure;
+ }
+
+ return h;
+
+failure:
+ net_close_serial(h);
return NULL;
}
+enum net_role net_role_serial(const struct net_host_domain *const h)
+{
+ return h->role;
+}
+
int net_init_serial(void)
{
return 0;
@@ -44,7 +294,7 @@ void net_deinit_serial(void)
{
}
-const char *const *net_serial_devices(size_t *const n)
+bool net_serial_unique(void)
{
- return NULL;
+ return false;
}
diff --git a/src/net/privinc/net_private.h b/src/net/privinc/net_private.h
index 53d7cd1..bc405d1 100644
--- a/src/net/privinc/net_private.h
+++ b/src/net/privinc/net_private.h
@@ -2,29 +2,35 @@
#define NET_PRIVATE_H
#include <net.h>
+#include <transport.h>
+#include <stddef.h>
-struct net_socket
+struct net_host
{
enum net_domain d;
- struct net_socket_domain *s;
+ struct net_host_domain *h;
};
int net_init_ipv4(void);
void net_deinit_ipv4(void);
-struct net_socket_domain *net_server_ipv4(const union net_server *srv);
-struct net_socket_domain *net_connect_ipv4(const union net_connect *c);
-int net_read_ipv4(struct net_socket_domain *h, void *buf, size_t n);
-int net_write_ipv4(struct net_socket_domain *h, const void *buf, size_t n);
-int net_close_ipv4(struct net_socket_domain *h);
-int net_update_ipv4(struct net_socket_domain *h);
+struct net_host_domain *net_server_ipv4(const union net_server *srv);
+struct net_host_domain *net_connect_ipv4(const union net_connect *c);
+int net_write_ipv4(struct net_host_domain *h, net_peer p, const void *buf, size_t n);
+int net_close_ipv4(struct net_host_domain *h);
+int net_update_ipv4(struct net_host_domain *h);
+enum net_role net_role_ipv4(const struct net_host_domain *h);
+int net_set_event_ipv4(struct net_host_domain *d, const struct net_event *ev);
int net_init_serial(void);
void net_deinit_serial(void);
-struct net_socket_domain *net_server_serial(const union net_server *srv);
-struct net_socket_domain *net_connect_serial(const union net_connect *c);
-int net_read_serial(struct net_socket_domain *h, void *buf, size_t n);
-int net_write_serial(struct net_socket_domain *h, const void *buf, size_t n);
-int net_close_serial(struct net_socket_domain *h);
-int net_update_serial(struct net_socket_domain *h);
+struct net_host_domain *net_server_serial(const union net_server *srv);
+struct net_host_domain *net_connect_serial(const union net_connect *c);
+int net_write_serial(struct net_host_domain *h, net_peer p, const void *buf, size_t n);
+int net_close_serial(struct net_host_domain *h);
+int net_update_serial(struct net_host_domain *h);
+enum net_role net_role_serial(const struct net_host_domain *h);
+int net_set_event_serial(struct net_host_domain *d, const struct net_event *ev);
+void net_serial_on_received(const struct transport_event *t_ev,
+ const struct net_event *n_ev);
#endif /* NET_PRIVATE_H */
diff --git a/src/net/privinc/net_serial_private.h b/src/net/privinc/net_serial_private.h
new file mode 100644
index 0000000..74eee55
--- /dev/null
+++ b/src/net/privinc/net_serial_private.h
@@ -0,0 +1,19 @@
+#ifndef NET_SERIAL_PRIVATE_H
+#define NET_SERIAL_PRIVATE_H
+
+#include <net.h>
+#include <transport.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void net_serial_on_received(const struct transport_event *t_ev,
+ const struct net_event *n_ev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_SERIAL_PRIVATE_H */
diff --git a/src/net/ps1/src/net.c b/src/net/ps1/src/net.c
index 8c037f3..af964cf 100644
--- a/src/net/ps1/src/net.c
+++ b/src/net/ps1/src/net.c
@@ -1,39 +1,233 @@
#include <net.h>
#include <net/serial.h>
-#include <errno.h>
+#include <net_serial_private.h>
+#include <transport.h>
+#include <psx.h>
+#include <psxsio.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdio.h>
-int net_read(struct net_socket *const h, void *const buf, const size_t n)
+static struct net_host
{
- return -1;
+ enum net_role role;
+ bool used, sending;
+ struct transport_handle h;
+ struct net_event ev;
+
+ struct net_host_fifo
+ {
+ char buf[32];
+ size_t pending, read;
+ } in, out;
+} host;
+
+enum {TX_INT_ENABLE = 1 << 10};
+
+int net_write(struct net_host *const h, const net_peer peer,
+ const void *const buf, size_t n)
+{
+ return transport_send(&h->h, buf, n);
}
-int net_write(struct net_socket *const h, const void *const buf, const size_t n)
+int net_close(struct net_host *const h)
{
- return -1;
+ SIOStop();
+ RemoveSIOHandler();
+ h->used = false;
+ return 0;
+}
+
+static void send_byte(struct net_host *const h)
+{
+ struct net_host_fifo *const f = &h->out;
+
+ if (f->pending != f->read)
+ {
+ if (SIOCheckOutBuffer())
+ {
+ size_t new = f->read + 1;
+
+ if (new >= sizeof f->buf)
+ new = 0;
+
+ h->sending = true;
+ SIOSendByte(f->buf[f->read = new]);
+ }
+ }
+ else
+ {
+ h->sending = false;
+ SIO_CTRL &= ~TX_INT_ENABLE;
+ }
}
-int net_close(struct net_socket *const h)
+int net_update(struct net_host *const h)
{
- return -1;
+ return transport_update(&h->h);
}
-int net_update(struct net_socket *const h)
+static void on_received(const struct transport_event *const ev,
+ void *const arg)
{
- return -1;
+ struct net_host *const h = arg;
+
+ net_serial_on_received(ev, &h->ev);
}
-struct net_socket *net_connect(const union net_connect *const c)
+static int on_write(const void *const buf, size_t n, void *const arg)
{
- return NULL;
+ const size_t orig = n;
+ struct net_host *const h = arg;
+
+ EnterCriticalSection();
+
+ for (const char *b = buf; n; n--, b++)
+ {
+ struct net_host_fifo *const f = &h->out;
+ size_t new = f->pending + 1;
+
+ if (new >= sizeof f->buf)
+ new = 0;
+
+ if (new == f->read)
+ {
+ fprintf(stderr, "%s: out FIFO full\n", __func__);
+ goto end;
+ }
+
+ f->buf[f->pending = new] = *b;
+ }
+
+ if (n != orig)
+ SIO_CTRL |= TX_INT_ENABLE;
+
+end:
+ ExitCriticalSection();
+ return orig - n;
+}
+
+static int on_read(void *const buf, const size_t n, void *const arg)
+{
+ size_t rem = n;
+ struct net_host *const h = arg;
+ struct net_host_fifo *const f = &h->in;
+
+ EnterCriticalSection();
+
+ for (char *b = buf; rem; rem--, b++)
+ {
+ if (f->read == f->pending)
+ goto end;
+
+ size_t new = f->read + 1;
+
+ if (new >= sizeof f->buf)
+ new = 0;
+
+ *b = f->buf[f->read = new];
+ }
+
+end:
+ ExitCriticalSection();
+ return n - rem;
+}
+
+static struct transport_cfg get_transport_cfg(void)
+{
+ return (const struct transport_cfg)
+ {
+ .arg = &host,
+ .received = on_received,
+ .read = on_read,
+ .write = on_write
+ };
}
-struct net_socket *net_server(const union net_server *const c)
+static void on_byte_received(struct net_host *const h, const char ch)
{
+ struct net_host_fifo *const f = &h->in;
+ size_t new = f->pending + 1;
+
+ if (new >= sizeof f->buf)
+ new = 0;
+
+ f->buf[f->pending = new] = ch;
+}
+
+static void sio(void)
+{
+ struct net_host *const h = &host;
+
+ if (SIOCheckInBuffer())
+ on_byte_received(h, SIOReadByte());
+
+ send_byte(h);
+}
+
+static void start_sio(const unsigned long baud)
+{
+ enum
+ {
+ RX_INT_MODE_1_BYTE = 1 << 8,
+ RX_INT_ENABLE = 1 << 11
+ };
+
+ SIOStartEx(baud, SIO_DATA_LEN_8, SIO_PARITY_NONE, SIO_STOP_BIT_1);
+ SetSIOHandler(sio);
+ SIO_CTRL |= RX_INT_ENABLE | RX_INT_MODE_1_BYTE;
+}
+
+struct net_host *net_connect(const union net_connect *const c)
+{
+ struct net_host *const h = &host;
+
+ if (h->used)
+ goto failure;
+
+ start_sio(c->serial.cfg.baud);
+
+ *h = (const struct net_host)
+ {
+ .role = NET_ROLE_CLIENT,
+ .used = true,
+ .h =
+ {
+ .cfg = get_transport_cfg()
+ }
+ };
+
+ if (transport_connect(&h->h))
+ goto failure;
+
+ return h;
+
+failure:
+ net_close(h);
return NULL;
}
+struct net_host *net_server(const union net_server *const c)
+{
+ struct net_host *const h = &host;
+
+ if (h->used)
+ return NULL;
+
+ *h = (const struct net_host)
+ {
+ .role = NET_ROLE_SERVER,
+ .used = true,
+ .h =
+ {
+ .cfg = get_transport_cfg()
+ }
+ };
+
+ start_sio(c->serial.cfg.baud);
+ return h;
+}
+
int net_init(void)
{
return 0;
@@ -43,18 +237,24 @@ void net_deinit(void)
{
}
+int net_set_event(struct net_host *const h,
+ const struct net_event *const ev)
+{
+ h->ev = *ev;
+ return 0;
+}
+
bool net_available(const enum net_domain d)
{
return d == NET_DOMAIN_SERIAL;
}
-const char *const *net_serial_devices(size_t *const n)
+bool net_serial_unique(void)
{
- static const char *const dev[] =
- {
- "PS1 port"
- };
+ return true;
+}
- *n = sizeof dev / sizeof *dev;
- return dev;
+enum net_role net_role(const struct net_host *s)
+{
+ return s->role;
}
diff --git a/src/net/src/enet/ipv4.c b/src/net/src/enet/ipv4.c
index 86d1697..53b6dc0 100644
--- a/src/net/src/enet/ipv4.c
+++ b/src/net/src/enet/ipv4.c
@@ -5,11 +5,11 @@
#include <stdio.h>
#include <stdlib.h>
-struct net_socket_domain
+struct net_host_domain
{
+ enum net_role role;
ENetHost *host;
- ENetPeer *peers;
- struct net_connect_ev ev;
+ struct net_event ev;
};
enum
@@ -18,17 +18,20 @@ enum
MAX_CHANNELS
};
-struct net_socket_domain *net_connect_ipv4(const union net_connect *const c)
+struct net_host_domain *net_connect_ipv4(const union net_connect *const c)
{
- struct net_socket_domain *const s = calloc(1, sizeof *s);
+ struct net_host_domain *const h = malloc(sizeof *h);
- if (!s)
+ if (!h)
goto failure;
- s->ev = c->common.ev;
- s->host = enet_host_create(NULL, 1, MAX_CHANNELS, 0, 0);
+ *h = (const struct net_host_domain)
+ {
+ .ev = c->common.ev,
+ .host = enet_host_create(NULL, 1, MAX_CHANNELS, 0, 0)
+ };
- if (!s->host)
+ if (!h->host)
{
fprintf(stderr, "%s: enet_host_create failed\n", __func__);
goto failure;
@@ -44,66 +47,89 @@ struct net_socket_domain *net_connect_ipv4(const union net_connect *const c)
addr.port = c->ipv4.port;
- if (!(s->peers = enet_host_connect(s->host, &addr, MAX_CHANNELS, 0)))
+ if (!enet_host_connect(h->host, &addr, MAX_CHANNELS, 0))
{
fprintf(stderr, "%s: enet_host_connect failed\n", __func__);
goto failure;
}
- return s;
+ h->role = NET_ROLE_CLIENT;
+ return h;
failure:
- if (s && s->host)
- enet_host_destroy(s->host);
+ if (h && h->host)
+ enet_host_destroy(h->host);
- free(s);
+ free(h);
return NULL;
}
-int net_read_ipv4(struct net_socket_domain *const s, void *const buf,
- const size_t n)
+int net_write_ipv4(struct net_host_domain *const h, const net_peer p,
+ const void *const buf, const size_t n)
{
- return -1;
+ ENetPacket *const pk = enet_packet_create(buf, n, ENET_PACKET_FLAG_RELIABLE);
+
+ if (!p)
+ {
+ fprintf(stderr, "%s: enet_packet_create failed\n", __func__);
+ return -1;
+ }
+ else if (enet_peer_send(p, CHANNEL, pk))
+ {
+ fprintf(stderr, "%s: enet_peer_send failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
}
-int net_write_ipv4(struct net_socket_domain *const s, const void *const buf,
- const size_t n)
+int net_set_event_ipv4(struct net_host_domain *const d,
+ const struct net_event *const ev)
{
- return -1;
+ d->ev = *ev;
+ return 0;
}
-int net_close_ipv4(struct net_socket_domain *const s)
+int net_close_ipv4(struct net_host_domain *const h)
{
- if (s && s->host)
- enet_host_destroy(s->host);
+ if (h && h->host)
+ enet_host_destroy(h->host);
- free(s);
+ free(h);
return 0;
}
-int net_update_ipv4(struct net_socket_domain *const s)
+int net_update_ipv4(struct net_host_domain *const h)
{
int res;
ENetEvent ev;
- while ((res = enet_host_service(s->host, &ev, 0)) > 0)
+ while ((res = enet_host_service(h->host, &ev, 0)) > 0)
{
switch (ev.type)
{
case ENET_EVENT_TYPE_CONNECT:
- if (s->ev.connected)
- s->ev.connected(s->ev.arg);
+ if (h->ev.connected)
+ h->ev.connected(ev.peer, h->ev.arg);
break;
case ENET_EVENT_TYPE_DISCONNECT:
- if (s->ev.disconnected)
- s->ev.disconnected(s->ev.arg);
+ if (h->ev.disconnected)
+ h->ev.disconnected(ev.peer, h->ev.arg);
break;
case ENET_EVENT_TYPE_RECEIVE:
+ if (h->ev.received)
+ {
+ const enet_uint8 *const buf = ev.packet->data;
+ const size_t n = ev.packet->dataLength;
+
+ if (buf && n)
+ h->ev.received(ev.peer, buf, n, h->ev.arg);
+ }
break;
case ENET_EVENT_TYPE_NONE:
@@ -120,11 +146,11 @@ int net_update_ipv4(struct net_socket_domain *const s)
return 0;
}
-struct net_socket_domain *net_server_ipv4(const union net_server *const srv)
+struct net_host_domain *net_server_ipv4(const union net_server *const srv)
{
- struct net_socket_domain *const s = calloc(1, sizeof *s);
+ struct net_host_domain *const h = malloc(sizeof *h);
- if (!s)
+ if (!h)
goto failure;
const ENetAddress addr =
@@ -132,22 +158,31 @@ struct net_socket_domain *net_server_ipv4(const union net_server *const srv)
.port = srv->ipv4.port
};
- s->host = enet_host_create(&addr, srv->common.max_players,
- MAX_CHANNELS, 0, 0);
+ *h = (const struct net_host_domain)
+ {
+ .host = enet_host_create(&addr, srv->common.max_players,
+ MAX_CHANNELS, 0, 0)
+ };
- if (!s->host)
+ if (!h->host)
{
fprintf(stderr, "%s: enet_host_create failed\n", __func__);
goto failure;
}
- return s;
+ h->role = NET_ROLE_SERVER;
+ return h;
failure:
- free(s);
+ free(h);
return NULL;
}
+enum net_role net_role_ipv4(const struct net_host_domain *const h)
+{
+ return h->role;
+}
+
int net_init_ipv4(void)
{
return enet_initialize();
diff --git a/src/net/src/net.c b/src/net/src/net.c
index f5dc938..fb0d155 100644
--- a/src/net/src/net.c
+++ b/src/net/src/net.c
@@ -1,104 +1,129 @@
#include <net.h>
#include <net_private.h>
+#include <net_serial_private.h>
#include <stddef.h>
#include <stdlib.h>
-struct net_socket *net_connect(const union net_connect *const c)
+struct net_host *net_connect(const union net_connect *const c)
{
- struct net_socket *const s = calloc(1, sizeof *s);
+ struct net_host *const h = malloc(sizeof *h);
- if (!s)
+ if (!h)
goto failure;
- static struct net_socket_domain *(*const f[])(const union net_connect *) =
+ static struct net_host_domain *(*const f[])(const union net_connect *) =
{
[NET_DOMAIN_IPV4] = net_connect_ipv4,
[NET_DOMAIN_SERIAL] = net_connect_serial
};
- if (!(s->s = f[c->common.domain](c)))
+ *h = (const struct net_host)
+ {
+ .d = c->common.domain
+ };
+
+ if (!(h->h = f[h->d](c)))
goto failure;
- return s;
+ return h;
failure:
- free(s);
+ free(h);
return NULL;
}
-int net_read(struct net_socket *const s, void *const buf, const size_t n)
+int net_write(struct net_host *const h, const net_peer p,
+ const void *const buf, const size_t n)
{
- static int (*const f[])(struct net_socket_domain *, void *, size_t) =
- {
- [NET_DOMAIN_IPV4] = net_read_ipv4,
- [NET_DOMAIN_SERIAL] = net_read_serial
- };
-
- return f[s->d](s->s, buf, n);
-}
-
-int net_write(struct net_socket *const s, const void *const buf, const size_t n)
-{
- static int (*const f[])(struct net_socket_domain *, const void *, size_t) =
+ static int (*const f[])(struct net_host_domain *, net_peer,
+ const void *, size_t) =
{
[NET_DOMAIN_IPV4] = net_write_ipv4,
[NET_DOMAIN_SERIAL] = net_write_serial
};
- return f[s->d](s->s, buf, n);
+ return f[h->d](h->h, p, buf, n);
}
-int net_close(struct net_socket *const s)
+int net_close(struct net_host *const h)
{
- if (!s)
+ if (!h)
return 0;
- static int (*const f[])(struct net_socket_domain *) =
+ static int (*const f[])(struct net_host_domain *) =
{
[NET_DOMAIN_IPV4] = net_close_ipv4,
[NET_DOMAIN_SERIAL] = net_close_serial
};
- const int res = f[s->d](s->s);
+ const int res = f[h->d](h->h);
- free(s);
+ free(h);
return res;
}
-int net_update(struct net_socket *const s)
+int net_update(struct net_host *const h)
{
- static int (*const f[])(struct net_socket_domain *) =
+ static int (*const f[])(struct net_host_domain *) =
{
[NET_DOMAIN_IPV4] = net_update_ipv4,
[NET_DOMAIN_SERIAL] = net_update_serial
};
- return f[s->d](s->s);
+ return f[h->d](h->h);
}
-struct net_socket *net_server(const union net_server *const srv)
+struct net_host *net_server(const union net_server *const srv)
{
- struct net_socket *const s = calloc(1, sizeof *s);
+ struct net_host *const h = malloc(sizeof *h);
- if (!s)
+ if (!h)
goto failure;
- s->d = srv->common.domain;
-
- static struct net_socket_domain *(*const f[])(const union net_server *) =
+ static struct net_host_domain *(*const f[])(const union net_server *) =
{
[NET_DOMAIN_IPV4] = net_server_ipv4,
[NET_DOMAIN_SERIAL] = net_server_serial
};
- if (!(s->s = f[s->d](srv)))
+ *h = (const struct net_host)
+ {
+ .d = srv->common.domain
+ };
+
+ if (!(h->h = f[h->d](srv)))
goto failure;
+ return h;
+
failure:
- net_close(s);
+ net_close(h);
return NULL;
}
+enum net_role net_role(const struct net_host *const h)
+{
+ static enum net_role (*const f[])(const struct net_host_domain *) =
+ {
+ [NET_DOMAIN_IPV4] = net_role_ipv4,
+ [NET_DOMAIN_SERIAL] = net_role_serial
+ };
+
+ return f[h->d](h->h);
+}
+
+int net_set_event(struct net_host *const h,
+ const struct net_event *const ev)
+{
+ static int (*const f[])(struct net_host_domain *, const struct net_event *) =
+ {
+ [NET_DOMAIN_IPV4] = net_set_event_ipv4,
+ [NET_DOMAIN_SERIAL] = net_set_event_serial
+ };
+
+ return f[h->d](h->h, ev);
+}
+
int net_init(void)
{
return net_init_ipv4() || net_init_serial();
diff --git a/src/net/src/serial.c b/src/net/src/serial.c
new file mode 100644
index 0000000..a9f3f73
--- /dev/null
+++ b/src/net/src/serial.c
@@ -0,0 +1,37 @@
+#include <net.h>
+#include <net_serial_private.h>
+#include <transport.h>
+
+void net_serial_on_received(const struct transport_event *const t_ev,
+ const struct net_event *const n_ev)
+{
+ static struct peer
+ {
+ int dummy;
+ } peer;
+
+ switch (t_ev->common.type)
+ {
+ case TRANSPORT_EVENT_TYPE_CONNECT:
+ if (n_ev->connected)
+ n_ev->connected(&peer, n_ev->arg);
+
+ break;
+
+ case TRANSPORT_EVENT_TYPE_DISCONNECT:
+ if (n_ev->disconnected)
+ n_ev->disconnected(&peer, n_ev->arg);
+
+ break;
+
+ case TRANSPORT_EVENT_TYPE_DATA:
+ if (n_ev->received)
+ {
+ const struct transport_event_data *const d = &t_ev->u.data;
+
+ n_ev->received(&peer, d->buf, d->n, n_ev->arg);
+ }
+
+ break;
+ }
+}
diff --git a/src/net/win9x/src/serial.c b/src/net/win9x/src/serial.c
index c12369a..c1cffb6 100644
--- a/src/net/win9x/src/serial.c
+++ b/src/net/win9x/src/serial.c
@@ -1,36 +1,406 @@
#include <net.h>
#include <net_private.h>
+#include <windows.h>
+#include <errno.h>
#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-int net_read_serial(struct net_socket_domain *const h, void *const buf,
- const size_t n)
+struct net_host_domain
{
- return -1;
+ HANDLE com;
+ enum net_role role;
+ struct transport_handle h;
+ struct net_event ev;
+
+ struct req
+ {
+ const void *buf;
+ size_t n;
+ OVERLAPPED ov;
+
+ enum req_type
+ {
+ REQ_TYPE_READ,
+ REQ_TYPE_WRITE
+ } type;
+ } *reqs;
+
+ size_t n_reqs;
+};
+
+int net_write_serial(struct net_host_domain *const h, const net_peer p,
+ const void *const buf, const size_t n)
+{
+ return transport_send(&h->h, buf, n);
+}
+
+int net_close_serial(struct net_host_domain *const h)
+{
+ if (h)
+ {
+ if (h->com != INVALID_HANDLE_VALUE)
+ CloseHandle(h->com);
+
+ if (h->reqs)
+ for (size_t i = 0; i < h->n_reqs; i++)
+ {
+ struct req *const r = &h->reqs[i];
+
+ if (r->ov.hEvent)
+ CloseHandle(r->ov.hEvent);
+ }
+
+ free(h->reqs);
+ }
+
+ free(h);
+ return 0;
+}
+
+int net_update_serial(struct net_host_domain *const h)
+{
+ return transport_update(&h->h);
+}
+
+static void on_received(const struct transport_event *const ev,
+ void *const arg)
+{
+ struct net_host_domain *const h = arg;
+
+ net_serial_on_received(ev, &h->ev);
+}
+
+static struct req *find_req(struct net_host_domain *const h,
+ const void *const buf, const size_t n, const enum req_type t)
+{
+ for (size_t i = 0; i < h->n_reqs; i++)
+ {
+ struct req *const r = &h->reqs[i];
+
+ if (r->buf == buf && r->n == n && r->type == t)
+ return r;
+ }
+
+ return NULL;
}
-int net_write_serial(struct net_socket_domain *const h, const void *const buf,
- const size_t n)
+static int free_req(struct net_host_domain *const h, struct req *const r)
{
+ for (size_t i = 0; i < h->n_reqs; i++)
+ if (&h->reqs[i] == r)
+ {
+ const HANDLE ev = r->ov.hEvent;
+
+ if (ev)
+ CloseHandle(ev);
+
+ for (size_t j = i + 1; j < h->n_reqs; j++)
+ h->reqs[j - 1] = h->reqs[j];
+
+ const size_t n = h->n_reqs - 1;
+
+ if (n)
+ {
+ if (!(h->reqs = realloc(h->reqs, n * sizeof *h->reqs)))
+ {
+ fprintf(stderr, "%s: realloc(3) failed: %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+ }
+ else
+ {
+ free(h->reqs);
+ h->reqs = NULL;
+ }
+
+ h->n_reqs = n;
+ return 0;
+ }
+
return -1;
}
-int net_close_serial(struct net_socket_domain *const h)
+static struct req *alloc_req(struct net_host_domain *const h,
+ const void *const buf, const size_t n, const enum req_type t)
+{
+ if (!(h->reqs = realloc(h->reqs, (h->n_reqs + 1) * sizeof *h->reqs)))
+ {
+ fprintf(stderr, "%s: realloc(3) failed: %s\n",
+ __func__, strerror(errno));
+ return NULL;
+ }
+
+ struct req *const r = &h->reqs[h->n_reqs++];
+
+ *r = (const struct req)
+ {
+ .buf = buf,
+ .n = n,
+ .type = t,
+ .ov =
+ {
+ .hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)
+ }
+ };
+
+ if (!r->ov.hEvent)
+ {
+ fprintf(stderr, "%s: CreateEvent failed: %#lx\n", __func__,
+ GetLastError());
+ free_req(h, r);
+ return NULL;
+ }
+
+ return r;
+}
+
+static int get_result(struct net_host_domain *const h, struct req *const r)
+{
+ DWORD res;
+
+ if (GetOverlappedResult(h->com, &r->ov, &res, FALSE) == FALSE)
+ {
+ const DWORD error = GetLastError();
+
+ if (error != ERROR_IO_INCOMPLETE)
+ {
+ fprintf(stderr, "%s: GetOverlappedResult failed: %#lx\n",
+ __func__, error);
+ return -1;
+ }
+ else
+ return 0;
+ }
+ else if (ResetEvent(r->ov.hEvent) == FALSE)
+ {
+ fprintf(stderr, "%s: ResetEvent failed: %#lx\n", __func__,
+ GetLastError());
+ return -1;
+ }
+ else if (free_req(h, r))
+ {
+ fprintf(stderr, "%s: free_req failed\n", __func__);
+ return -1;
+ }
+
+ return res;
+}
+
+static int on_read(void *const buf, const size_t n, void *const arg)
{
+ const enum req_type t = REQ_TYPE_READ;
+ struct net_host_domain *const h = arg;
+ struct req *r = find_req(h, buf, n, t);
+
+ if (!r)
+ {
+ DWORD res;
+
+ if (!(r = alloc_req(h, buf, n, t)))
+ {
+ fprintf(stderr, "%s: alloc_req failed\n", __func__);
+ goto failure;
+ }
+ else if (ReadFile(h->com, buf, n, &res, &r->ov) == FALSE
+ && GetLastError() != ERROR_IO_PENDING)
+ {
+ fprintf(stderr, "%s: ReadFile failed: %#lx\n",
+ __func__, GetLastError());
+ goto failure;
+ }
+ }
+
+ return get_result(h, r);
+
+failure:
+ if (r)
+ free_req(h, r);
+
return -1;
}
-int net_update_serial(struct net_socket_domain *const h)
+static int on_write(const void *const buf, const size_t n, void *const arg)
{
+ const enum req_type t = REQ_TYPE_WRITE;
+ struct net_host_domain *const h = arg;
+ struct req *r = find_req(h, buf, n, t);
+
+ if (!r)
+ {
+ DWORD res;
+
+ if (!(r = alloc_req(h, buf, n, t)))
+ {
+ fprintf(stderr, "%s: alloc_req failed\n", __func__);
+ goto failure;
+ }
+ else if (WriteFile(h->com, buf, n, &res, &r->ov) == FALSE
+ && GetLastError() != ERROR_IO_PENDING)
+ {
+ fprintf(stderr, "%s: WriteFile failed: %#lx\n",
+ __func__, GetLastError());
+ goto failure;
+ }
+ }
+
+ return get_result(h, r);
+
+failure:
+ if (r)
+ free_req(h, r);
+
return -1;
}
-struct net_socket_domain *net_connect_serial(const union net_connect *const srv)
+static struct transport_cfg get_transport_cfg(void *const arg)
+{
+ return (const struct transport_cfg)
+ {
+ .arg = arg,
+ .received = on_received,
+ .read = on_read,
+ .write = on_write
+ };
+}
+
+static HANDLE init_com(const char *const dev)
+{
+ static const char *const prefix = "\\\\.\\";
+
+ /* snprintf(NULL, 0, ...) could have been used to determine the
+ * total length of the string. However, this is a C99 feature that
+ * must be supported by the underlying libc, which is not the case
+ * for Win9x, where only ANSI C is supported. */
+ char *const fulldev = calloc(1, strlen(prefix) + strlen(dev) + 1);
+ HANDLE ret = INVALID_HANDLE_VALUE;
+
+ if (!fulldev)
+ {
+ fprintf(stderr, "%s: calloc(3) failed: %s\n", __func__,
+ strerror(errno));
+ goto end;
+ }
+
+ strcat(fulldev, prefix);
+ strcat(fulldev, dev);
+
+ ret = CreateFile(
+ fulldev,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, /* No sharing. */
+ NULL, /* No security. */
+ OPEN_EXISTING, /* Open existing port only. */
+ FILE_FLAG_OVERLAPPED, /* Asynchronous I/O. */
+ NULL /* NULL for COM devices. */);
+
+ if (ret == INVALID_HANDLE_VALUE)
+ fprintf(stderr, "%s: CreateFile failed: %#lx\n", __func__,
+ GetLastError());
+
+end:
+ free(fulldev);
+ return ret;
+}
+
+static int config_baud(const HANDLE com, const unsigned long baud)
+{
+ DCB dcb =
+ {
+ .DCBlength = sizeof dcb,
+ .BaudRate = baud,
+ .ByteSize = 8,
+ .StopBits = ONESTOPBIT,
+ .Parity = NOPARITY,
+ .fBinary = TRUE
+ };
+
+ if (SetCommState(com, &dcb) == FALSE)
+ {
+ fprintf(stderr, "%s: SetCommState failed: %#lx\n",
+ __func__, GetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+struct net_host_domain *net_connect_serial(const union net_connect *const c)
{
+ struct net_host_domain *const h = malloc(sizeof *h);
+ const struct net_serial_cfg *const cfg = &c->serial.cfg;
+
+ if (!h)
+ goto failure;
+
+ *h = (const struct net_host_domain)
+ {
+ .role = NET_ROLE_CLIENT,
+ .com = init_com(cfg->dev),
+ .h =
+ {
+ .cfg = get_transport_cfg(h)
+ }
+ };
+
+ if (h->com == INVALID_HANDLE_VALUE)
+ {
+ fprintf(stderr, "%s: init_com failed\n", __func__);
+ goto failure;
+ }
+ else if (config_baud(h->com, cfg->baud))
+ {
+ fprintf(stderr, "%s: config_com_baud failed\n", __func__);
+ goto failure;
+ }
+ else if (transport_connect(&h->h))
+ {
+ fprintf(stderr, "%s: transport_connect failed\n", __func__);
+ goto failure;
+ }
+
+ return h;
+
+failure:
+ net_close_serial(h);
return NULL;
}
-struct net_socket_domain *net_server_serial(const union net_server *const srv)
+struct net_host_domain *net_server_serial(const union net_server *const s)
{
+ struct net_host_domain *const h = malloc(sizeof *h);
+ const struct net_serial_cfg *const cfg = &s->serial.cfg;
+
+ if (!h)
+ goto failure;
+
+ *h = (const struct net_host_domain)
+ {
+ .role = NET_ROLE_SERVER,
+ .com = init_com(cfg->dev),
+ .h =
+ {
+ .cfg = get_transport_cfg(h)
+ }
+ };
+
+ if (h->com == INVALID_HANDLE_VALUE)
+ {
+ fprintf(stderr, "%s: init_com failed\n", __func__);
+ goto failure;
+ }
+ else if (config_baud(h->com, cfg->baud))
+ {
+ fprintf(stderr, "%s: config_com_baud failed\n", __func__);
+ goto failure;
+ }
+
+ return h;
+
+failure:
+ net_close_serial(h);
return NULL;
}
@@ -43,7 +413,19 @@ void net_deinit_serial(void)
{
}
-const char *const *net_serial_devices(size_t *const n)
+int net_set_event_serial(struct net_host_domain *const d,
+ const struct net_event *const ev)
{
- return NULL;
+ d->ev = *ev;
+ return 0;
+}
+
+enum net_role net_role_serial(const struct net_host_domain *const h)
+{
+ return h->role;
+}
+
+bool net_serial_unique(void)
+{
+ return false;
}
diff --git a/src/packet/CMakeLists.txt b/src/packet/CMakeLists.txt
new file mode 100644
index 0000000..597dff5
--- /dev/null
+++ b/src/packet/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_library(packet "src/packet.c")
+target_include_directories(packet PUBLIC "inc")
+target_link_libraries(packet PUBLIC game net PRIVATE util)
diff --git a/src/packet/inc/packet.h b/src/packet/inc/packet.h
new file mode 100644
index 0000000..6f555a6
--- /dev/null
+++ b/src/packet/inc/packet.h
@@ -0,0 +1,91 @@
+#ifndef PACKET_H
+#define PACKET_H
+
+#include <game.h>
+#include <net.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+union packet
+{
+ struct packet_common
+ {
+ enum packet_type
+ {
+ PACKET_TYPE_NAME,
+ PACKET_TYPE_CHAT,
+ PACKET_TYPE_READY,
+
+ MAX_PACKET_TYPES
+ } type;
+ } common;
+
+ struct packet_name
+ {
+ struct packet_common common;
+ const char *name;
+ } name;
+
+ struct packet_chat
+ {
+ struct packet_common common;
+ const char *msg;
+ } chat;
+
+ struct packet_ready
+ {
+ struct packet_common common;
+ bool ready;
+ } ready;
+};
+
+enum {PACKET_MAX_MESSAGE_LEN = 64};
+
+struct packet_ctx
+{
+ struct net_host *host;
+ void (*cb)(net_peer, const union packet *, void *);
+ void *arg;
+};
+
+struct packet_input
+{
+ union packet p;
+ size_t body_i;
+
+ enum
+ {
+ PACKET_STATE_HEADER,
+ PACKET_STATE_BODY
+ } state;
+
+ union packet_data
+ {
+ struct packet_ctx_name
+ {
+ size_t len;
+ char name[GAME_PLAYER_NAME_LEN];
+ } name;
+
+ struct packet_ctx_chat
+ {
+ size_t len;
+ char msg[PACKET_MAX_MESSAGE_LEN];
+ } chat;
+ } data;
+};
+
+int packet_send(const struct packet_ctx *h, net_peer peer, const union packet *pkt);
+int packet_feed(const struct packet_ctx *h, net_peer p,
+ struct packet_input *in, const void *buf, size_t n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PACKET_H */
diff --git a/src/packet/src/packet.c b/src/packet/src/packet.c
new file mode 100644
index 0000000..3ace4af
--- /dev/null
+++ b/src/packet/src/packet.c
@@ -0,0 +1,251 @@
+#include <packet.h>
+#include <net.h>
+#include <util.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef uint8_t packet_len, packet_ready;
+
+int packet_send(const struct packet_ctx *const h, const net_peer peer,
+ const union packet *const pkt)
+{
+ const uint8_t header = pkt->common.type;
+
+ if (net_write(h->host, peer, &header, sizeof header))
+ {
+ fprintf(stderr, "%s: failed to write header\n", __func__);
+ return -1;
+ }
+
+ const void *buf = NULL;
+ size_t n = 0;
+ bool send_len = false;
+ packet_ready r;
+
+ switch (pkt->common.type)
+ {
+ case PACKET_TYPE_NAME:
+ buf = pkt->name.name;
+ n = strlen(buf);
+ send_len = true;
+ break;
+
+ case PACKET_TYPE_CHAT:
+ buf = pkt->chat.msg;
+ n = strlen(buf);
+ send_len = true;
+ break;
+
+ case PACKET_TYPE_READY:
+ r = pkt->ready.ready;
+ buf = &r;
+ n = sizeof r;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown packet type %d\n", __func__,
+ pkt->common.type);
+ return -1;
+ }
+
+ if (send_len)
+ {
+ const packet_len len = n;
+
+ if (net_write(h->host, peer, &len, sizeof len))
+ {
+ fprintf(stderr, "%s: failed to write body length\n", __func__);
+ return -1;
+ }
+ }
+
+ if (net_write(h->host, peer, buf, n))
+ {
+ fprintf(stderr, "%s: failed to write body\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_header(struct packet_input *const in,
+ const void **const buf, size_t *const n)
+{
+ const uint8_t header = *(const uint8_t *)*buf;
+
+ if (header >= MAX_PACKET_TYPES)
+ {
+ fprintf(stderr, "%s: invalid packet type %" PRIu8 "\n",
+ __func__, header);
+ return -1;
+ }
+
+ in->p.common.type = header;
+ *buf = (const uint8_t *)*buf + sizeof header;
+ *n -= sizeof header;
+ return 0;
+}
+
+static int read_string_len(size_t *const len, const size_t maxlen,
+ const void **const buf, size_t *const n)
+{
+ *len = *(const packet_len *)*buf;
+
+ if (*len >= maxlen)
+ {
+ fprintf(stderr, "%s: maximum length exceeded (%zu/%zu)\n",
+ __func__, *len, maxlen);
+ return -1;
+ }
+ else if (!*len)
+ {
+ fprintf(stderr, "%s: invalid zero length\n", __func__);
+ return -1;
+ }
+
+ *buf = (const uint8_t *)*buf + sizeof (packet_len);
+ *n -= sizeof (packet_len);
+ return 0;
+}
+
+static int read_string_body(struct packet_input *const in, char *const dst,
+ const size_t len, const void **const buf, size_t *const n)
+{
+ const size_t rem = len > *n ? *n : len;
+
+ for (const char *ch = *buf;
+ ch - (const char *)*buf < rem;
+ ch++)
+ {
+ if (!isprint((unsigned char)*ch))
+ {
+ fprintf(stderr, "%s: unexpected character %#hhx\n", __func__, *ch);
+ return -1;
+ }
+
+ dst[in->body_i++] = *ch;
+ }
+
+ *buf = (const uint8_t *)*buf + rem;
+ *n -= rem;
+
+ if (in->body_i == len)
+ {
+ dst[in->body_i] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
+static int read_string(struct packet_input *const in, char *const dst,
+ size_t *const len, const size_t maxlen, const void **const buf,
+ size_t *const n)
+{
+ if (!*len && read_string_len(len, maxlen, buf, n))
+ return -1;
+ else if (!*n)
+ return 1;
+
+ return read_string_body(in, dst, *len, buf, n);
+}
+
+static int read_chat(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ struct packet_ctx_chat *const c = &in->data.chat;
+ const int ret = read_string(in, c->msg, &c->len, sizeof c->msg - 1, buf, n);
+
+ if (!ret)
+ in->p.chat.msg = c->msg;
+
+ return ret;
+}
+
+static int read_name(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ struct packet_ctx_name *const c = &in->data.name;
+ const int ret = read_string(in, c->name, &c->len,
+ sizeof c->name - 1, buf, n);
+
+ if (!ret)
+ in->p.name.name = c->name;
+
+ return ret;
+}
+
+static int read_ready(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ const packet_ready pr = *(const packet_ready *)*buf;
+
+ if (pr > 1)
+ {
+ fprintf(stderr, "%s: invalid ready flag %#hhx\n", __func__, pr);
+ return -1;
+ }
+
+ in->p.ready.ready = pr;
+ *buf = (const uint8_t *)*buf + sizeof pr;
+ *n -= sizeof pr;
+ return 0;
+}
+
+static int read_body(struct packet_input *const in, const void **const buf,
+ size_t *const n)
+{
+ static int (*const f[])(struct packet_input *, const void **, size_t *) =
+ {
+ [PACKET_TYPE_CHAT] = read_chat,
+ [PACKET_TYPE_NAME] = read_name,
+ [PACKET_TYPE_READY] = read_ready
+ };
+
+ return f[in->p.common.type](in, buf, n);
+}
+
+static void reset(struct packet_input *const in)
+{
+ *in = (const struct packet_input){0};
+}
+
+int packet_feed(const struct packet_ctx *const h, const net_peer p,
+ struct packet_input *const in, const void *buf, size_t n)
+{
+ int res;
+
+ switch (in->state)
+ {
+ case PACKET_STATE_HEADER:
+ if ((res = read_header(in, &buf, &n)) < 0)
+ goto failure;
+ else if (!res)
+ in->state = PACKET_STATE_BODY;
+
+ if (!n)
+ break;
+
+ /* Fall through. */
+ case PACKET_STATE_BODY:
+ if ((res = read_body(in, &buf, &n)) < 0)
+ goto failure;
+ else if (!res)
+ {
+ h->cb(p, &in->p, h->arg);
+ reset(in);
+ }
+
+ break;
+ }
+
+ return 0;
+
+failure:
+ reset(in);
+ return -1;
+}
diff --git a/src/pad/inc/pad.h b/src/pad/inc/pad.h
index c8f6b31..d68f734 100644
--- a/src/pad/inc/pad.h
+++ b/src/pad/inc/pad.h
@@ -3,6 +3,7 @@
#include <pad/port.h>
#include <stdbool.h>
+#include <stddef.h>
#ifdef __cplusplus
extern "C"
@@ -34,17 +35,20 @@ enum pad_key
struct pad
{
- int player;
+ int id;
int mask, oldmask;
struct pad_port port;
};
-void pad_init(int player, struct pad *p);
+void pad_init(int id, struct pad *p);
+void pad_deinit(int id, struct pad *p);
void pad_update(struct pad *p);
bool pad_pressed(const struct pad *p, enum pad_key k);
bool pad_justpressed(const struct pad *p, enum pad_key k);
bool pad_released(const struct pad *p, enum pad_key k);
const char *pad_str(enum pad_key k);
+size_t pad_count(void);
+const char *pad_name(int id);
#ifdef __cplusplus
}
diff --git a/src/pad/ps1/src/pad.c b/src/pad/ps1/src/pad.c
index eea56ed..660cf89 100644
--- a/src/pad/ps1/src/pad.c
+++ b/src/pad/ps1/src/pad.c
@@ -4,10 +4,18 @@
#include <stddef.h>
#include <string.h>
-void pad_init(const int player, struct pad *const p)
+static const char *const pads[] =
{
- memset(p, 0, sizeof *p);
- p->player = player;
+ "Pad 1",
+ "Pad 2"
+};
+
+void pad_init(const int id, struct pad *const p)
+{
+ *p = (const struct pad)
+ {
+ .id = id
+ };
}
void pad_port_update(struct pad *const p)
@@ -27,7 +35,7 @@ void pad_port_update(struct pad *const p)
};
p->mask = 0;
- PSX_PollPad(p->player, &p->port.pps);
+ PSX_PollPad(p->id, &p->port.pps);
for (size_t i = 0; i < sizeof psx_keys / sizeof *psx_keys; i++)
{
@@ -39,3 +47,13 @@ void pad_port_update(struct pad *const p)
p->mask &= ~mask;
}
}
+
+size_t pad_count(void)
+{
+ return sizeof pads / sizeof *pads;
+}
+
+const char *pad_name(const int id)
+{
+ return pads[id];
+}
diff --git a/src/pad/sdl-1.2/inc/pad/port.h b/src/pad/sdl-1.2/inc/pad/port.h
index 8334b6a..d28bb11 100644
--- a/src/pad/sdl-1.2/inc/pad/port.h
+++ b/src/pad/sdl-1.2/inc/pad/port.h
@@ -1,6 +1,8 @@
#ifndef PAD_SDL_1_2_H
#define PAD_SDL_1_2_H
+#include <SDL_joystick.h>
+
#ifdef __cplusplus
extern "C"
{
@@ -8,7 +10,7 @@ extern "C"
struct pad_port
{
- int dummy;
+ SDL_Joystick *joystick;
};
#ifdef __cplusplus
diff --git a/src/pad/sdl-1.2/src/pad.c b/src/pad/sdl-1.2/src/pad.c
index 57fdb25..97aa639 100644
--- a/src/pad/sdl-1.2/src/pad.c
+++ b/src/pad/sdl-1.2/src/pad.c
@@ -1,11 +1,57 @@
#include <pad.h>
#include <SDL.h>
+#include <SDL_joystick.h>
#include <stdio.h>
+static void global_init(void)
+{
+ SDL_JoystickEventState(SDL_ENABLE);
+}
+
void pad_port_update(struct pad *const p)
{
+ enum {FLAGS = SDL_JOYBUTTONDOWNMASK
+ | SDL_JOYBUTTONUPMASK
+ | SDL_JOYAXISMOTIONMASK
+ | SDL_QUITMASK};
+
+ SDL_Event ev;
+ int n;
+
+ while ((n = SDL_PeepEvents(&ev, 1, SDL_GETEVENT, FLAGS)) > 0)
+ {
+ }
+
+ if (n < 0)
+ fprintf(stderr, "%s: SDL_PeepEvents: %s\n", __func__, SDL_GetError());
+}
+
+void pad_init(const int id, struct pad *const p)
+{
+ static bool init;
+
+ if (!init)
+ {
+ global_init();
+ init = true;
+ }
+
+ if (!(p->port.joystick = SDL_JoystickOpen(id)))
+ fprintf(stderr, "%s: SDL_JoystickOpen failed: %s\n",
+ __func__, SDL_GetError());
+ else
+ printf("%s: num axis: %d\n", __func__,
+ SDL_JoystickNumAxes(p->port.joystick));
+}
+
+size_t pad_count(void)
+{
+ const int res = SDL_NumJoysticks();
+
+ return res > 0 ? res : 0;
}
-void pad_init(const int player, struct pad *const p)
+const char *pad_name(const int id)
{
+ return SDL_JoystickName(id);
}
diff --git a/src/peripheral/inc/peripheral.h b/src/peripheral/inc/peripheral.h
index 8f8f73b..2895f54 100644
--- a/src/peripheral/inc/peripheral.h
+++ b/src/peripheral/inc/peripheral.h
@@ -55,6 +55,7 @@ UTIL_STATIC_ASSERT(!offsetof(struct peripheral_pad, common),
UTIL_STATIC_ASSERT(!offsetof(struct peripheral_kbm, common),
"unexpected offsetof for struct peripheral_kbm");
+int peripheral_get_default(struct peripheral_cfg *cfg);
void peripheral_init(const struct peripheral_cfg *cfg, union peripheral *p);
void peripheral_input_set(union peripheral *p, void (*cb)(char, void *));
void peripheral_update(union peripheral *p);
diff --git a/src/peripheral/src/peripheral.c b/src/peripheral/src/peripheral.c
index eeec4fa..71f81f8 100644
--- a/src/peripheral/src/peripheral.c
+++ b/src/peripheral/src/peripheral.c
@@ -62,5 +62,14 @@ void peripheral_init(const struct peripheral_cfg *const cfg,
int peripheral_get_default(struct peripheral_cfg *const cfg)
{
- return -1;
+ *cfg = (const struct peripheral_cfg){0};
+
+ if (pad_count())
+ cfg->type = PERIPHERAL_TYPE_PAD;
+ else if (keyboard_available() && mouse_available())
+ cfg->type = PERIPHERAL_TYPE_KEYBOARD_MOUSE;
+ else
+ return -1;
+
+ return 0;
}
diff --git a/src/player/inc/human_player.h b/src/player/inc/human_player.h
index 173ff80..abc25e9 100644
--- a/src/player/inc/human_player.h
+++ b/src/player/inc/human_player.h
@@ -22,9 +22,8 @@ enum {MAX_SELECTED_INSTANCES = 4};
struct human_player_cfg
{
- enum peripheral_type sel_periph;
struct player_cfg pl;
- int padn;
+ union peripheral *p;
struct camera_dim dim;
};
@@ -32,7 +31,7 @@ struct human_player
{
struct player pl;
struct camera cam;
- union peripheral periph;
+ union peripheral *periph;
struct input in;
struct sel_instance
diff --git a/src/player/src/human_player.c b/src/player/src/human_player.c
index 314581b..cded3b0 100644
--- a/src/player/src/human_player.c
+++ b/src/player/src/human_player.c
@@ -543,7 +543,7 @@ static void update_target(struct human_player *const h)
static void update_from_pad(struct human_player *const h,
struct player_others *const o)
{
- const struct pad *const p = &h->periph.pad.pad;
+ const struct pad *const p = &h->periph->pad.pad;
const struct input *const in = &h->in;
if (input_pad_justpressed(in, p, PAD_KEY_A))
@@ -559,9 +559,9 @@ static void update_from_pad(struct human_player *const h,
static void update_from_touch(struct human_player *const h,
struct player_others *const o)
{
- const struct mouse *const m = &h->periph.kbm.mouse;
+ const struct mouse *const m = &h->periph->kbm.mouse;
const struct input *const in = &h->in;
- struct peripheral_kbm *const kbm = &h->periph.kbm;
+ struct peripheral_kbm *const kbm = &h->periph->kbm;
bool *const pan = &h->cam.pan;
if (input_mouse_pressed(in, m, MOUSE_BUTTON_LEFT) && !*pan)
@@ -590,8 +590,8 @@ static void update_from_touch(struct human_player *const h,
static void update_from_keyboard_mouse(struct human_player *const h,
struct player_others *const o)
{
- const struct mouse *const m = &h->periph.kbm.mouse;
- const struct keyboard *const k = &h->periph.kbm.keyboard;
+ const struct mouse *const m = &h->periph->kbm.mouse;
+ const struct keyboard *const k = &h->periph->kbm.keyboard;
const struct input *const in = &h->in;
if (input_mouse_justreleased(in, m, MOUSE_BUTTON_LEFT))
@@ -619,10 +619,10 @@ void human_player_update(struct human_player *const h,
human_player_gui_update(h);
update_selected(h);
update_target(h);
- peripheral_update(&h->periph);
- input_update(&h->in, &h->periph);
+ peripheral_update(h->periph);
+ input_update(&h->in, h->periph);
- switch (h->periph.common.type)
+ switch (h->periph->common.type)
{
case PERIPHERAL_TYPE_PAD:
update_from_pad(h, o);
@@ -637,7 +637,7 @@ void human_player_update(struct human_player *const h,
break;
}
- camera_update(&h->cam, &h->periph, &h->in);
+ camera_update(&h->cam, h->periph, &h->in);
player_update(p);
}
}
@@ -710,10 +710,10 @@ int human_player_render(const struct human_player *const h,
|| render_own_buildings(h)
|| render_resources(h, o->res, o->n_res)
|| human_player_gui_render(h)
- || input_render(&h->in, &h->periph))
+ || input_render(&h->in, h->periph))
return -1;
- switch (h->periph.common.type)
+ switch (h->periph->common.type)
{
case PERIPHERAL_TYPE_PAD:
/* Fall through. */
@@ -739,14 +739,8 @@ int human_player_init(const struct human_player_cfg *const cfg,
if (player_init(&cfg->pl, &h->pl))
return -1;
- const struct peripheral_cfg p_cfg =
- {
- .type = cfg->sel_periph,
- .padn = cfg->padn
- };
-
- peripheral_init(&p_cfg, &h->periph);
cursor_init(&h->cam.cursor);
+ h->periph = cfg->p;
h->cam.dim = cfg->dim;
h->top_gui = true;
UTIL_STATIC_ASSERT(sizeof h->gui_res == sizeof h->pl.resources,
diff --git a/src/player/src/human_player_gui.c b/src/player/src/human_player_gui.c
index fce749e..79245dc 100644
--- a/src/player/src/human_player_gui.c
+++ b/src/player/src/human_player_gui.c
@@ -248,14 +248,13 @@ static int render_sel_multiple(const struct human_player *const h,
static int render_sel(const struct human_player *const h)
{
- enum {OFFSET = 60};
struct gui_rounded_rect r;
gui_rounded_rect_init(&r);
r.common.x = 16;
- r.common.y = screen_h - OFFSET;
r.w = screen_w - (r.common.x * 2);
- r.h = OFFSET;
+ r.h = screen_h / 4;
+ r.common.y = screen_h - r.h;
if (h->n_sel == 1)
return render_sel_single(h, &r.common);
diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt
new file mode 100644
index 0000000..f7442e4
--- /dev/null
+++ b/src/settings/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_library(settings
+ "src/settings.c"
+)
+set(inc "inc")
+
+if(PS1_BUILD)
+ set(inc ${inc} "ps1/inc")
+elseif(SDL1_2_BUILD)
+ set(inc ${inc} "sdl-1.2/inc")
+endif()
+
+target_include_directories(settings PUBLIC ${inc})
+target_link_libraries(settings PUBLIC game peripheral PRIVATE gfx)
diff --git a/src/settings/inc/settings.h b/src/settings/inc/settings.h
new file mode 100644
index 0000000..25e73b9
--- /dev/null
+++ b/src/settings/inc/settings.h
@@ -0,0 +1,36 @@
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include <settings/port.h>
+#include <game.h>
+#include <peripheral.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct settings
+{
+ short screen_w, screen_h;
+ bool fullscreen;
+ enum peripheral_type periph_type;
+ int pad_i;
+ char name[GAME_PLAYER_NAME_LEN];
+};
+
+struct settings_rt
+{
+ union peripheral *p;
+};
+
+int settings_load_ex(const char *path, struct settings *s);
+int settings_apply(const struct settings *s, const struct settings_rt *r);
+int settings_save_ex(const char *path, const struct settings *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SETTINGS_H */
diff --git a/src/settings/ps1/inc/settings/port.h b/src/settings/ps1/inc/settings/port.h
new file mode 100644
index 0000000..2792717
--- /dev/null
+++ b/src/settings/ps1/inc/settings/port.h
@@ -0,0 +1,16 @@
+#ifndef SETTINGS_PORT_H
+#define SETTINGS_PORT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define settings_load(path, s) settings_load_ex("bu00:\\" path, s)
+#define settings_save(path, s) settings_save_ex("bu00:\\" path, s)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SETTINGS_PORT_H */
diff --git a/src/settings/sdl-1.2/inc/settings/port.h b/src/settings/sdl-1.2/inc/settings/port.h
new file mode 100644
index 0000000..6332842
--- /dev/null
+++ b/src/settings/sdl-1.2/inc/settings/port.h
@@ -0,0 +1,15 @@
+#ifndef SETTINGS_PORT_H
+#define SETTINGS_PORT_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define settings_load(...) settings_load_ex(__VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SETTINGS_PORT_H */
diff --git a/src/settings/src/settings.c b/src/settings/src/settings.c
new file mode 100644
index 0000000..0427ef8
--- /dev/null
+++ b/src/settings/src/settings.c
@@ -0,0 +1,48 @@
+#include <settings.h>
+#include <gfx.h>
+#include <peripheral.h>
+#include <stdio.h>
+
+int settings_apply(const struct settings *const s,
+ const struct settings_rt *const r)
+{
+ if (gfx_fullscreen_available()
+ && gfx_set_fullscreen(s->screen_w, s->screen_h))
+ return -1;
+
+ const struct peripheral_cfg cfg =
+ {
+ .padn = s->pad_i,
+ .type = s->periph_type
+ };
+
+ peripheral_init(&cfg, r->p);
+ return 0;
+}
+
+int settings_load_ex(const char *const path, struct settings *const s)
+{
+ int ret = -1;
+ FILE *const f = fopen(path, "rb");
+
+ if (!f)
+ goto end;
+
+ ret = 0;
+
+end:
+ if (ret)
+ {
+ *s = (const struct settings)
+ {
+ .name = "Player",
+ };
+
+ gfx_display_size(&s->screen_w, &s->screen_h);
+ }
+
+ if (f)
+ fclose(f);
+
+ return ret;
+}
diff --git a/src/system/CMakeLists.txt b/src/system/CMakeLists.txt
index 4c315b9..e541096 100644
--- a/src/system/CMakeLists.txt
+++ b/src/system/CMakeLists.txt
@@ -8,9 +8,16 @@ if(PS1_BUILD)
elseif(SDL1_2_BUILD)
set(src "sdl-1.2/src/system.c")
set(inc ${inc} "sdl-1.2/inc")
+ set(privinc ${privinc} "sdl-1.2/privinc")
set(privdeps ${privdeps} SDL::SDL)
+
+ if(WIN9X_BUILD)
+ set(src ${src} "win9x/src/system.c")
+ else()
+ set(src ${src} "sdl-1.2/src/stubs.c")
+ endif()
endif()
add_library(system ${src})
-target_include_directories(system PUBLIC ${inc})
+target_include_directories(system PUBLIC ${inc} PRIVATE ${privinc})
target_link_libraries(system PRIVATE ${privdeps})
diff --git a/src/system/sdl-1.2/privinc/system_private.h b/src/system/sdl-1.2/privinc/system_private.h
new file mode 100644
index 0000000..70ed8c8
--- /dev/null
+++ b/src/system/sdl-1.2/privinc/system_private.h
@@ -0,0 +1,15 @@
+#ifndef SYSTEM_PRIVATE_H
+#define SYSTEM_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int system_init_os(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SYSTEM_PRIVATE_H */
diff --git a/src/system/sdl-1.2/src/stubs.c b/src/system/sdl-1.2/src/stubs.c
new file mode 100644
index 0000000..c07a790
--- /dev/null
+++ b/src/system/sdl-1.2/src/stubs.c
@@ -0,0 +1,6 @@
+#include <system_private.h>
+
+int system_init_os(void)
+{
+ return 0;
+}
diff --git a/src/system/sdl-1.2/src/system.c b/src/system/sdl-1.2/src/system.c
index bff5e4f..71d5d7e 100644
--- a/src/system/sdl-1.2/src/system.c
+++ b/src/system/sdl-1.2/src/system.c
@@ -1,7 +1,8 @@
+#include <system.h>
+#include <system_private.h>
#include <gfx.h>
-#include <sfx.h>
#include <net.h>
-#include <system.h>
+#include <sfx.h>
#include <SDL.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,7 +27,9 @@ void system_deinit(void)
int system_init(void)
{
- if (SDL_Init(0))
+ if (system_init_os())
+ goto failure;
+ else if (SDL_Init(0))
{
fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
goto failure;
@@ -36,6 +39,8 @@ int system_init(void)
SDL_WM_SetCaption("rts", NULL);
SDL_ShowCursor(0);
+ /* SDL_WM_GrabInput(SDL_GRAB_ON); */
+ SDL_EnableUNICODE(1);
return 0;
failure:
diff --git a/src/system/win9x/src/system.c b/src/system/win9x/src/system.c
new file mode 100644
index 0000000..dd2aa10
--- /dev/null
+++ b/src/system/win9x/src/system.c
@@ -0,0 +1,36 @@
+#include <system_private.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int system_init_os(void)
+{
+ /* Windows GUI applications do not print stderr or stdout anywhere.
+ * Therefore, it makes sense to redirect them to plaintext files. */
+ if (!(freopen("stderr.txt", "wb", stderr)))
+ {
+ fprintf(stderr, "%s: freopen(3) stderr failed: %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+ else if (setvbuf(stderr, NULL, _IONBF, 0))
+ {
+ fprintf(stderr, "%s: setvbuf(3) stderr failed: %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+ else if (!(freopen("stdout.txt", "wb", stdout)))
+ {
+ fprintf(stderr, "%s: freopen(3) stdout failed: %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+ else if (setvbuf(stdout, NULL, _IONBF, 0))
+ {
+ fprintf(stderr, "%s: setvbuf(3) stdout failed: %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/transport/CMakeLists.txt b/src/transport/CMakeLists.txt
new file mode 100644
index 0000000..8f05c31
--- /dev/null
+++ b/src/transport/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(src "src/transport.c")
+
+if(PS1_BUILD)
+ set(src ${src} "src/static.c")
+elseif(SDL1_2_BUILD)
+ set(src ${src} "src/heap.c")
+endif()
+
+add_library(transport ${src})
+target_include_directories(transport PUBLIC "inc" PRIVATE "privinc")
diff --git a/src/transport/inc/transport.h b/src/transport/inc/transport.h
new file mode 100644
index 0000000..503812b
--- /dev/null
+++ b/src/transport/inc/transport.h
@@ -0,0 +1,66 @@
+#ifndef TRANSPORT_H
+#define TRANSPORT_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum {TRANSPORT_MAX_DATA_LEN = 32};
+
+struct transport_event
+{
+ struct transport_event_common
+ {
+ enum transport_event_type
+ {
+ TRANSPORT_EVENT_TYPE_CONNECT,
+ TRANSPORT_EVENT_TYPE_DISCONNECT,
+ TRANSPORT_EVENT_TYPE_DATA
+ } type;
+ } common;
+
+ union
+ {
+ struct transport_event_data
+ {
+ const void *buf;
+ size_t n;
+ } data;
+ } u;
+};
+
+union transport_packet;
+
+struct transport_handle
+{
+ struct transport_cfg
+ {
+ void *arg;
+ int (*write)(const void *, size_t, void *);
+ int (*read)(void *, size_t, void *);
+ void (*received)(const struct transport_event *, void *);
+ } cfg;
+
+ union transport_packet **packets;
+ size_t n_packets;
+
+ struct transport_input
+ {
+ union transport_packet *p;
+ struct transport_event ev;
+ } input;
+};
+
+int transport_connect(struct transport_handle *h);
+int transport_disconnect(struct transport_handle *h);
+int transport_send(struct transport_handle *h, const void *buf, size_t n);
+int transport_update(struct transport_handle *h);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TRANSPORT_H */
diff --git a/src/transport/privinc/transport_private.h b/src/transport/privinc/transport_private.h
new file mode 100644
index 0000000..dc7481e
--- /dev/null
+++ b/src/transport/privinc/transport_private.h
@@ -0,0 +1,66 @@
+#ifndef TRANSPORT_PRIVATE_H
+#define TRANSPORT_PRIVATE_H
+
+#include <transport.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef uint8_t transport_checksum;
+
+union transport_packet
+{
+ struct transport_common
+ {
+ enum transport_packet_type
+ {
+ TRANSPORT_PACKET_TYPE_CONNECT,
+ TRANSPORT_PACKET_TYPE_DISCONNECT,
+ TRANSPORT_PACKET_TYPE_ACK,
+ TRANSPORT_PACKET_TYPE_NACK,
+ TRANSPORT_PACKET_TYPE_DATA,
+
+ MAX_TRANSPORT_PACKET_TYPES
+ } type;
+
+ enum transport_state
+ {
+ TRANSPORT_STATE_HEADER,
+ TRANSPORT_STATE_LEN,
+ TRANSPORT_STATE_BODY,
+ TRANSPORT_STATE_CHECKSUM
+ } state;
+
+ bool done;
+ unsigned ttl;
+ } common;
+
+ struct transport_packet_data
+ {
+ struct transport_common common;
+ uint8_t buf[TRANSPORT_MAX_DATA_LEN];
+ size_t n, written;
+ } data;
+
+ struct transport_packet_ack
+ {
+ struct transport_common common;
+ transport_checksum checksum;
+ } ack;
+};
+
+union transport_packet *transport_packet_alloc(enum transport_packet_type t);
+void transport_packet_free(union transport_packet *p);
+int transport_append(struct transport_handle *h, union transport_packet *p);
+int transport_pop(struct transport_handle *h);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TRANSPORT_PRIVATE_H */
diff --git a/src/transport/src/heap.c b/src/transport/src/heap.c
new file mode 100644
index 0000000..cf43426
--- /dev/null
+++ b/src/transport/src/heap.c
@@ -0,0 +1,93 @@
+#include <transport.h>
+#include <transport_private.h>
+#include <stdlib.h>
+
+static void *alloc_common(void)
+{
+ struct transport_common *const ret = malloc(sizeof *ret);
+
+ if (ret)
+ *ret = (const struct transport_common){0};
+
+ return ret;
+}
+
+static void *alloc_ack(void)
+{
+ struct transport_packet_ack *const ret = malloc(sizeof *ret);
+
+ if (ret)
+ *ret = (const struct transport_packet_ack){0};
+
+ return ret;
+}
+
+static void *alloc_data(void)
+{
+ struct transport_packet_data *const ret = malloc(sizeof *ret);
+
+ if (ret)
+ *ret = (const struct transport_packet_data){0};
+
+ return ret;
+}
+
+union transport_packet *transport_packet_alloc(const enum transport_packet_type t)
+{
+ static void *(*const alloc[])(void) =
+ {
+ [TRANSPORT_PACKET_TYPE_CONNECT] = alloc_common,
+ [TRANSPORT_PACKET_TYPE_DISCONNECT] = alloc_common,
+ [TRANSPORT_PACKET_TYPE_NACK] = alloc_common,
+ [TRANSPORT_PACKET_TYPE_ACK] = alloc_ack,
+ [TRANSPORT_PACKET_TYPE_DATA] = alloc_data
+ };
+
+ union transport_packet *const ret = alloc[t]();
+
+ if (!ret)
+ return NULL;
+
+ ret->common.type = t;
+ return ret;
+}
+
+int transport_append(struct transport_handle *const h,
+ union transport_packet *const p)
+{
+ h->packets = realloc(h->packets, (h->n_packets + 1) * sizeof *h->packets);
+
+ if (!h->packets)
+ return -1;
+
+ h->packets[h->n_packets++] = p;
+ return 0;
+}
+
+void transport_packet_free(union transport_packet *const p)
+{
+ free(p);
+}
+
+int transport_pop(struct transport_handle *const h)
+{
+ if (!h->n_packets)
+ return -1;
+
+ union transport_packet *const p = h->packets[h->n_packets - 1];
+
+ if (--h->n_packets)
+ {
+ if (!(h->packets = realloc(h->packets,
+ (h->n_packets - 1) * sizeof *h->packets)))
+ return -1;
+ }
+ else
+ {
+ free(h->packets);
+ h->packets = NULL;
+ }
+
+ transport_packet_free(p);
+ return 0;
+}
diff --git a/src/transport/src/static.c b/src/transport/src/static.c
new file mode 100644
index 0000000..e5dda54
--- /dev/null
+++ b/src/transport/src/static.c
@@ -0,0 +1,71 @@
+#include <transport.h>
+#include <transport_private.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct list
+{
+ union transport_packet p;
+ bool used;
+} list[16];
+
+static union transport_packet *packets[sizeof list / sizeof *list];
+
+union transport_packet *transport_packet_alloc(const enum transport_packet_type t)
+{
+ for (size_t i = 0; i < sizeof list / sizeof *list; i++)
+ {
+ struct list *const l = &list[i];
+
+ if (!l->used)
+ {
+ union transport_packet *const p = &l->p;
+
+ l->used = true;
+ *p = (const union transport_packet)
+ {
+ .common =
+ {
+ .type = t
+ }
+ };
+
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+int transport_append(struct transport_handle *const h,
+ union transport_packet *const p)
+{
+ if (h->n_packets >= sizeof packets / sizeof *packets)
+ return -1;
+
+ packets[h->n_packets++] = p;
+ h->packets = packets;
+ return 0;
+}
+
+void transport_packet_free(union transport_packet *const p)
+{
+ for (size_t i = 0; i < sizeof list / sizeof *list; i++)
+ {
+ struct list *const l = &list[i];
+
+ if (p == &l->p)
+ {
+ l->used = false;
+ return;
+ }
+ }
+}
+
+int transport_pop(struct transport_handle *const h)
+{
+ transport_packet_free(h->packets[h->n_packets - 1]);
+ return 0;
+}
diff --git a/src/transport/src/transport.c b/src/transport/src/transport.c
new file mode 100644
index 0000000..fe2d54f
--- /dev/null
+++ b/src/transport/src/transport.c
@@ -0,0 +1,613 @@
+#include <transport.h>
+#include <transport_private.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef uint8_t packet_header, packet_len;
+
+int transport_connect(struct transport_handle *const h)
+{
+ union transport_packet *const p = transport_packet_alloc(
+ TRANSPORT_PACKET_TYPE_CONNECT);
+
+ if (!p)
+ return -1;
+
+ return transport_append(h, p);
+}
+
+int transport_disconnect(struct transport_handle *const h)
+{
+ union transport_packet *const p = transport_packet_alloc(
+ TRANSPORT_PACKET_TYPE_DISCONNECT);
+
+ if (!p)
+ return -1;
+
+ return transport_append(h, p);
+}
+
+int transport_send(struct transport_handle *const h,
+ const void *const buf, size_t n)
+{
+ const char *b = buf;
+
+ while (n)
+ {
+ union transport_packet *const p = transport_packet_alloc(
+ TRANSPORT_PACKET_TYPE_DATA);
+
+ if (!p)
+ return -1;
+
+ struct transport_packet_data *const d = &p->data;
+ const size_t rem = n > sizeof d->buf ? sizeof d->buf : n;
+
+ memcpy(d->buf, b, rem);
+ d->n = rem;
+
+ if (transport_append(h, p))
+ return -1;
+
+ n -= rem;
+ b += rem;
+ }
+
+ return 0;
+}
+
+static enum transport_state header_next_state(const enum transport_packet_type t)
+{
+ switch (t)
+ {
+ case TRANSPORT_PACKET_TYPE_CONNECT:
+ /* Fall through. */
+ case TRANSPORT_PACKET_TYPE_DISCONNECT:
+ /* Fall through. */
+ case TRANSPORT_PACKET_TYPE_NACK:
+ /* Fall through. */
+ return TRANSPORT_STATE_CHECKSUM;
+
+ case TRANSPORT_PACKET_TYPE_ACK:
+ return TRANSPORT_STATE_BODY;
+
+ case TRANSPORT_PACKET_TYPE_DATA:
+ return TRANSPORT_STATE_LEN;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int read_header(const struct transport_cfg *const cfg,
+ struct transport_input *const in, bool *const done)
+{
+ packet_header header;
+ const int res = cfg->read(&header, sizeof header, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof header)
+ return 1;
+
+ if (header >= MAX_TRANSPORT_PACKET_TYPES)
+ {
+ fprintf(stderr, "%s: invalid header %" PRIu8 "\n",
+ __func__, header);
+ return -1;
+ }
+
+ if (!(in->p = transport_packet_alloc(header)))
+ {
+ fprintf(stderr, "%s: transport_packet_alloc failed\n", __func__);
+ return -1;
+ }
+
+ struct transport_common *const c = &in->p->common;
+
+ c->state = header_next_state(c->type);
+ return 0;
+}
+
+static int read_len(const struct transport_cfg *const cfg,
+ struct transport_input *const in, bool *const done)
+{
+ packet_len len;
+ const int res = cfg->read(&len, sizeof len, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof len)
+ return 1;
+
+ struct transport_packet_data *const d = &in->p->data;
+
+ if (len >= sizeof d->buf)
+ {
+ fprintf(stderr, "%s: invalid length %" PRIu8 "\n", __func__, len);
+ return -1;
+ }
+
+ d->n = len;
+ d->common.state = TRANSPORT_STATE_BODY;
+ return 0;
+}
+
+static int read_data_body(const struct transport_cfg *const cfg,
+ struct transport_input *const in)
+{
+ struct transport_packet_data *const d = &in->p->data;
+ const size_t rem = d->n - d->written;
+ const int res = cfg->read(&d->buf[d->written], rem, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != rem)
+ {
+ d->written += res;
+ return 1;
+ }
+
+ in->p->common.state = TRANSPORT_STATE_CHECKSUM;
+ return 0;
+}
+
+static int read_ack_body(const struct transport_cfg *const cfg,
+ struct transport_input *const in)
+{
+ struct transport_packet_ack *const a = &in->p->ack;
+ const int res = cfg->read(&a->checksum, sizeof a->checksum, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof a->checksum)
+ return 1;
+
+ in->p->common.state = TRANSPORT_STATE_CHECKSUM;
+ return 0;
+}
+
+static int read_body(const struct transport_cfg *const cfg,
+ struct transport_input *const in, bool *const done)
+{
+ const struct transport_common *const c = &in->p->common;
+
+ switch (c->type)
+ {
+ case TRANSPORT_PACKET_TYPE_ACK:
+ return read_ack_body(cfg, in);
+
+ case TRANSPORT_PACKET_TYPE_DATA:
+ return read_data_body(cfg, in);
+
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static transport_checksum calc_checksum(const union transport_packet *const p)
+{
+ const struct transport_common *const c = &p->common;
+ transport_checksum ret = c->type;
+
+ switch (c->type)
+ {
+ case TRANSPORT_PACKET_TYPE_DATA:
+ {
+ const struct transport_packet_data *const d = &p->data;
+
+ ret += d->n;
+
+ for (size_t i = 0; i < d->n; i++)
+ ret += d->buf[i];
+ }
+ break;
+
+ case TRANSPORT_PACKET_TYPE_ACK:
+ ret += p->ack.checksum;
+ break;
+
+ default:
+ break;
+ }
+
+ return ~ret;
+}
+
+static int read_checksum(const struct transport_cfg *const cfg,
+ struct transport_input *const in, bool *const done)
+{
+ transport_checksum checksum;
+ const int res = cfg->read(&checksum, sizeof checksum, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof checksum)
+ return 1;
+
+ const transport_checksum expected = calc_checksum(in->p);
+
+ *done = true;
+
+ if (checksum != expected)
+ {
+ fprintf(stderr, "%s: invalid checksum %#" PRIx8 ", expected %#" PRIx8,
+ __func__, checksum, expected);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int send_nack(struct transport_handle *const h)
+{
+ union transport_packet *const p = transport_packet_alloc(
+ TRANSPORT_PACKET_TYPE_NACK);
+
+ if (!p)
+ return -1;
+
+ return transport_append(h, p);
+}
+
+static bool requires_ack(const enum transport_packet_type t)
+{
+ static const bool r[] =
+ {
+ [TRANSPORT_PACKET_TYPE_CONNECT] = true,
+ [TRANSPORT_PACKET_TYPE_DISCONNECT] = true,
+ [TRANSPORT_PACKET_TYPE_ACK] = false,
+ [TRANSPORT_PACKET_TYPE_NACK] = false,
+ [TRANSPORT_PACKET_TYPE_DATA] = true
+ };
+
+ return r[t];
+}
+
+static int send_ack(struct transport_handle *const h)
+{
+ union transport_packet *const p = transport_packet_alloc(
+ TRANSPORT_PACKET_TYPE_ACK);
+
+ if (!p)
+ return -1;
+
+ p->ack.checksum = calc_checksum(h->input.p);
+ return transport_append(h, p);
+}
+
+static void delete_input(struct transport_input *const in)
+{
+ transport_packet_free(in->p);
+ in->p = NULL;
+}
+
+static void send_event(const struct transport_cfg *const cfg,
+ const union transport_packet *const p,
+ struct transport_event *const ev)
+{
+ switch (p->common.type)
+ {
+ case TRANSPORT_PACKET_TYPE_CONNECT:
+ ev->common.type = TRANSPORT_EVENT_TYPE_CONNECT;
+ break;
+
+ case TRANSPORT_PACKET_TYPE_DISCONNECT:
+ ev->common.type = TRANSPORT_EVENT_TYPE_DISCONNECT;
+ break;
+
+ case TRANSPORT_PACKET_TYPE_DATA:
+ ev->common.type = TRANSPORT_EVENT_TYPE_DATA;
+ ev->u.data.buf = p->data.buf;
+ ev->u.data.n = p->data.n;
+ break;
+
+ default:
+ return;
+ }
+
+ if (cfg->received)
+ cfg->received(ev, cfg->arg);
+}
+
+static int remove_packet(struct transport_handle *const h,
+ union transport_packet *const p)
+{
+ for (size_t i = 0; i < h->n_packets; i++)
+ {
+ union transport_packet **pp = &h->packets[i];
+
+ if (*pp == p)
+ {
+ if (i + 1 < h->n_packets)
+ for (union transport_packet **pr = &h->packets[i + 1];
+ pr - h->packets < h->n_packets;
+ pp++, pr++)
+ *pp = *pr;
+
+ return transport_pop(h);
+ }
+ }
+
+ return -1;
+}
+
+static void get_event(const struct transport_cfg *const cfg,
+ const union transport_packet *const p,
+ struct transport_event *const ev)
+{
+ switch (p->common.type)
+ {
+ case TRANSPORT_PACKET_TYPE_CONNECT:
+ ev->common.type = TRANSPORT_EVENT_TYPE_CONNECT;
+ break;
+
+ case TRANSPORT_PACKET_TYPE_DISCONNECT:
+ ev->common.type = TRANSPORT_EVENT_TYPE_DISCONNECT;
+ break;
+
+ default:
+ return;
+ }
+
+ if (cfg->received)
+ cfg->received(ev, cfg->arg);
+}
+
+static int process_ack(struct transport_handle *const h,
+ const struct transport_packet_ack *const a)
+{
+ for (size_t i = 0; i < h->n_packets; i++)
+ {
+ union transport_packet *const p = h->packets[i];
+
+ if (p->common.ttl && a->checksum == calc_checksum(p))
+ {
+ get_event(&h->cfg, p, &h->input.ev);
+
+ if (remove_packet(h, p))
+ return -1;
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int process_nack(struct transport_handle *const h)
+{
+ if (!h->n_packets)
+ {
+ fprintf(stderr, "%s: received nack without previous sent packet\n",
+ __func__);
+ return -1;
+ }
+
+ h->packets[0]->common.ttl = 0;
+ return 0;
+}
+
+static int process_input(struct transport_handle *const h)
+{
+ const union transport_packet *const p = h->input.p;
+
+ switch (p->common.type)
+ {
+ case TRANSPORT_PACKET_TYPE_ACK:
+ return process_ack(h, &p->ack);
+
+ case TRANSPORT_PACKET_TYPE_NACK:
+ return process_nack(h);
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int update_input(struct transport_handle *const h)
+{
+ int ret = -1;
+ bool done = false;
+ struct transport_input *const in = &h->input;
+
+ static int (*const f[])(const struct transport_cfg *,
+ struct transport_input *, bool *) =
+ {
+ [TRANSPORT_STATE_HEADER] = read_header,
+ [TRANSPORT_STATE_LEN] = read_len,
+ [TRANSPORT_STATE_BODY] = read_body,
+ [TRANSPORT_STATE_CHECKSUM] = read_checksum
+ };
+
+ const struct transport_cfg *const cfg = &h->cfg;
+
+ while (!done)
+ {
+ const enum transport_state s = in->p ? in->p->common.state : 0;
+ const int res = f[s](cfg, in, &done);
+
+ if (res < 0)
+ {
+ ret = send_nack(h);
+ goto end;
+ }
+ else if (res)
+ return 0;
+ }
+
+ const union transport_packet *const p = in->p;
+ const enum transport_packet_type t = p->common.type;
+
+ if (process_input(h))
+ goto end;
+ else if (requires_ack(t))
+ {
+ if (send_ack(h))
+ {
+ fprintf(stderr, "%s: send_ack failed\n", __func__);
+ goto end;
+ }
+
+ send_event(&h->cfg, p, &h->input.ev);
+ }
+
+ ret = 0;
+
+end:
+ delete_input(in);
+ return ret;
+}
+
+static int write_header(const struct transport_cfg *const cfg,
+ union transport_packet *const p)
+{
+ const packet_header header = p->common.type;
+ struct transport_common *const c = &p->common;
+ const int res = cfg->write(&header, sizeof header, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof header)
+ return 1;
+
+ c->state = header_next_state(c->type);
+ return 0;
+}
+
+static int write_len(const struct transport_cfg *const cfg,
+ union transport_packet *const p)
+{
+ const packet_len len = p->data.n;
+ const int res = cfg->write(&len, sizeof len, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof len)
+ return 1;
+
+ p->common.state = TRANSPORT_STATE_BODY;
+ return 0;
+}
+
+static int write_ack_body(const struct transport_cfg *const cfg,
+ struct transport_packet_ack *const a)
+{
+ const int res = cfg->write(&a->checksum, sizeof a->checksum, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof a->checksum)
+ return 1;
+
+ a->common.state = TRANSPORT_STATE_CHECKSUM;
+ return 0;
+}
+
+static int write_data_body(const struct transport_cfg *const cfg,
+ struct transport_packet_data *const d)
+{
+ const size_t rem = d->n - d->written;
+ const int res = cfg->write(&d->buf[d->written], rem, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != rem)
+ {
+ d->written += res;
+ return 1;
+ }
+
+ d->common.state = TRANSPORT_STATE_CHECKSUM;
+ return 0;
+}
+
+static int write_body(const struct transport_cfg *const cfg,
+ union transport_packet *const p)
+{
+ struct transport_common *const c = &p->common;
+
+ switch (c->type)
+ {
+ case TRANSPORT_PACKET_TYPE_ACK:
+ return write_ack_body(cfg, &p->ack);
+
+ case TRANSPORT_PACKET_TYPE_DATA:
+ return write_data_body(cfg, &p->data);
+
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static int write_checksum(const struct transport_cfg *const cfg,
+ union transport_packet *const p)
+{
+ const transport_checksum checksum = calc_checksum(p);
+ const int res = cfg->write(&checksum, sizeof checksum, cfg->arg);
+
+ if (res < 0)
+ return res;
+ else if (res != sizeof checksum)
+ return 1;
+
+ p->common.done = true;
+ return 0;
+}
+
+static int update_output(struct transport_handle *const h)
+{
+ for (size_t i = 0; i < h->n_packets; i++)
+ {
+ union transport_packet *const p = h->packets[i];
+ struct transport_common *const c = &p->common;
+
+ if (!c->ttl)
+ {
+ static int (*const f[])(const struct transport_cfg *,
+ union transport_packet *) =
+ {
+ [TRANSPORT_STATE_HEADER] = write_header,
+ [TRANSPORT_STATE_LEN] = write_len,
+ [TRANSPORT_STATE_BODY] = write_body,
+ [TRANSPORT_STATE_CHECKSUM] = write_checksum
+ };
+
+ while (!c->done)
+ {
+ const int res = f[c->state](&h->cfg, p);
+
+ if (res < 0)
+ return res;
+ else if (res)
+ return 0;
+ }
+
+ /* Arbitrary value, also depends on frame rate, yet good enough. */
+ enum {TTL = 200};
+
+ c->ttl = TTL;
+ }
+ else
+ c->ttl--;
+ }
+
+ return 0;
+}
+
+int transport_update(struct transport_handle *const h)
+{
+ return update_input(h) || update_output(h);
+}